Alrik 10 years ago
commit 4c5850bf00
  1. 21
      .travis.yml
  2. 3
      jme3-blender/build.gradle
  3. 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java
  4. 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  5. 12
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java
  6. 266
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  7. 128
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java
  8. 24
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java
  9. 214
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Matrix.java
  10. 2
      jme3-core/src/main/java/com/jme3/light/SpotLight.java
  11. 2
      jme3-core/src/main/java/com/jme3/renderer/Limits.java
  12. 4
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
  13. 13
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java
  14. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java
  15. 21
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java
  16. 28
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
  17. 112
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  18. 36
      jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java
  19. 14
      jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib
  20. 1
      jme3-core/src/plugins/java/com/jme3/asset/plugins/ZipLocator.java
  21. 148
      jme3-networking/src/main/java/com/jme3/network/service/AbstractHostedConnectionService.java
  22. 62
      jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHostedService.java
  23. 6
      jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java
  24. 1
      sdk/jme3-model-importer/src/com/jme3/gde/modelimporter/ImportModel.java
  25. 1
      sdk/jme3-model-importer/src/com/jme3/gde/modelimporter/ModelImporterVisualPanel3.java
  26. 53
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerToolController.java
  27. 204
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/MoveManager.java
  28. 12
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/PickManager.java
  29. 11
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/RotateTool.java
  30. 684
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java
  31. 123
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/DeleteShortcut.java
  32. 141
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/DuplicateShortcut.java
  33. 227
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/MoveShortcut.java
  34. 189
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/RotateShortcut.java
  35. 199
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ScaleShortcut.java
  36. 335
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ShortcutManager.java
  37. 29
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ShortcutTool.java

@ -14,11 +14,28 @@ branches:
notifications:
slack:
secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
on_success: change
on_failure: always
rooms:
secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
before_install:
install:
- ./gradlew assemble
script:
- ./gradlew check
- ./gradlew createZipDistribution
deploy:
provider: releases
api_key:
secure: "KbFiMt0a8FxUKvCJUYwikLYaqqGMn1p6k4OsXnGqwptQZEUIayabNLHeaD2kTNT3e6AY1ETwQLff/lB2LttmIo4g5NWW63g1K3A/HwgnhJwETengiProZ/Udl+ugPeDL/+ar43HUhFq4knBnzFKnEcHAThTPVqH/RMDvZf1UUYI="
file: build/distributions/jME3.1.0_snapshot-github_2015-06-20.zip
skip_cleanup: true
on:
tags: true
# before_install:
# required libs for android build tools
# sudo apt-get update
# sudo apt-get install -qq p7zip-full

@ -6,4 +6,7 @@ dependencies {
compile project(':jme3-core')
compile project(':jme3-desktop')
compile project(':jme3-effects')
compile ('org.ejml:core:0.27')
compile ('org.ejml:dense64:0.27')
compile ('org.ejml:simple:0.27')
}

@ -64,7 +64,7 @@ public abstract class Constraint {
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
if (pData.isNotNull()) {
Structure data = pData.fetchData().get(0);
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, ownerOMA, blenderContext);
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, name, ownerOMA, blenderContext);
Pointer pTar = (Pointer) data.getFieldValue("tar");
if (pTar != null && pTar.isNotNull()) {
targetOMA = pTar.getOldMemoryAddress();
@ -77,7 +77,7 @@ public abstract class Constraint {
}
} else {
// Null constraint has no data, so create it here
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, name, null, blenderContext);
}
ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
ipo = influenceIpo;

@ -30,6 +30,8 @@ public abstract class ConstraintDefinition {
protected Set<Long> alteredOmas;
/** The variable that determines if the constraint will alter the track in any way. */
protected boolean trackToBeChanged = true;
/** The name of the constraint. */
protected String constraintName;
/**
* Loads a constraint definition based on the constraint definition
@ -54,6 +56,10 @@ public abstract class ConstraintDefinition {
this.ownerOMA = ownerOMA;
}
public void setConstraintName(String constraintName) {
this.constraintName = constraintName;
}
/**
* @return determines if the definition of the constraint will change the bone in any way; in most cases
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint

@ -92,7 +92,7 @@ public class ConstraintDefinitionFactory {
* this exception is thrown when the blender file is somehow
* corrupted
*/
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException {
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, String constraintName, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException {
if (constraintStructure == null) {
return new ConstraintDefinitionNull(null, ownerOMA, blenderContext);
}
@ -100,7 +100,9 @@ public class ConstraintDefinitionFactory {
Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
if (constraintDefinitionClass != null) {
try {
return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext);
ConstraintDefinition def = (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext);
def.setConstraintName(constraintName);
return def;
} catch (IllegalArgumentException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (SecurityException e) {
@ -113,9 +115,9 @@ public class ConstraintDefinitionFactory {
throw new BlenderFileException(e.getLocalizedMessage(), e);
}
} else {
String constraintName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
if (constraintName != null) {
return new UnsupportedConstraintDefinition(constraintName);
String unsupportedConstraintClassName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
if (unsupportedConstraintClassName != null) {
return new UnsupportedConstraintDefinition(unsupportedConstraintClassName);
} else {
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
}

@ -1,36 +1,32 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.ejml.simple.SimpleMatrix;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.math.DQuaternion;
import com.jme3.scene.plugins.blender.math.DTransform;
import com.jme3.scene.plugins.blender.math.Matrix;
import com.jme3.scene.plugins.blender.math.Vector3d;
/**
* The Inverse Kinematics constraint.
*
* @author Wesley Shillingford (wezrule)
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintDefinitionIK extends ConstraintDefinition {
private static final float MIN_DISTANCE = 0.0001f;
private static final float MIN_DISTANCE = 0.001f;
private static final int FLAG_USE_TAIL = 0x01;
private static final int FLAG_POSITION = 0x20;
private BonesChain bones;
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
private int bonesAffected;
/** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */
private double chainLength;
/** Indicates if the tail of the bone should be used or not. */
private boolean useTail;
/** The amount of iterations of the algorithm. */
@ -51,122 +47,85 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
}
}
@Override
public Set<Long> getAlteredOmas() {
return bones.alteredOmas;
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged || targetTransform == null) {
return;// no need to do anything
}
DQuaternion q = new DQuaternion();
Vector3d t = new Vector3d(targetTransform.getTranslation());
List<BoneContext> bones = this.loadBones();
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, blenderContext);
if (bones.size() == 0) {
return;// no need to do anything
}
double distanceFromTarget = Double.MAX_VALUE;
int iterations = this.iterations;
if (bones.size() == 1) {
iterations = 1;// if only one bone is in the chain then only one iteration that will properly rotate it will be needed
} else {
// if the target cannot be rached by the bones' chain then the chain will become straight and point towards the target
// in this case only one iteration will be needed, computed from the root to top bone
BoneContext rootBone = bones.get(bones.size() - 1);
Transform rootBoneTransform = constraintHelper.getTransform(rootBone.getArmatureObjectOMA(), rootBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
if (t.distance(new Vector3d(rootBoneTransform.getTranslation())) >= chainLength) {
Collections.reverse(bones);
for (BoneContext boneContext : bones) {
Bone bone = boneContext.getBone();
DTransform boneTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD));
Vector3d e = boneTransform.getTranslation().add(boneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(boneContext.getLength()));// effector
Vector3d j = boneTransform.getTranslation(); // current join position
Vector3d currentDir = e.subtractLocal(j).normalizeLocal();
Vector3d target = t.subtract(j).normalizeLocal();
double angle = currentDir.angleBetween(target);
if (angle != 0) {
Vector3d cross = currentDir.crossLocal(target).normalizeLocal();
q.fromAngleAxis(angle, cross);
if(bone.equals(this.getOwner())) {
if (boneContext.isLockX()) {
q.set(0, q.getY(), q.getZ(), q.getW());
}
if (boneContext.isLockY()) {
q.set(q.getX(), 0, q.getZ(), q.getW());
}
if (boneContext.isLockZ()) {
q.set(q.getX(), q.getY(), 0, q.getW());
}
}
boneTransform.getRotation().set(q.multLocal(boneTransform.getRotation()));
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform.toTransform());
}
}
iterations = 0;
Vector3d target = new Vector3d(targetTransform.getTranslation());
Vector3d[] rotationVectors = new Vector3d[bones.size()];
BoneContext topBone = bones.get(0);
for (int i = 1; i <= iterations; ++i) {
DTransform topBoneTransform = bones.getWorldTransform(topBone);
Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
distanceFromTarget = e.distance(t);
if (distanceFromTarget <= MIN_DISTANCE) {
break;
}
}
List<Transform> bestSolution = new ArrayList<Transform>(bones.size());
double bestSolutionDistance = Double.MAX_VALUE;
BoneContext topBone = bones.get(0);
for (int i = 0; i < iterations && distanceFromTarget > MIN_DISTANCE; ++i) {
for (BoneContext boneContext : bones) {
Bone bone = boneContext.getBone();
DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD));
DTransform boneWorldTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD));
Matrix deltaP = new Matrix(3, 1);
deltaP.setColumn(target.subtract(e), 0);
Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
Matrix J = new Matrix(3, bones.size());
int column = 0;
for (BoneContext boneContext : bones) {
DTransform boneWorldTransform = bones.getWorldTransform(boneContext);
Vector3d j = boneWorldTransform.getTranslation(); // current join position
Vector3d currentDir = e.subtractLocal(j).normalizeLocal();
Vector3d target = t.subtract(j).normalizeLocal();
double angle = currentDir.angleBetween(target);
if (angle != 0) {
Vector3d cross = currentDir.crossLocal(target).normalizeLocal();
q.fromAngleAxis(angle, cross);
if(bone.equals(this.getOwner())) {
if (boneContext.isLockX()) {
q.set(0, q.getY(), q.getZ(), q.getW());
}
if (boneContext.isLockY()) {
q.set(q.getX(), 0, q.getZ(), q.getW());
}
if (boneContext.isLockZ()) {
q.set(q.getX(), q.getY(), 0, q.getW());
}
}
boneWorldTransform.getRotation().set(q.multLocal(boneWorldTransform.getRotation()));
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform.toTransform());
} else {
iterations = 0;
break;
}
Vector3d vectorFromJointToEffector = e.subtract(j);
rotationVectors[column] = vectorFromJointToEffector.cross(target.subtract(j)).normalize();
Vector3d col = rotationVectors[column].cross(vectorFromJointToEffector);
J.setColumn(col, column++);
}
Matrix J_1 = J.pseudoinverse();
DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD));
Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
distanceFromTarget = e.distance(t);
SimpleMatrix deltaThetas = J_1.mult(deltaP);
for (int j = 0; j < deltaThetas.numRows(); ++j) {
double angle = deltaThetas.get(j, 0);
Vector3d rotationVector = rotationVectors[j];
if(distanceFromTarget < bestSolutionDistance) {
bestSolutionDistance = distanceFromTarget;
bestSolution.clear();
for(BoneContext boneContext : bones) {
bestSolution.add(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD));
q.fromAngleAxis(angle, rotationVector);
BoneContext boneContext = bones.get(j);
Bone bone = boneContext.getBone();
if (bone.equals(this.getOwner())) {
if (boneContext.isLockX()) {
q.set(0, q.getY(), q.getZ(), q.getW());
}
if (boneContext.isLockY()) {
q.set(q.getX(), 0, q.getZ(), q.getW());
}
if (boneContext.isLockZ()) {
q.set(q.getX(), q.getY(), 0, q.getW());
}
}
DTransform boneTransform = bones.getWorldTransform(boneContext);
boneTransform.getRotation().set(q.mult(boneTransform.getRotation()));
bones.setWorldTransform(boneContext, boneTransform);
}
}
// applying best solution
for(int i=0;i<bestSolution.size();++i) {
// applying the results
for (int i = bones.size() - 1; i >= 0; --i) {
BoneContext boneContext = bones.get(i);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, bestSolution.get(i));
DTransform transform = bones.getWorldTransform(boneContext);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform());
}
bones.reset();
}
@Override
@ -174,49 +133,12 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
return "Inverse kinematics";
}
/**
* @return the bone contexts of all bones that will be used in this constraint computations
*/
private List<BoneContext> loadBones() {
List<BoneContext> bones = new ArrayList<BoneContext>();
Bone bone = (Bone) this.getOwner();
if (bone == null) {
return bones;
}
if (!useTail) {
bone = bone.getParent();
}
chainLength = 0;
while (bone != null) {
BoneContext boneContext = blenderContext.getBoneContext(bone);
chainLength += boneContext.getLength();
bones.add(boneContext);
alteredOmas.add(boneContext.getBoneOma());
if (bonesAffected != 0 && bones.size() >= bonesAffected) {
break;
}
// need to add spaces between bones to the chain length
Transform boneWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
Vector3f boneWorldTranslation = boneWorldTransform.getTranslation();
bone = bone.getParent();
if (bone != null) {
boneContext = blenderContext.getBoneContext(bone);
Transform parentWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
Vector3f parentWorldTranslation = parentWorldTransform.getTranslation();
chainLength += boneWorldTranslation.distance(parentWorldTranslation);
}
}
return bones;
}
@Override
public boolean isTrackToBeChanged() {
if (trackToBeChanged) {
// need to check the bone structure too (when constructor was called not all of the bones might have been loaded yet)
// that is why it is also checked here
List<BoneContext> bones = this.loadBones();
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, blenderContext);
trackToBeChanged = bones.size() > 0;
}
return trackToBeChanged;
@ -226,4 +148,68 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
public boolean isTargetRequired() {
return true;
}
private static class BonesChain extends ArrayList<BoneContext> {
private static final long serialVersionUID = -1850524345643600718L;
private Set<Long> alteredOmas = new HashSet<Long>();
private List<Matrix> originalBonesMatrices = new ArrayList<Matrix>();
private List<Matrix> bonesMatrices = new ArrayList<Matrix>();
public BonesChain(Bone bone, boolean useTail, int bonesAffected, BlenderContext blenderContext) {
if (bone != null) {
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
if (!useTail) {
bone = bone.getParent();
}
while (bone != null && this.size() < bonesAffected) {
BoneContext boneContext = blenderContext.getBoneContext(bone);
this.add(boneContext);
alteredOmas.add(boneContext.getBoneOma());
Space space = this.size() < bonesAffected ? Space.CONSTRAINT_SPACE_LOCAL : Space.CONSTRAINT_SPACE_WORLD;
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), space);
originalBonesMatrices.add(new DTransform(transform).toMatrix());
bone = bone.getParent();
}
this.reset();
}
}
public DTransform getWorldTransform(BoneContext bone) {
int index = this.indexOf(bone);
return this.getWorldMatrix(index).toTransform();
}
public void setWorldTransform(BoneContext bone, DTransform transform) {
int index = this.indexOf(bone);
Matrix boneMatrix = transform.toMatrix();
if (index < this.size() - 1) {
// computing the current bone local transform
Matrix parentWorldMatrix = this.getWorldMatrix(index + 1);
SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix);
boneMatrix = new Matrix(m);
}
bonesMatrices.set(index, boneMatrix);
}
public Matrix getWorldMatrix(int index) {
if (index == this.size() - 1) {
return new Matrix(bonesMatrices.get(this.size() - 1));
}
SimpleMatrix result = this.getWorldMatrix(index + 1);
result = result.mult(bonesMatrices.get(index));
return new Matrix(result);
}
public void reset() {
bonesMatrices.clear();
for (Matrix m : originalBonesMatrices) {
bonesMatrices.add(new Matrix(m));
}
}
}
}

@ -164,6 +164,134 @@ public final class DQuaternion implements Savable, Cloneable, java.io.Serializab
w = 1;
}
/**
* <code>norm</code> returns the norm of this quaternion. This is the dot
* product of this quaternion with itself.
*
* @return the norm of the quaternion.
*/
public double norm() {
return w * w + x * x + y * y + z * z;
}
public DQuaternion fromRotationMatrix(double m00, double m01, double m02,
double m10, double m11, double m12, double m20, double m21, double m22) {
// first normalize the forward (F), up (U) and side (S) vectors of the rotation matrix
// so that the scale does not affect the rotation
double lengthSquared = m00 * m00 + m10 * m10 + m20 * m20;
if (lengthSquared != 1f && lengthSquared != 0f) {
lengthSquared = 1.0 / Math.sqrt(lengthSquared);
m00 *= lengthSquared;
m10 *= lengthSquared;
m20 *= lengthSquared;
}
lengthSquared = m01 * m01 + m11 * m11 + m21 * m21;
if (lengthSquared != 1 && lengthSquared != 0f) {
lengthSquared = 1.0 / Math.sqrt(lengthSquared);
m01 *= lengthSquared;
m11 *= lengthSquared;
m21 *= lengthSquared;
}
lengthSquared = m02 * m02 + m12 * m12 + m22 * m22;
if (lengthSquared != 1f && lengthSquared != 0f) {
lengthSquared = 1.0 / Math.sqrt(lengthSquared);
m02 *= lengthSquared;
m12 *= lengthSquared;
m22 *= lengthSquared;
}
// Use the Graphics Gems code, from
// ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z
// *NOT* the "Matrix and Quaternions FAQ", which has errors!
// the trace is the sum of the diagonal elements; see
// http://mathworld.wolfram.com/MatrixTrace.html
double t = m00 + m11 + m22;
// we protect the division by s by ensuring that s>=1
if (t >= 0) { // |w| >= .5
double s = Math.sqrt(t + 1); // |s|>=1 ...
w = 0.5f * s;
s = 0.5f / s; // so this division isn't bad
x = (m21 - m12) * s;
y = (m02 - m20) * s;
z = (m10 - m01) * s;
} else if (m00 > m11 && m00 > m22) {
double s = Math.sqrt(1.0 + m00 - m11 - m22); // |s|>=1
x = s * 0.5f; // |x| >= .5
s = 0.5f / s;
y = (m10 + m01) * s;
z = (m02 + m20) * s;
w = (m21 - m12) * s;
} else if (m11 > m22) {
double s = Math.sqrt(1.0 + m11 - m00 - m22); // |s|>=1
y = s * 0.5f; // |y| >= .5
s = 0.5f / s;
x = (m10 + m01) * s;
z = (m21 + m12) * s;
w = (m02 - m20) * s;
} else {
double s = Math.sqrt(1.0 + m22 - m00 - m11); // |s|>=1
z = s * 0.5f; // |z| >= .5
s = 0.5f / s;
x = (m02 + m20) * s;
y = (m21 + m12) * s;
w = (m10 - m01) * s;
}
return this;
}
/**
* <code>toRotationMatrix</code> converts this quaternion to a rotational
* matrix. The result is stored in result. 4th row and 4th column values are
* untouched. Note: the result is created from a normalized version of this quat.
*
* @param result
* The Matrix4f to store the result in.
* @return the rotation matrix representation of this quaternion.
*/
public Matrix toRotationMatrix(Matrix result) {
Vector3d originalScale = new Vector3d();
result.toScaleVector(originalScale);
result.setScale(1, 1, 1);
double norm = this.norm();
// we explicitly test norm against one here, saving a division
// at the cost of a test and branch. Is it worth it?
double s = norm == 1f ? 2f : norm > 0f ? 2f / norm : 0;
// compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
// will be used 2-4 times each.
double xs = x * s;
double ys = y * s;
double zs = z * s;
double xx = x * xs;
double xy = x * ys;
double xz = x * zs;
double xw = w * xs;
double yy = y * ys;
double yz = y * zs;
double yw = w * ys;
double zz = z * zs;
double zw = w * zs;
// using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
result.set(0, 0, 1 - (yy + zz));
result.set(0, 1, xy - zw);
result.set(0, 2, xz + yw);
result.set(1, 0, xy + zw);
result.set(1, 1, 1 - (xx + zz));
result.set(1, 2, yz - xw);
result.set(2, 0, xz - yw);
result.set(2, 1, yz + xw);
result.set(2, 2, 1 - (xx + yy));
result.setScale(originalScale);
return result;
}
/**
* <code>fromAngleAxis</code> sets this quaternion to the values specified
* by an angle and an axis of rotation. This method creates an object, so

@ -31,11 +31,15 @@
*/
package com.jme3.scene.plugins.blender.math;
import com.jme3.export.*;
import com.jme3.math.Transform;
import java.io.IOException;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.math.Transform;
/**
* Started Date: Jul 16, 2004<br>
* <br>
@ -57,6 +61,12 @@ public final class DTransform implements Savable, Cloneable, java.io.Serializabl
private Vector3d translation;
private Vector3d scale;
public DTransform() {
translation = new Vector3d();
rotation = new DQuaternion();
scale = new Vector3d();
}
public DTransform(Transform transform) {
translation = new Vector3d(transform.getTranslation());
rotation = new DQuaternion(transform.getRotation());
@ -67,6 +77,14 @@ public final class DTransform implements Savable, Cloneable, java.io.Serializabl
return new Transform(translation.toVector3f(), rotation.toQuaternion(), scale.toVector3f());
}
public Matrix toMatrix() {
Matrix m = Matrix.identity(4);
m.setTranslation(translation);
m.setRotationQuaternion(rotation);
m.setScale(scale);
return m;
}
/**
* Sets this translation to the given value.
* @param trans

@ -0,0 +1,214 @@
package com.jme3.scene.plugins.blender.math;
import java.text.DecimalFormat;
import org.ejml.ops.CommonOps;
import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD;
import com.jme3.math.FastMath;
/**
* Encapsulates a 4x4 matrix
*
*
*/
public class Matrix extends SimpleMatrix {
private static final long serialVersionUID = 2396600537315902559L;
public Matrix(int rows, int cols) {
super(rows, cols);
}
/**
* Copy constructor
*/
public Matrix(SimpleMatrix m) {
super(m);
}
public Matrix(double[][] data) {
super(data);
}
public static Matrix identity(int size) {
Matrix result = new Matrix(size, size);
CommonOps.setIdentity(result.mat);
return result;
}
public Matrix pseudoinverse() {
return this.pseudoinverse(1);
}
@SuppressWarnings("unchecked")
public Matrix pseudoinverse(double lambda) {
SimpleSVD<SimpleMatrix> simpleSVD = this.svd();
SimpleMatrix U = simpleSVD.getU();
SimpleMatrix S = simpleSVD.getW();
SimpleMatrix V = simpleSVD.getV();
int N = Math.min(this.numRows(),this.numCols());
double maxSingular = 0;
for( int i = 0; i < N; i++ ) {
if( S.get(i, i) > maxSingular ) {
maxSingular = S.get(i, i);
}
}
double tolerance = FastMath.DBL_EPSILON * Math.max(this.numRows(),this.numCols()) * maxSingular;
for(int i=0;i<Math.min(S.numRows(), S.numCols());++i) {
double a = S.get(i, i);
if(a <= tolerance) {
a = 0;
} else {
a = a/(a * a + lambda * lambda);
}
S.set(i, i, a);
}
return new Matrix(V.mult(S.transpose()).mult(U.transpose()));
}
public void setColumn(Vector3d col, int column) {
this.setColumn(column, 0, col.x, col.y, col.z);
}
/**
* Just for some debug informations in order to compare the results with the scilab computation program.
* @param name the name of the matrix
* @param m the matrix to print out
* @return the String format of the matrix to easily input it to Scilab
*/
public String toScilabString(String name, SimpleMatrix m) {
String result = name + " = [";
for(int i=0;i<m.numRows();++i) {
for(int j=0;j<m.numCols();++j) {
result += m.get(i, j) + " ";
}
result += ";";
}
return result;
}
/**
* @return a String representation of the matrix
*/
@Override
public String toString() {
DecimalFormat df = new DecimalFormat("#.0000");
StringBuilder buf = new StringBuilder();
for (int r = 0; r < this.numRows(); ++r) {
buf.append("\n| ");
for (int c = 0; c < this.numCols(); ++c) {
buf.append(df.format(this.get(r, c))).append(' ');
}
buf.append('|');
}
return buf.toString();
}
public void setTranslation(Vector3d translation) {
this.setColumn(translation, 3);
}
/**
* Sets the scale.
*
* @param scale
* the scale vector to set
*/
public void setScale(Vector3d scale) {
this.setScale(scale.x, scale.y, scale.z);
}
/**
* Sets the scale.
*
* @param x
* the X scale
* @param y
* the Y scale
* @param z
* the Z scale
*/
public void setScale(double x, double y, double z) {
Vector3d vect1 = new Vector3d(this.get(0, 0), this.get(1, 0), this.get(2, 0));
vect1.normalizeLocal().multLocal(x);
this.set(0, 0, vect1.x);
this.set(1, 0, vect1.y);
this.set(2, 0, vect1.z);
vect1.set(this.get(0, 1), this.get(1, 1), this.get(2, 1));
vect1.normalizeLocal().multLocal(y);
this.set(0, 1, vect1.x);
this.set(1, 1, vect1.y);
this.set(2, 1, vect1.z);
vect1.set(this.get(0, 2), this.get(1, 2), this.get(2, 2));
vect1.normalizeLocal().multLocal(z);
this.set(0, 2, vect1.x);
this.set(1, 2, vect1.y);
this.set(2, 2, vect1.z);
}
/**
* <code>setRotationQuaternion</code> builds a rotation from a
* <code>Quaternion</code>.
*
* @param quat
* the quaternion to build the rotation from.
* @throws NullPointerException
* if quat is null.
*/
public void setRotationQuaternion(DQuaternion quat) {
quat.toRotationMatrix(this);
}
public DTransform toTransform() {
DTransform result = new DTransform();
result.setTranslation(this.toTranslationVector());
result.setRotation(this.toRotationQuat());
result.setScale(this.toScaleVector());
return result;
}
public Vector3d toTranslationVector() {
return new Vector3d(this.get(0, 3), this.get(1, 3), this.get(2, 3));
}
public DQuaternion toRotationQuat() {
DQuaternion quat = new DQuaternion();
quat.fromRotationMatrix(this.get(0, 0), this.get(0, 1), this.get(0, 2), this.get(1, 0), this.get(1, 1), this.get(1, 2), this.get(2, 0), this.get(2, 1), this.get(2, 2));
return quat;
}
/**
* Retreives the scale vector from the matrix and stores it into a given
* vector.
*
* @param the
* vector where the scale will be stored
*/
public Vector3d toScaleVector() {
Vector3d result = new Vector3d();
this.toScaleVector(result);
return result;
}
/**
* Retreives the scale vector from the matrix and stores it into a given
* vector.
*
* @param the
* vector where the scale will be stored
*/
public void toScaleVector(Vector3d vector) {
double scaleX = Math.sqrt(this.get(0, 0) * this.get(0, 0) + this.get(1, 0) * this.get(1, 0) + this.get(2, 0) * this.get(2, 0));
double scaleY = Math.sqrt(this.get(0, 1) * this.get(0, 1) + this.get(1, 1) * this.get(1, 1) + this.get(2, 1) * this.get(2, 1));
double scaleZ = Math.sqrt(this.get(0, 2) * this.get(0, 2) + this.get(1, 2) * this.get(1, 2) + this.get(2, 2) * this.get(2, 2));
vector.set(scaleX, scaleY, scaleZ);
}
}

@ -63,7 +63,7 @@ public class SpotLight extends Light {
protected float spotInnerAngle = FastMath.QUARTER_PI / 8;
protected float spotOuterAngle = FastMath.QUARTER_PI / 6;
protected float spotRange = 100;
protected float invSpotRange = 1 / 100;
protected float invSpotRange = 1f / 100;
protected float packedAngleCos=0;
protected float outerAngleCosSqr, outerAngleSinSqr;

@ -75,4 +75,6 @@ public enum Limits {
ColorTextureSamples,
DepthTextureSamples,
VertexUniformVectors,
}

@ -74,6 +74,7 @@ public interface GL {
public static final int GL_FRONT_AND_BACK = 0x408;
public static final int GL_GEQUAL = 0x206;
public static final int GL_GREATER = 0x204;
public static final int GL_GREEN = 0x1904;
public static final int GL_INCR = 0x1E02;
public static final int GL_INCR_WRAP = 0x8507;
public static final int GL_INFO_LOG_LENGTH = 0x8B84;
@ -99,6 +100,8 @@ public interface GL {
public static final int GL_MAX_TEXTURE_SIZE = 0xD33;
public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869;
public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C;
public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A;
public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
public static final int GL_MIRRORED_REPEAT = 0x8370;
public static final int GL_NEAREST = 0x2600;
public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702;
@ -114,6 +117,7 @@ public interface GL {
public static final int GL_OUT_OF_MEMORY = 0x505;
public static final int GL_POINTS = 0x0;
public static final int GL_POLYGON_OFFSET_FILL = 0x8037;
public static final int GL_RED = 0x1903;
public static final int GL_RENDERER = 0x1F01;
public static final int GL_REPEAT = 0x2901;
public static final int GL_REPLACE = 0x1E01;

@ -34,7 +34,7 @@ package com.jme3.renderer.opengl;
import java.nio.IntBuffer;
/**
* GL functions only available on vanilla desktop OpenGL 3.0.
* GL functions only available on vanilla desktop OpenGL 3.0+.
*
* @author Kirill Vainer
*/
@ -43,6 +43,17 @@ public interface GL3 extends GL2 {
public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
public static final int GL_GEOMETRY_SHADER = 0x8DD9;
public static final int GL_NUM_EXTENSIONS = 0x821D;
public static final int GL_R8 = 0x8229;
public static final int GL_R16F = 0x822D;
public static final int GL_R32F = 0x822E;
public static final int GL_RG16F = 0x822F;
public static final int GL_RG32F = 0x8230;
public static final int GL_RG = 0x8227;
public static final int GL_RG8 = 0x822B;
public static final int GL_TEXTURE_SWIZZLE_A = 0x8E45;
public static final int GL_TEXTURE_SWIZZLE_B = 0x8E44;
public static final int GL_TEXTURE_SWIZZLE_G = 0x8E43;
public static final int GL_TEXTURE_SWIZZLE_R = 0x8E42;
public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
public void glBindVertexArray(int param1); /// GL3+

@ -34,7 +34,7 @@ package com.jme3.renderer.opengl;
import java.nio.IntBuffer;
/**
* GL functions only available on vanilla desktop OpenGL 3.0.
* GL functions only available on vanilla desktop OpenGL 4.0.
*
* @author Kirill Vainer
*/

@ -42,6 +42,7 @@ public final class GLImageFormat {
public final int format;
public final int dataType;
public final boolean compressed;
public final boolean swizzleRequired;
/**
* Constructor for formats.
@ -55,6 +56,7 @@ public final class GLImageFormat {
this.format = format;
this.dataType = dataType;
this.compressed = false;
this.swizzleRequired = false;
}
/**
@ -63,11 +65,30 @@ public final class GLImageFormat {
* @param internalFormat OpenGL internal format
* @param format OpenGL format
* @param dataType OpenGL datatype
* @param compressed Format is compressed
*/
public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) {
this.internalFormat = internalFormat;
this.format = format;
this.dataType = dataType;
this.compressed = compressed;
this.swizzleRequired = false;
}
/**
* Constructor for formats.
*
* @param internalFormat OpenGL internal format
* @param format OpenGL format
* @param dataType OpenGL datatype
* @param compressed Format is compressed
* @param swizzleRequired Need to use texture swizzle to upload texture
*/
public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed, boolean swizzleRequired) {
this.internalFormat = internalFormat;
this.format = format;
this.dataType = dataType;
this.compressed = compressed;
this.swizzleRequired = swizzleRequired;
}
}

@ -52,6 +52,13 @@ public final class GLImageFormats {
formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType);
}
private static void formatSwiz(GLImageFormat[][] formatToGL, Image.Format format,
int glInternalFormat,
int glFormat,
int glDataType){
formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true);
}
private static void formatSrgb(GLImageFormat[][] formatToGL, Image.Format format,
int glInternalFormat,
int glFormat,
@ -60,6 +67,14 @@ public final class GLImageFormats {
formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType);
}
private static void formatSrgbSwiz(GLImageFormat[][] formatToGL, Image.Format format,
int glInternalFormat,
int glFormat,
int glDataType)
{
formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true);
}
private static void formatComp(GLImageFormat[][] formatToGL, Image.Format format,
int glCompressedFormat,
int glFormat,
@ -88,6 +103,19 @@ public final class GLImageFormats {
public static GLImageFormat[][] getFormatsForCaps(EnumSet<Caps> caps) {
GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length];
// Core Profile Formats (supported by both OpenGL Core 3.3 and OpenGL ES 3.0+)
if (caps.contains(Caps.CoreProfile)) {
formatSwiz(formatToGL, Format.Alpha8, GL3.GL_R8, GL.GL_RED, GL.GL_UNSIGNED_BYTE);
formatSwiz(formatToGL, Format.Luminance8, GL3.GL_R8, GL.GL_RED, GL.GL_UNSIGNED_BYTE);
formatSwiz(formatToGL, Format.Luminance8Alpha8, GL3.GL_RG8, GL3.GL_RG, GL.GL_UNSIGNED_BYTE);
formatSwiz(formatToGL, Format.Luminance16F, GL3.GL_R16F, GL.GL_RED, GLExt.GL_HALF_FLOAT_ARB);
formatSwiz(formatToGL, Format.Luminance32F, GL3.GL_R32F, GL.GL_RED, GL.GL_FLOAT);
formatSwiz(formatToGL, Format.Luminance16FAlpha16F, GL3.GL_RG16F, GL3.GL_RG, GLExt.GL_HALF_FLOAT_ARB);
formatSrgbSwiz(formatToGL, Format.Luminance8, GLExt.GL_SRGB8_EXT, GL.GL_RED, GL.GL_UNSIGNED_BYTE);
formatSrgbSwiz(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SRGB8_ALPHA8_EXT, GL3.GL_RG, GL.GL_UNSIGNED_BYTE);
}
if (caps.contains(Caps.OpenGL20)) {
if (!caps.contains(Caps.CoreProfile)) {
format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);

@ -106,7 +106,7 @@ public class GLRenderer implements Renderer {
this.gl4 = gl instanceof GL4 ? (GL4)gl : null;
this.glfbo = glfbo;
this.glext = glext;
this.texUtil = new TextureUtil(gl, gl2, glext, context);
this.texUtil = new TextureUtil(gl, gl2, glext);
}
@Override
@ -257,13 +257,17 @@ public class GLRenderer implements Renderer {
// gl.glGetInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
// fragUniforms = intBuf16.get(0);
// logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
if (caps.contains(Caps.OpenGLES20)) {
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS));
} else {
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4);
}
limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS));
limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE));
limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
if (hasExtension("GL_ARB_draw_instanced") &&
hasExtension("GL_ARB_instanced_arrays")) {
hasExtension("GL_ARB_instanced_arrays")) {
caps.add(Caps.MeshInstancing);
}
@ -280,11 +284,11 @@ public class GLRenderer implements Renderer {
boolean hasFloatTexture;
hasFloatTexture = hasExtension("GL_OES_texture_half_float") &&
hasExtension("GL_OES_texture_float");
hasExtension("GL_OES_texture_float");
if (!hasFloatTexture) {
hasFloatTexture = hasExtension("GL_ARB_texture_float") &&
hasExtension("GL_ARB_half_float_pixel");
hasExtension("GL_ARB_half_float_pixel");
if (!hasFloatTexture) {
hasFloatTexture = caps.contains(Caps.OpenGL30);
@ -302,8 +306,8 @@ public class GLRenderer implements Renderer {
}
if (hasExtension("GL_OES_rgb8_rgba8") ||
hasExtension("GL_ARM_rgba8") ||
hasExtension("GL_EXT_texture_format_BGRA8888")) {
hasExtension("GL_ARM_rgba8") ||
hasExtension("GL_EXT_texture_format_BGRA8888")) {
caps.add(Caps.Rgba8);
}
@ -312,7 +316,7 @@ public class GLRenderer implements Renderer {
}
if (hasExtension("GL_ARB_color_buffer_float") &&
hasExtension("GL_ARB_half_float_pixel")) {
hasExtension("GL_ARB_half_float_pixel")) {
// XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
caps.add(Caps.FloatColorBuffer);
}
@ -322,7 +326,7 @@ public class GLRenderer implements Renderer {
}
if ((hasExtension("GL_EXT_packed_float") && hasFloatTexture) ||
caps.contains(Caps.OpenGL30)) {
caps.contains(Caps.OpenGL30)) {
// Either OpenGL3 is available or both packed_float & half_float_pixel.
caps.add(Caps.PackedFloatColorBuffer);
caps.add(Caps.PackedFloatTexture);
@ -350,13 +354,13 @@ public class GLRenderer implements Renderer {
}
if (hasExtension("GL_ARB_texture_non_power_of_two") ||
hasExtension("GL_OES_texture_npot") ||
caps.contains(Caps.OpenGL30)) {
hasExtension("GL_OES_texture_npot") ||
caps.contains(Caps.OpenGL30)) {
caps.add(Caps.NonPowerOfTwoTextures);
} else {
logger.log(Level.WARNING, "Your graphics card does not "
+ "support non-power-of-2 textures. "
+ "Some features might not work.");
+ "support non-power-of-2 textures. "
+ "Some features might not work.");
}
if (caps.contains(Caps.OpenGLES20)) {
@ -422,7 +426,7 @@ public class GLRenderer implements Renderer {
// Supports sRGB pipeline.
if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB"))
|| caps.contains(Caps.OpenGL30) ) {
|| caps.contains(Caps.OpenGL30) ) {
caps.add(Caps.Srgb);
}
@ -444,18 +448,18 @@ public class GLRenderer implements Renderer {
// Print context information
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
" * Vendor: {0}\n" +
" * Renderer: {1}\n" +
" * OpenGL Version: {2}\n" +
" * GLSL Version: {3}\n" +
" * Profile: {4}",
new Object[]{
gl.glGetString(GL.GL_VENDOR),
gl.glGetString(GL.GL_RENDERER),
gl.glGetString(GL.GL_VERSION),
gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility"
});
" * Vendor: {0}\n" +
" * Renderer: {1}\n" +
" * OpenGL Version: {2}\n" +
" * GLSL Version: {3}\n" +
" * Profile: {4}",
new Object[]{
gl.glGetString(GL.GL_VENDOR),
gl.glGetString(GL.GL_RENDERER),
gl.glGetString(GL.GL_VERSION),
gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility"
});
// Print capabilities (if fine logging is enabled)
if (logger.isLoggable(Level.FINE)) {
@ -536,8 +540,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Render State *|
\*********************************************************************/
|* Render State *|
\*********************************************************************/
public void setDepthRange(float start, float end) {
gl.glDepthRange(start, end);
}
@ -815,8 +819,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Camera and World transforms *|
\*********************************************************************/
|* Camera and World transforms *|
\*********************************************************************/
public void setViewPort(int x, int y, int w, int h) {
if (x != vpX || vpY != y || vpW != w || vpH != h) {
gl.glViewport(x, y, w, h);
@ -859,8 +863,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Shaders *|
\*********************************************************************/
|* Shaders *|
\*********************************************************************/
protected void updateUniformLocation(Shader shader, Uniform uniform) {
int loc = gl.glGetUniformLocation(shader.getId(), uniform.getName());
if (loc < 0) {
@ -1043,7 +1047,7 @@ public class GLRenderer implements Renderer {
if (gles2 && !language.equals("GLSL100")) {
throw new RendererException("This shader cannot run in OpenGL ES 2. "
+ "Only GLSL 1.00 shaders are supported.");
+ "Only GLSL 1.00 shaders are supported.");
}
// Upload shader source.
@ -1246,8 +1250,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Framebuffers *|
\*********************************************************************/
|* Framebuffers *|
\*********************************************************************/
public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
copyFrameBuffer(src, dst, true);
}
@ -1663,7 +1667,7 @@ public class GLRenderer implements Renderer {
gl.glReadPixels(vpX, vpY, vpW, vpH, glFormat, dataType, byteBuf);
}
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
GLImageFormat glFormat = texUtil.getImageFormatWithError(format, false);
readFrameBufferWithGLFormat(fb, byteBuf, glFormat.format, glFormat.dataType);
}
@ -1696,12 +1700,12 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Textures *|
\*********************************************************************/
|* Textures *|
\*********************************************************************/
private int convertTextureType(Texture.Type type, int samples, int face) {
if (samples > 1 && !caps.contains(Caps.TextureMultisample)) {
throw new RendererException("Multisample textures are not supported" +
" by the video hardware.");
" by the video hardware.");
}
switch (type) {
@ -1724,7 +1728,7 @@ public class GLRenderer implements Renderer {
case ThreeDimensional:
if (!caps.contains(Caps.OpenGL20)) {
throw new RendererException("3D textures are not supported" +
" by the video hardware.");
" by the video hardware.");
}
return GL2.GL_TEXTURE_3D;
case CubeMap:
@ -1878,8 +1882,8 @@ public class GLRenderer implements Renderer {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
}
}else{
//restoring default value
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
//restoring default value
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
}
tex.compareModeUpdated();
}
@ -1910,13 +1914,13 @@ public class GLRenderer implements Renderer {
if (!caps.contains(Caps.PartialNonPowerOfTwoTextures)) {
// Cannot use any type of NPOT texture (uncommon)
throw new RendererException("non-power-of-2 textures are not "
+ "supported by the video hardware");
+ "supported by the video hardware");
}
// Partial NPOT supported..
if (tex.getMinFilter().usesMipMapLevels()) {
throw new RendererException("non-power-of-2 textures with mip-maps "
+ "are not supported by the video hardware");
+ "are not supported by the video hardware");
}
switch (tex.getType()) {
@ -1924,7 +1928,7 @@ public class GLRenderer implements Renderer {
case ThreeDimensional:
if (tex.getWrap(WrapAxis.R) != Texture.WrapMode.EdgeClamp) {
throw new RendererException("repeating non-power-of-2 textures "
+ "are not supported by the video hardware");
+ "are not supported by the video hardware");
}
// fallthrough intentional!!!
case TwoDimensionalArray:
@ -1932,7 +1936,7 @@ public class GLRenderer implements Renderer {
if (tex.getWrap(WrapAxis.S) != Texture.WrapMode.EdgeClamp
|| tex.getWrap(WrapAxis.T) != Texture.WrapMode.EdgeClamp) {
throw new RendererException("repeating non-power-of-2 textures "
+ "are not supported by the video hardware");
+ "are not supported by the video hardware");
}
break;
default:
@ -2095,9 +2099,9 @@ public class GLRenderer implements Renderer {
int nextWidth = FastMath.nearestPowerOfTwo(tex.getImage().getWidth());
int nextHeight = FastMath.nearestPowerOfTwo(tex.getImage().getHeight());
logger.log(Level.WARNING,
"Non-power-of-2 textures are not supported! Scaling texture '" + tex.getName() +
"' of size " + tex.getImage().getWidth() + "x" + tex.getImage().getHeight() +
" to " + nextWidth + "x" + nextHeight);
"Non-power-of-2 textures are not supported! Scaling texture '" + tex.getName() +
"' of size " + tex.getImage().getWidth() + "x" + tex.getImage().getHeight() +
" to " + nextWidth + "x" + nextHeight);
}
scaleToPot = true;
}
@ -2147,8 +2151,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Vertex Buffers and Attributes *|
\*********************************************************************/
|* Vertex Buffers and Attributes *|
\*********************************************************************/
private int convertUsage(Usage usage) {
switch (usage) {
case Static:
@ -2487,8 +2491,8 @@ public class GLRenderer implements Renderer {
}
/*********************************************************************\
|* Render Calls *|
\*********************************************************************/
|* Render Calls *|
\*********************************************************************/
public int convertElementMode(Mesh.Mode mode) {
switch (mode) {
case Points:
@ -2654,7 +2658,7 @@ public class GLRenderer implements Renderer {
if (!caps.contains(Caps.Srgb) && enableSrgb) {
// Not supported, sorry.
logger.warning("sRGB framebuffer is not supported " +
"by video hardware, but was requested.");
"by video hardware, but was requested.");
return;
}

@ -54,14 +54,12 @@ final class TextureUtil {
private final GL gl;
private final GL2 gl2;
private final GLExt glext;
private final RenderContext context;
private GLImageFormat[][] formats;
public TextureUtil(GL gl, GL2 gl2, GLExt glext, RenderContext context) {
public TextureUtil(GL gl, GL2 gl2, GLExt glext) {
this.gl = gl;
this.gl2 = gl2;
this.glext = glext;
this.context = context;
}
public void initialize(EnumSet<Caps> caps) {
@ -103,6 +101,33 @@ final class TextureUtil {
return glFmt;
}
private void setupTextureSwizzle(int target, Format format) {
// Needed for OpenGL 3.3 to support luminance / alpha formats
switch (format) {
case Alpha8:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_RED);
break;
case Luminance8:
case Luminance16F:
case Luminance32F:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_ONE);
break;
case Luminance8Alpha8:
case Luminance16FAlpha16F:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_GREEN);
break;
}
}
private void uploadTextureLevel(GLImageFormat format, int target, int level, int slice, int sliceCount, int width, int height, int depth, int samples, ByteBuffer data) {
if (format.compressed && data != null) {
if (target == GL2.GL_TEXTURE_3D) {
@ -243,6 +268,11 @@ final class TextureUtil {
int samples = image.getMultiSamples();
// For OGL3 core: setup texture swizzle.
if (oglFormat.swizzleRequired) {
setupTextureSwizzle(target, jmeFormat);
}
for (int i = 0; i < mipSizes.length; i++) {
int mipWidth = Math.max(1, width >> i);
int mipHeight = Math.max(1, height >> i);

@ -0,0 +1,14 @@
#if _VERSION_ >= 150
out vec4 outFragColor;
# define texture1D texture
# define texture2D texture
# define texture3D texture
# define texture2DLod texture
# if defined VERTEX_SHADER
# define varying out
# define attribute in
# elif defined FRAGMENT_SHADER
# define varying in
# define gl_FragColor outFragColor
# endif
#endif

@ -82,6 +82,7 @@ public class ZipLocator implements AssetLocator {
public AssetInfo locate(AssetManager manager, AssetKey key) {
String name = key.getName();
if(name.startsWith("/"))name=name.substring(1);
ZipEntry entry = zipfile.getEntry(name);
if (entry == null)
return null;

@ -0,0 +1,148 @@
/*
* Copyright (c) 2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.network.service;
import com.jme3.network.HostedConnection;
import com.jme3.network.Server;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Convenient base class for HostedServices providing some default HostedService
* interface implementations as well as a few convenience methods
* such as getServiceManager() and getService(type). This implementation
* enhances the default capabilities provided by AbstractHostedService by
* adding automatic connection management.
*
* <p>Subclasses must at least override the onInitialize(), startHostingOnConnection(), and
* stopHostingOnConnection() methods to handle service and connection initialization.</p>
*
* <p>An autoHost flag controls whether startHostingOnConnection() is called
* automatically when new connections are detected. If autoHohst is false then it
* is up to the implementation or appliction to specifically start hosting at
* some point.</p>
*
* @author Paul Speed
*/
public abstract class AbstractHostedConnectionService extends AbstractHostedService {
static final Logger log = Logger.getLogger(AbstractHostedConnectionService.class.getName());
private boolean autoHost;
/**
* Creates a new HostedService that will autohost connections
* when detected.
*/
protected AbstractHostedConnectionService() {
this(true);
}
/**
* Creates a new HostedService that will automatically host
* connections only if autoHost is true.
*/
protected AbstractHostedConnectionService( boolean autoHost ) {
this.autoHost = autoHost;
}
/**
* When set to true, all new connections will automatically have
* hosting services attached to them by calling startHostingOnConnection().
* If this is set to false then it is up to the application or other services
* to eventually call startHostingOnConnection().
*
* <p>Reasons for doing this vary but usually would be because
* the client shouldn't be allowed to perform any service-related calls until
* it has provided more information... for example, logging in.</p>
*/
public void setAutoHost( boolean b ) {
this.autoHost = b;
}
/**
* Returns true if this service automatically attaches
* hosting capabilities to new connections.
*/
public boolean getAutoHost() {
return autoHost;
}
/**
* Performs implementation specific connection hosting setup.
* Generally this involves setting up some handlers or session
* attributes on the connection. If autoHost is true then this
* method is called automatically during connectionAdded()
* processing.
*/
public abstract void startHostingOnConnection( HostedConnection hc );
/**
* Performs implementation specific connection tear-down.
* This will be called automatically when the connectionRemoved()
* event occurs... whether the application has already called it
* or not.
*/
public abstract void stopHostingOnConnection( HostedConnection hc );
/**
* Called internally when a new connection is detected for
* the server. If the current autoHost property is true then
* startHostingOnConnection(hc) is called.
*/
@Override
public void connectionAdded(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionAdded({0}, {1})", new Object[]{server, hc});
}
if( autoHost ) {
startHostingOnConnection(hc);
}
}
/**
* Called internally when an existing connection is leaving
* the server. This method always calls stopHostingOnConnection(hc).
* Implementations should be aware that if they stopHostingOnConnection()
* early that they will get a second call when the connection goes away.
*/
@Override
public void connectionRemoved(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionRemoved({0}, {1})", new Object[]{server, hc});
}
stopHostingOnConnection(hc);
}
}

@ -35,8 +35,8 @@ package com.jme3.network.service.rpc;
import com.jme3.network.HostedConnection;
import com.jme3.network.Server;
import com.jme3.network.serializing.Serializer;
import com.jme3.network.service.AbstractHostedConnectionService;
import com.jme3.network.util.SessionDataDelegator;
import com.jme3.network.service.AbstractHostedService;
import com.jme3.network.service.HostedServiceManager;
import com.jme3.network.service.rpc.msg.RpcCallMessage;
import com.jme3.network.service.rpc.msg.RpcResponseMessage;
@ -62,13 +62,12 @@ import java.util.logging.Logger;
*
* @author Paul Speed
*/
public class RpcHostedService extends AbstractHostedService {
public class RpcHostedService extends AbstractHostedConnectionService {
private static final String ATTRIBUTE_NAME = "rpcSession";
static final Logger log = Logger.getLogger(RpcHostedService.class.getName());
private boolean autoHost;
private SessionDataDelegator delegator;
/**
@ -87,7 +86,7 @@ public class RpcHostedService extends AbstractHostedService {
* on the specified 'autoHost' flag.
*/
public RpcHostedService( boolean autoHost ) {
this.autoHost = autoHost;
super(autoHost);
// This works for me... has to be different in
// the general case
@ -115,31 +114,6 @@ public class RpcHostedService extends AbstractHostedService {
}
}
/**
* When set to true, all new connections will automatically have
* RPC hosting services attached to them, meaning they can send
* and receive RPC calls. If this is set to false then it is up
* to other services to eventually call startHostingOnConnection().
*
* <p>Reasons for doing this vary but usually would be because
* the client shouldn't be allowed to perform any RPC calls until
* it has provided more information. In general, this is unnecessary
* because the RpcHandler registries are not shared. Each client
* gets their own and RPC calls will fail until the appropriate
* objects have been registtered.</p>
*/
public void setAutoHost( boolean b ) {
this.autoHost = b;
}
/**
* Returns true if this service automatically attaches RPC
* hosting capabilities to new connections.
*/
public boolean getAutoHost() {
return autoHost;
}
/**
* Retrieves the RpcConnection for the specified HostedConnection
* if that HostedConnection has had RPC services started using
@ -157,6 +131,7 @@ public class RpcHostedService extends AbstractHostedService {
* This method is called automatically for all new connections if
* autohost is set to true.
*/
@Override
public void startHostingOnConnection( HostedConnection hc ) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "startHostingOnConnection:{0}", hc);
@ -173,6 +148,7 @@ public class RpcHostedService extends AbstractHostedService {
* This method is called automatically for all leaving connections if
* autohost is set to true.
*/
@Override
public void stopHostingOnConnection( HostedConnection hc ) {
RpcConnection rpc = hc.getAttribute(ATTRIBUTE_NAME);
if( rpc == null ) {
@ -195,33 +171,5 @@ public class RpcHostedService extends AbstractHostedService {
server.removeMessageListener(delegator, delegator.getMessageTypes());
}
/**
* Called internally when a new connection is detected for
* the server. If the current autoHost property is true then
* startHostingOnConnection(hc) is called.
*/
@Override
public void connectionAdded(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionAdded({0}, {1})", new Object[]{server, hc});
}
if( autoHost ) {
startHostingOnConnection(hc);
}
}
/**
* Called internally when an existing connection is leaving
* the server. If the current autoHost property is true then
* stopHostingOnConnection(hc) is called.
*/
@Override
public void connectionRemoved(Server server, HostedConnection hc) {
if( log.isLoggable(Level.FINEST) ) {
log.log(Level.FINEST, "connectionRemoved({0}, {1})", new Object[]{server, hc});
}
stopHostingOnConnection(hc);
}
}

@ -38,6 +38,8 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import de.lessvoid.nifty.Nifty;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
@ -45,6 +47,8 @@ import java.io.IOException;
*/
public class GuiEvent extends AbstractCinematicEvent {
static final Logger log = Logger.getLogger(GuiEvent.class.getName());
protected String screen;
protected Nifty nifty;
@ -76,7 +80,7 @@ public class GuiEvent extends AbstractCinematicEvent {
@Override
public void onPlay() {
System.out.println("screen should be " + screen);
log.log(Level.FINEST, "screen should be {0}", screen);
nifty.gotoScreen(screen);
}

@ -212,6 +212,7 @@ public final class ImportModel implements ActionListener {
}
replaceLocatedTextures(spat, manager);
targetData.saveAsset();
targetData.closeAsset();
((SpatialAssetDataObject) targetModel).getLookupContents().remove(tempProjectManager);
}
} catch (Exception ex) {

@ -126,6 +126,7 @@ public final class ModelImporterVisualPanel3 extends JPanel {
DialogDisplayer.getDefault().notifyLater(msg);
Exceptions.printStackTrace(e);
}
data.closeAsset();
manager.unregisterLocator(manager.getAssetFolderName(), UberAssetLocator.class);
panel.fireChangeEvent();
}

@ -11,6 +11,7 @@ import com.jme3.gde.core.scene.controller.SceneToolController;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.scenecomposer.tools.PickManager;
import com.jme3.gde.scenecomposer.tools.shortcuts.ShortcutManager;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.light.Light;
import com.jme3.light.PointLight;
@ -31,6 +32,7 @@ import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import java.util.concurrent.Callable;
import org.openide.util.Lookup;
/**
*
@ -184,7 +186,12 @@ public class SceneComposerToolController extends SceneToolController {
* @param camera
*/
public void doEditToolActivatedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
if (editTool != null) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.getActiveShortcut().setCamera(camera);
scm.getActiveShortcut().actionPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
} else if (editTool != null) {
editTool.setCamera(camera);
editTool.actionPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
}
@ -198,36 +205,66 @@ public class SceneComposerToolController extends SceneToolController {
* @param camera
*/
public void doEditToolActivatedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
if (editTool != null) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.getActiveShortcut().setCamera(camera);
scm.getActiveShortcut().actionSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
} else if (editTool != null) {
editTool.setCamera(camera);
editTool.actionSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
}
}
public void doEditToolMoved(Vector2f mouseLoc, Camera camera) {
if (editTool != null) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.getActiveShortcut().setCamera(camera);
scm.getActiveShortcut().mouseMoved(mouseLoc, rootNode, editorController.getCurrentDataObject(), selectedSpatial);
} else if (editTool != null) {
editTool.setCamera(camera);
editTool.mouseMoved(mouseLoc, rootNode, editorController.getCurrentDataObject(), selectedSpatial);
}
}
public void doEditToolDraggedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
if (editTool != null) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.getActiveShortcut().setCamera(camera);
scm.getActiveShortcut().draggedPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
} else if (editTool != null) {
editTool.setCamera(camera);
editTool.draggedPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
}
}
public void doEditToolDraggedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
if (editTool != null) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.getActiveShortcut().setCamera(null);
scm.getActiveShortcut().draggedSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
} else if (editTool != null) {
editTool.setCamera(camera);
editTool.draggedSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
}
}
void doKeyPressed(KeyInputEvent kie) {
if (editTool != null) {
editTool.keyPressed(kie);
public void doKeyPressed(KeyInputEvent kie) {
ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
if (scm.isActive()) {
scm.doKeyPressed(kie);
} else {
if (scm.activateShortcut(kie)) {
scm.getActiveShortcut().activate(manager, toolsNode, onTopToolsNode, selected, this);
} else {
if (editTool != null) {
editTool.keyPressed(kie);
}
}
}
}

@ -1,204 +0,0 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneEditTool;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
import org.openide.util.lookup.ServiceProvider;
/**
*
* @author Nehon
*/
@ServiceProvider(service = MoveManager.class)
public class MoveManager {
private Vector3f startLoc;
private Vector3f startWorldLoc;
private Vector3f lastLoc;
private Vector3f offset;
private Node alternativePickTarget = null;
private Node plane;
private Spatial spatial;
protected static final Quaternion XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
protected static final Quaternion YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));
protected static final Quaternion XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0));
//temp vars
private Quaternion rot = new Quaternion();
private Vector3f newPos = new Vector3f();
public MoveManager() {
float size = 1000;
Geometry g = new Geometry("plane", new Quad(size, size));
g.setLocalTranslation(-size / 2, -size / 2, 0);
plane = new Node();
plane.attachChild(g);
}
public Vector3f getOffset() {
return offset;
}
public void reset() {
offset = null;
startLoc = null;
startWorldLoc = null;
lastLoc = null;
spatial = null;
alternativePickTarget = null;
}
public void initiateMove(Spatial selectedSpatial, Quaternion planeRotation, boolean local) {
spatial = selectedSpatial;
startLoc = selectedSpatial.getLocalTranslation().clone();
startWorldLoc = selectedSpatial.getWorldTranslation().clone();
if (local) {
rot.set(selectedSpatial.getWorldRotation());
plane.setLocalRotation(rot.multLocal(planeRotation));
} else {
rot.set(planeRotation);
}
plane.setLocalRotation(rot);
plane.setLocalTranslation(startWorldLoc);
}
public void updatePlaneRotation(Quaternion planeRotation) {
plane.setLocalRotation(rot);
}
public boolean move(Camera camera, Vector2f screenCoord) {
return move(camera, screenCoord, Vector3f.UNIT_XYZ, false);
}
public boolean move(Camera camera, Vector2f screenCoord, Vector3f constraintAxis, boolean gridSnap) {
Node toPick = alternativePickTarget == null ? plane : alternativePickTarget;
Vector3f planeHit = SceneEditTool.pickWorldLocation(camera, screenCoord, toPick, alternativePickTarget == null ? null : spatial);
if (planeHit == null) {
return false;
}
Spatial parent = spatial.getParent();
//we are moving the root node, there is a slight chance that something went wrong.
if (parent == null) {
return false;
}
//offset in world space
if (offset == null) {
offset = planeHit.subtract(spatial.getWorldTranslation()); // get the offset when we start so it doesn't jump
}
newPos.set(planeHit).subtractLocal(offset);
//constraining the translation with the contraintAxis.
Vector3f tmp = startWorldLoc.mult(Vector3f.UNIT_XYZ.subtract(constraintAxis));
newPos.multLocal(constraintAxis).addLocal(tmp);
worldToLocalMove(gridSnap);
return true;
}
private void worldToLocalMove(boolean gridSnap) {
//snap to grid (grid is assumed 1 WU per cell)
if (gridSnap) {
newPos.set(Math.round(newPos.x), Math.round(newPos.y), Math.round(newPos.z));
}
//computing the inverse world transform to get the new localtranslation
newPos.subtractLocal(spatial.getParent().getWorldTranslation());
newPos = spatial.getParent().getWorldRotation().inverse().normalizeLocal().multLocal(newPos);
newPos.divideLocal(spatial.getParent().getWorldScale());
lastLoc = newPos;
spatial.setLocalTranslation(newPos);
RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
if (control != null) {
control.setPhysicsLocation(spatial.getWorldTranslation());
}
CharacterControl character = spatial.getControl(CharacterControl.class);
if (character != null) {
character.setPhysicsLocation(spatial.getWorldTranslation());
}
}
public boolean moveAcross(Vector3f constraintAxis, float value, boolean gridSnap) {
newPos.set(startWorldLoc).addLocal(constraintAxis.mult(value));
Spatial parent = spatial.getParent();
//we are moving the root node, there is a slight chance that something went wrong.
if (parent == null) {
return false;
}
worldToLocalMove(gridSnap);
return true;
}
public MoveUndo makeUndo() {
return new MoveUndo(spatial, startLoc, lastLoc);
}
public void setAlternativePickTarget(Node alternativePickTarget) {
this.alternativePickTarget = alternativePickTarget;
}
protected class MoveUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Vector3f before = new Vector3f(), after = new Vector3f();
MoveUndo(Spatial spatial, Vector3f before, Vector3f after) {
this.spatial = spatial;
this.before.set(before);
if (after != null) {
this.after.set(after);
}
}
@Override
public void sceneUndo() {
spatial.setLocalTranslation(before);
RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
if (control != null) {
control.setPhysicsLocation(spatial.getWorldTranslation());
}
CharacterControl character = spatial.getControl(CharacterControl.class);
if (character != null) {
character.setPhysicsLocation(spatial.getWorldTranslation());
}
// toolController.selectedSpatialTransformed();
}
@Override
public void sceneRedo() {
spatial.setLocalTranslation(after);
RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
if (control != null) {
control.setPhysicsLocation(spatial.getWorldTranslation());
}
CharacterControl character = spatial.getControl(CharacterControl.class);
if (character != null) {
character.setPhysicsLocation(spatial.getWorldTranslation());
}
//toolController.selectedSpatialTransformed();
}
public void setAfter(Vector3f after) {
this.after.set(after);
}
}
}

@ -33,9 +33,9 @@ public class PickManager {
private Spatial spatial;
private SceneComposerToolController.TransformationType transformationType;
protected static final Quaternion PLANE_XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
protected static final Quaternion PLANE_YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));//YAW090
protected static final Quaternion PLANE_XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0)); //PITCH090
public static final Quaternion PLANE_XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
public static final Quaternion PLANE_YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));//YAW090
public static final Quaternion PLANE_XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0)); //PITCH090
public PickManager() {
@ -75,7 +75,7 @@ public class PickManager {
origineRotation = new Quaternion(Quaternion.IDENTITY);
} else if (transformationType == SceneComposerToolController.TransformationType.camera) {
rot.set(camera.getRotation());
origineRotation = camera.getRotation().clone();
origineRotation = camera.getRotation();
}
plane.setLocalRotation(rot);
}
@ -87,10 +87,6 @@ public class PickManager {
* @return true if the the new picked location is set, else return false.
*/
public boolean updatePick(Camera camera, Vector2f screenCoord) {
if(transformationType == SceneComposerToolController.TransformationType.camera){
origineRotation = camera.getRotation();
plane.setLocalRotation(camera.getRotation());
}
finalPickLoc = SceneEditTool.pickWorldLocation(camera, screenCoord, plane, null);
return finalPickLoc != null;
}

@ -25,7 +25,6 @@ import org.openide.util.Lookup;
public class RotateTool extends SceneEditTool {
private Vector3f pickedMarker;
private Vector2f lastScreenCoord;
private Quaternion startRotate;
private Quaternion lastRotate;
private boolean wasDragging = false;
@ -48,9 +47,8 @@ public class RotateTool extends SceneEditTool {
if (!pressed) {
setDefaultAxisMarkerColors();
pickedMarker = null; // mouse released, reset selection
lastScreenCoord = null;
if (wasDragging) {
actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
wasDragging = false;
}
pickManager.reset();
@ -100,10 +98,9 @@ public class RotateTool extends SceneEditTool {
if (!pressed) {
setDefaultAxisMarkerColors();
pickedMarker = null; // mouse released, reset selection
lastScreenCoord = null;
if (wasDragging) {
actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
wasDragging = false;
}
pickManager.reset();
@ -138,12 +135,12 @@ public class RotateTool extends SceneEditTool {
}
}
private class ScaleUndo extends AbstractUndoableSceneEdit {
private class RotateUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Quaternion before, after;
ScaleUndo(Spatial spatial, Quaternion before, Quaternion after) {
RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
this.spatial = spatial;
this.before = before;
this.after = after;

@ -8,425 +8,51 @@ import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneEditTool;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.terrain.Terrain;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
* This duplicates the Blender manipulate tool.
* It supports quick access to Grab, Rotate, and Scale operations
* by typing one of the following keys: 'g', 'r', or 's'
* Those keys can be followed by an axis key to specify what axis
* to perform the transformation: x, y, z
* Then, after the operation and axis are selected, you can type in a
* number and then hit 'enter' to complete the transformation.
* This duplicates the Blender manipulate tool. It supports quick access to
* Grab, Rotate, and Scale operations by typing one of the following keys: 'g',
* 'r', or 's' Those keys can be followed by an axis key to specify what axis to
* perform the transformation: x, y, z Then, after the operation and axis are
* selected, you can type in a number and then hit 'enter' to complete the
* transformation.
*
* Ctrl+Shift+D will duplicate an object
* X will delete an object
* Ctrl+Shift+D will duplicate an object X will delete an object
*
* ITEMS TO FINISH:
* 1) fixed scale and rotation values by holding Ctrl and dragging mouse
* BUGS:
* 1) window always needs focus from primary click when it should focus from secondary and middle mouse
* ITEMS TO FINISH: 1) fixed scale and rotation values by holding Ctrl and
* dragging mouse BUGS: 1) window always needs focus from primary click when it
* should focus from secondary and middle mouse
*
* @author Brent Owens
*/
public class SelectTool extends SceneEditTool {
private enum State {
translate, rotate, scale
};
private State currentState = null;
private Vector3f currentAxis = Vector3f.UNIT_XYZ;
private StringBuilder numberBuilder = new StringBuilder(); // gets appended with numbers
private Quaternion startRot;
private Vector3f startTrans;
private Vector3f startScale;
private boolean wasDraggingL = false;
private boolean wasDraggingR = false;
private boolean wasDownR = false;
private boolean ctrlDown = false;
private boolean shiftDown = false;
private boolean altDown = false;
private MoveManager.MoveUndo moving;
private ScaleUndo scaling;
private RotateUndo rotating;
private Vector2f startMouseCoord; // for scaling and rotation
private Vector2f startSelectedCoord; // for scaling and rotation
private float lastRotAngle; // used for rotation
/**
* This is stateful:
* First it checks for a command (rotate, translate, delete, etc..)
* Then it checks for an axis (x,y,z)
* Then it checks for a number (user typed a number
* Then, finally, it checks if Enter was hit.
* This is stateful: First it checks for a command (rotate, translate,
* delete, etc..) Then it checks for an axis (x,y,z) Then it checks for a
* number (user typed a number Then, finally, it checks if Enter was hit.
*
* If either of the commands was actioned, the preceeding states/axis/amount
* will be reset. For example if the user types: G Y 2 R
* Then it will:
* 1) Set state as 'Translate' for the G (grab)
* 2) Set the axis as 'Y'; it will translate along the Y axis
* 3) Distance will be 2, when the 2 key is hit
* 4) Distance, Axis, and state are then reset because a new state was set: Rotate
* it won't actually translate because 'Enter' was not hit and 'R' reset the state.
* will be reset. For example if the user types: G Y 2 R Then it will: 1)
* Set state as 'Translate' for the G (grab) 2) Set the axis as 'Y'; it will
* translate along the Y axis 3) Distance will be 2, when the 2 key is hit
* 4) Distance, Axis, and state are then reset because a new state was set:
* Rotate it won't actually translate because 'Enter' was not hit and 'R'
* reset the state.
*
*/
@Override
public void keyPressed(KeyInputEvent kie) {
checkModificatorKeys(kie); // alt,shift,ctrl
Spatial selected = toolController.getSelectedSpatial();
if (selected == null) {
return; // only do anything if a spatial is selected
}
// key released
if (kie.isPressed()) {
boolean commandUsed = checkCommandKey(kie);
boolean stateChange = checkStateKey(kie);
boolean axisChange = checkAxisKey(kie);
boolean numberChange = checkNumberKey(kie);
boolean enterHit = checkEnterHit(kie);
boolean escHit = checkEscHit(kie);
if (commandUsed) {
return; // commands take priority
}
if (stateChange) {
currentAxis = Vector3f.UNIT_XYZ;
numberBuilder = new StringBuilder();
recordInitialState(selected);
} else if (axisChange) {
} else if (numberChange) {
} else if (enterHit) {
if (currentState != null && numberBuilder.length() > 0) {
applyKeyedChangeState(selected);
clearState(false);
}
}
// -----------------------
// reset conditions below:
if (escHit) {
if (moving != null) {
moving.sceneUndo();
}
moving = null;
clearState();
}
if (!stateChange && !axisChange && !numberChange && !enterHit && !escHit) {
// nothing valid was hit, reset the state
//clearState(); // this will be
}
}
}
/**
* Abort any manipulations
*/
private void clearState() {
clearState(true);
}
private void clearState(boolean resetSelected) {
Spatial selected = toolController.getSelectedSpatial();
if (resetSelected && selected != null) {
// reset the transforms
if (startRot != null) {
selected.setLocalRotation(startRot);
}
if (startTrans != null) {
selected.setLocalTranslation(startTrans);
}
if (startScale != null) {
selected.setLocalScale(startScale);
}
}
currentState = null;
currentAxis = Vector3f.UNIT_XYZ;
numberBuilder = new StringBuilder();
startRot = null;
startTrans = null;
startScale = null;
startMouseCoord = null;
startSelectedCoord = null;
lastRotAngle = 0;
}
private void recordInitialState(Spatial selected) {
startRot = selected.getLocalRotation().clone();
startTrans = selected.getLocalTranslation().clone();
startScale = selected.getLocalScale().clone();
}
/**
* Applies the changes entered by a number, not by mouse.
* Translate: adds the value to the current local translation
* Rotate: rotates by X degrees
* Scale: scale the current scale by X amount
*/
private void applyKeyedChangeState(Spatial selected) {
Float value = null;
try {
value = new Float(numberBuilder.toString());
} catch (NumberFormatException e) {
return;
}
if (currentState == State.translate) {
MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
moveManager.moveAcross(currentAxis, value, toolController.isSnapToGrid());
moving.setAfter(selected.getLocalTranslation());
actionPerformed(moving);
moving = null;
} else if (currentState == State.scale) {
float x = 1, y = 1, z = 1;
if (currentAxis == Vector3f.UNIT_X) {
x = value;
} else if (currentAxis == Vector3f.UNIT_Y) {
y = value;
} else if (currentAxis == Vector3f.UNIT_Z) {
z = value;
} else if (currentAxis == Vector3f.UNIT_XYZ) {
x = value;
y = value;
z = value;
}
Vector3f before = selected.getLocalScale().clone();
Vector3f after = selected.getLocalScale().multLocal(x, y, z);
selected.setLocalScale(after);
actionPerformed(new ScaleUndo(selected, before, after));
} else if (currentState == State.rotate) {
float x = 0, y = 0, z = 0;
if (currentAxis == Vector3f.UNIT_X) {
x = 1;
} else if (currentAxis == Vector3f.UNIT_Y) {
y = 1;
} else if (currentAxis == Vector3f.UNIT_Z) {
z = 1;
}
Vector3f axis = new Vector3f(x, y, z);
Quaternion initialRot = selected.getLocalRotation().clone();
Quaternion rot = new Quaternion();
rot = rot.fromAngleAxis(value * FastMath.DEG_TO_RAD, axis);
selected.setLocalRotation(selected.getLocalRotation().mult(rot));
RotateUndo undo = new RotateUndo(selected, initialRot, rot);
actionPerformed(undo);
toolController.updateSelection(null);// force a re-draw of the bbox shape
toolController.updateSelection(selected);
}
clearState(false);
}
private void checkModificatorKeys(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_LCONTROL || kie.getKeyCode() == KeyInput.KEY_RCONTROL) {
ctrlDown = kie.isPressed();
}
if (kie.getKeyCode() == KeyInput.KEY_LSHIFT || kie.getKeyCode() == KeyInput.KEY_RSHIFT) {
shiftDown = kie.isPressed();
}
if (kie.getKeyCode() == KeyInput.KEY_LMENU || kie.getKeyCode() == KeyInput.KEY_RMENU) {
altDown = kie.isPressed();
}
}
private boolean checkCommandKey(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_D) {
if (shiftDown) {
duplicateSelected();
return true;
}
}
// X will only delete if the user isn't already transforming
if (currentState == null && kie.getKeyCode() == KeyInput.KEY_X) {
if (!ctrlDown && !shiftDown) {
deleteSelected();
return true;
}
}
return false;
}
private boolean checkStateKey(KeyInputEvent kie) {
Spatial selected = toolController.getSelectedSpatial();
if (kie.getKeyCode() == KeyInput.KEY_G && !ctrlDown) {
currentState = State.translate;
MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
moveManager.reset();
Quaternion rot = camera.getRotation().mult(new Quaternion().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y));
moveManager.initiateMove(selected, rot, false);
moving = moveManager.makeUndo();
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_R && !ctrlDown) {
currentState = State.rotate;
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_S && !ctrlDown) {
currentState = State.scale;
return true;
}
return false;
}
private boolean checkAxisKey(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_X) {
currentAxis = Vector3f.UNIT_X;
checkMovePlane(MoveManager.XY, MoveManager.XZ);
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_Y) {
currentAxis = Vector3f.UNIT_Y;
checkMovePlane(MoveManager.XY, MoveManager.YZ);
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_Z) {
currentAxis = Vector3f.UNIT_Z;
checkMovePlane(MoveManager.XZ, MoveManager.YZ);
return true;
}
return false;
}
private void checkMovePlane(Quaternion rot1, Quaternion rot2) {
if (currentState == State.translate) {
MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
Quaternion rot = camera.getRotation().mult(new Quaternion().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y));
Quaternion planRot = null;
if (rot.dot(rot1) < rot.dot(rot2)) {
planRot = rot1;
} else {
planRot = rot2;
}
moveManager.updatePlaneRotation(planRot);
}
}
private boolean checkNumberKey(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_MINUS) {
if (numberBuilder.length() > 0) {
if (numberBuilder.charAt(0) == '-') {
numberBuilder.replace(0, 1, "");
} else {
numberBuilder.insert(0, '-');
}
} else {
numberBuilder.append('-');
}
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_0 || kie.getKeyCode() == KeyInput.KEY_NUMPAD0) {
numberBuilder.append('0');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_1 || kie.getKeyCode() == KeyInput.KEY_NUMPAD1) {
numberBuilder.append('1');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_2 || kie.getKeyCode() == KeyInput.KEY_NUMPAD2) {
numberBuilder.append('2');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_3 || kie.getKeyCode() == KeyInput.KEY_NUMPAD3) {
numberBuilder.append('3');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_4 || kie.getKeyCode() == KeyInput.KEY_NUMPAD4) {
numberBuilder.append('4');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_5 || kie.getKeyCode() == KeyInput.KEY_NUMPAD5) {
numberBuilder.append('5');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_6 || kie.getKeyCode() == KeyInput.KEY_NUMPAD6) {
numberBuilder.append('6');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_7 || kie.getKeyCode() == KeyInput.KEY_NUMPAD7) {
numberBuilder.append('7');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_8 || kie.getKeyCode() == KeyInput.KEY_NUMPAD8) {
numberBuilder.append('8');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_9 || kie.getKeyCode() == KeyInput.KEY_NUMPAD9) {
numberBuilder.append('9');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_PERIOD) {
if (numberBuilder.indexOf(".") == -1) { // if it doesn't exist yet
if (numberBuilder.length() == 0
|| (numberBuilder.length() == 1 && numberBuilder.charAt(0) == '-')) {
numberBuilder.append("0.");
} else {
numberBuilder.append(".");
}
}
return true;
}
return false;
}
private boolean checkEnterHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_RETURN) {
return true;
}
return false;
}
private boolean checkEscHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_ESCAPE) {
return true;
}
return false;
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
if (!pressed) {
Spatial selected = toolController.getSelectedSpatial();
// left mouse released
if (!wasDraggingL) {
// left mouse pressed
if (currentState != null) {
// finish manipulating the spatial
if (moving != null) {
moving.setAfter(selected.getLocalTranslation());
actionPerformed(moving);
moving = null;
clearState(false);
} else if (scaling != null) {
scaling.after = selected.getLocalScale().clone();
actionPerformed(scaling);
scaling = null;
clearState(false);
toolController.rebuildSelectionBox();
} else if (rotating != null) {
rotating.after = selected.getLocalRotation().clone();
actionPerformed(rotating);
rotating = null;
clearState(false);
}
} else {
// mouse released and wasn't dragging, place cursor
final Vector3f result = pickWorldLocation(getCamera(), screenCoord, rootNode);
if (result != null) {
if (toolController.isSnapToGrid()) {
result.set(Math.round(result.x), result.y, Math.round(result.z));
}
toolController.setCursorLocation(result);
}
}
}
wasDraggingL = false;
}
}
@Override
@ -435,19 +61,7 @@ public class SelectTool extends SceneEditTool {
Spatial selected = toolController.getSelectedSpatial();
// mouse down
if (moving != null) {
moving.sceneUndo();
moving = null;
clearState();
} else if (scaling != null) {
scaling.sceneUndo();
scaling = null;
clearState();
} else if (rotating != null) {
rotating.sceneUndo();
rotating = null;
clearState();
} else if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already
if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already
// pick on the spot
Spatial s = pickWorldSpatial(camera, screenCoord, rootNode);
if (!toolController.selectTerrain() && isTerrain(s)) {
@ -498,8 +112,8 @@ public class SelectTool extends SceneEditTool {
}
/**
* Climb up the spatial until we find the first node parent.
* TODO: use userData to determine the actual model's parent.
* Climb up the spatial until we find the first node parent. TODO: use
* userData to determine the actual model's parent.
*/
private Spatial findModelNodeParent(Spatial child) {
if (child == null) {
@ -519,14 +133,10 @@ public class SelectTool extends SceneEditTool {
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject currentDataObject, JmeSpatial selectedSpatial) {
if (currentState != null) {
handleMouseManipulate(screenCoord, currentState, currentAxis, rootNode, currentDataObject, selectedSpatial);
}
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
wasDraggingL = pressed;
}
@Override
@ -535,252 +145,8 @@ public class SelectTool extends SceneEditTool {
}
/**
* Manipulate the spatial
*/
private void handleMouseManipulate(Vector2f screenCoord,
State state,
Vector3f axis,
JmeNode rootNode,
DataObject currentDataObject,
JmeSpatial selectedSpatial) {
if (state == State.translate) {
doMouseTranslate(axis, screenCoord, rootNode, selectedSpatial);
} else if (state == State.scale) {
doMouseScale(axis, screenCoord, rootNode, selectedSpatial);
} else if (state == State.rotate) {
doMouseRotate(axis, screenCoord, rootNode, selectedSpatial);
}
}
private void doMouseTranslate(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
if (toolController.isSnapToScene()) {
moveManager.setAlternativePickTarget(rootNode.getLookup().lookup(Node.class));
}
// free form translation
moveManager.move(camera, screenCoord, axis, toolController.isSnapToGrid());
}
private void doMouseScale(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
Spatial selected = toolController.getSelectedSpatial();
// scale based on the original mouse position and original model-to-screen position
// and compare that to the distance from the new mouse position and the original distance
if (startMouseCoord == null) {
startMouseCoord = screenCoord.clone();
}
if (startSelectedCoord == null) {
Vector3f screen = getCamera().getScreenCoordinates(selected.getWorldTranslation());
startSelectedCoord = new Vector2f(screen.x, screen.y);
}
if (scaling == null) {
scaling = new ScaleUndo(selected, selected.getLocalScale().clone(), null);
}
float origDist = startMouseCoord.distanceSquared(startSelectedCoord);
float newDist = screenCoord.distanceSquared(startSelectedCoord);
if (origDist == 0) {
origDist = 1;
}
float ratio = newDist / origDist;
Vector3f prev = selected.getLocalScale();
if (axis == Vector3f.UNIT_X) {
selected.setLocalScale(ratio, prev.y, prev.z);
} else if (axis == Vector3f.UNIT_Y) {
selected.setLocalScale(prev.x, ratio, prev.z);
} else if (axis == Vector3f.UNIT_Z) {
selected.setLocalScale(prev.x, prev.y, ratio);
} else {
selected.setLocalScale(ratio, ratio, ratio);
}
}
private void doMouseRotate(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
Spatial selected = toolController.getSelectedSpatial();
if (startMouseCoord == null) {
startMouseCoord = screenCoord.clone();
}
if (startSelectedCoord == null) {
Vector3f screen = getCamera().getScreenCoordinates(selected.getWorldTranslation());
startSelectedCoord = new Vector2f(screen.x, screen.y);
}
if (rotating == null) {
rotating = new RotateUndo(selected, selected.getLocalRotation().clone(), null);
}
Vector2f origRot = startMouseCoord.subtract(startSelectedCoord);
Vector2f newRot = screenCoord.subtract(startSelectedCoord);
float newRotAngle = origRot.angleBetween(newRot);
float temp = newRotAngle;
if (lastRotAngle != 0) {
newRotAngle -= lastRotAngle;
}
lastRotAngle = temp;
Quaternion rotate = new Quaternion();
if (axis != Vector3f.UNIT_XYZ) {
rotate = rotate.fromAngleAxis(newRotAngle, selected.getWorldRotation().inverse().mult(axis));
} else {
rotate = rotate.fromAngleAxis(newRotAngle, selected.getWorldRotation().inverse().mult(getCamera().getDirection().mult(-1).normalizeLocal()));
}
selected.setLocalRotation(selected.getLocalRotation().mult(rotate));
}
private void duplicateSelected() {
Spatial selected = toolController.getSelectedSpatial();
if (selected == null) {
return;
}
Spatial clone = selected.clone();
clone.move(1, 0, 1);
selected.getParent().attachChild(clone);
actionPerformed(new DuplicateUndo(clone, selected.getParent()));
selected = clone;
final Spatial cloned = clone;
final JmeNode rootNode = toolController.getRootNode();
refreshSelected(rootNode, selected.getParent());
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (cloned != null) {
SceneViewerTopComponent.findInstance().setActivatedNodes(new org.openide.nodes.Node[]{rootNode.getChild(cloned)});
SceneExplorerTopComponent.findInstance().setSelectedNode(rootNode.getChild(cloned));
}
}
});
// set to automatically 'grab'/'translate' the new cloned model
toolController.updateSelection(selected);
currentState = State.translate;
currentAxis = Vector3f.UNIT_XYZ;
}
private void deleteSelected() {
Spatial selected = toolController.getSelectedSpatial();
if (selected == null) {
return;
}
Node parent = selected.getParent();
selected.removeFromParent();
actionPerformed(new DeleteUndo(selected, parent));
selected = null;
toolController.updateSelection(selected);
final JmeNode rootNode = toolController.getRootNode();
refreshSelected(rootNode, parent);
}
private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
jmeRootNode.getChild(parent).refresh(false);
}
});
}
private class ScaleUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Vector3f before, after;
ScaleUndo(Spatial spatial, Vector3f before, Vector3f after) {
this.spatial = spatial;
this.before = before;
this.after = after;
}
@Override
public void sceneUndo() {
spatial.setLocalScale(before);
}
@Override
public void sceneRedo() {
spatial.setLocalScale(after);
}
}
private class RotateUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Quaternion before, after;
RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
this.spatial = spatial;
this.before = before;
this.after = after;
}
@Override
public void sceneUndo() {
spatial.setLocalRotation(before);
}
@Override
public void sceneRedo() {
spatial.setLocalRotation(after);
}
}
private class DeleteUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Node parent;
DeleteUndo(Spatial spatial, Node parent) {
this.spatial = spatial;
this.parent = parent;
}
@Override
public void sceneUndo() {
parent.attachChild(spatial);
}
@Override
public void sceneRedo() {
spatial.removeFromParent();
}
}
private class DuplicateUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Node parent;
DuplicateUndo(Spatial spatial, Node parent) {
this.spatial = spatial;
this.parent = parent;
}
@Override
public void sceneUndo() {
spatial.removeFromParent();
}
@Override
public void sceneRedo() {
parent.attachChild(spatial);
}
}
/**
* Check if the selected item is a Terrain
* It will climb up the parent tree to see if
* a parent is terrain too.
* Recursive call.
* Check if the selected item is a Terrain It will climb up the parent tree
* to see if a parent is terrain too. Recursive call.
*/
protected boolean isTerrain(Spatial s) {
if (s == null) {

@ -0,0 +1,123 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.asset.AssetManager;
import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneComposerToolController;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public class DeleteShortcut extends ShortcutTool {
@Override
public boolean isActivableBy(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_X && kie.isPressed()) {
if (Lookup.getDefault().lookup(ShortcutManager.class).isShiftDown()) {
return true;
}
}
return false;
}
@Override
public void cancel() {
terminate();
}
@Override
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
hideMarker();
if (selectedSpatial != null) {
delete();
}
terminate();
}
private void delete() {
Spatial selected = toolController.getSelectedSpatial();
Node parent = selected.getParent();
selected.removeFromParent();
actionPerformed(new DeleteUndo(selected, parent));
selected = null;
toolController.updateSelection(selected);
final JmeNode rootNode = toolController.getRootNode();
refreshSelected(rootNode, parent);
}
private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
jmeRootNode.getChild(parent).refresh(false);
}
});
}
@Override
public void keyPressed(KeyInputEvent kie) {
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
}
@Override
public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
}
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
}
@Override
public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
}
private class DeleteUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Node parent;
DeleteUndo(Spatial spatial, Node parent) {
this.spatial = spatial;
this.parent = parent;
}
@Override
public void sceneUndo() {
parent.attachChild(spatial);
}
@Override
public void sceneRedo() {
spatial.removeFromParent();
}
}
}

@ -0,0 +1,141 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.asset.AssetManager;
import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneComposerToolController;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public class DuplicateShortcut extends ShortcutTool {
@Override
public boolean isActivableBy(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_D && kie.isPressed()) {
if (Lookup.getDefault().lookup(ShortcutManager.class).isShiftDown()) {
return true;
}
}
return false;
}
@Override
public void cancel() {
terminate();
}
@Override
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
hideMarker();
if (selectedSpatial != null) {
duplicate();
terminate();
//then enable move shortcut
toolController.doKeyPressed(new KeyInputEvent(KeyInput.KEY_G, 'g', true, false));
} else {
terminate();
}
}
private void duplicate() {
Spatial selected = toolController.getSelectedSpatial();
Spatial clone = selected.clone();
clone.move(1, 0, 1);
selected.getParent().attachChild(clone);
actionPerformed(new DuplicateUndo(clone, selected.getParent()));
selected = clone;
final Spatial cloned = clone;
final JmeNode rootNode = toolController.getRootNode();
refreshSelected(rootNode, selected.getParent());
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (cloned != null) {
SceneViewerTopComponent.findInstance().setActivatedNodes(new org.openide.nodes.Node[]{rootNode.getChild(cloned)});
SceneExplorerTopComponent.findInstance().setSelectedNode(rootNode.getChild(cloned));
}
}
});
toolController.updateSelection(selected);
}
private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
jmeRootNode.getChild(parent).refresh(false);
}
});
}
@Override
public void keyPressed(KeyInputEvent kie) {
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
}
@Override
public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
}
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
}
@Override
public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
}
private class DuplicateUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Node parent;
DuplicateUndo(Spatial spatial, Node parent) {
this.spatial = spatial;
this.parent = parent;
}
@Override
public void sceneUndo() {
spatial.removeFromParent();
}
@Override
public void sceneRedo() {
parent.attachChild(spatial);
}
}
}

@ -0,0 +1,227 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.asset.AssetManager;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneComposerToolController;
import com.jme3.gde.scenecomposer.tools.PickManager;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public class MoveShortcut extends ShortcutTool {
private Vector3f currentAxis;
private StringBuilder numberBuilder;
private Spatial spatial;
private PickManager pickManager;
private boolean pickEnabled;
private Vector3f startPosition;
private Vector3f finalPosition;
@Override
public boolean isActivableBy(KeyInputEvent kie) {
return kie.getKeyCode() == KeyInput.KEY_G;
}
@Override
public void cancel() {
spatial.setLocalTranslation(startPosition);
terminate();
}
private void apply() {
actionPerformed(new MoveUndo(toolController.getSelectedSpatial(), startPosition, finalPosition));
terminate();
}
private void init(Spatial selectedSpatial) {
spatial = selectedSpatial;
startPosition = spatial.getLocalTranslation().clone();
currentAxis = Vector3f.UNIT_XYZ;
pickManager = Lookup.getDefault().lookup(PickManager.class);
pickEnabled = false;
}
@Override
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
hideMarker();
numberBuilder = new StringBuilder();
if (selectedSpatial == null) {
terminate();
} else {
init(selectedSpatial);
}
}
@Override
public void keyPressed(KeyInputEvent kie) {
if (kie.isPressed()) {
Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
Vector3f axis = new Vector3f();
boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
if (axisChanged) {
currentAxis = axis;
}
boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
boolean enterHit = ShortcutManager.checkEnterHit(kie);
boolean escHit = ShortcutManager.checkEscHit(kie);
if (escHit) {
cancel();
} else if (enterHit) {
apply();
} else if (axisChanged && pickEnabled) {
//update pick manager
if (currentAxis.equals(Vector3f.UNIT_X)) {
pickManager.setTransformation(PickManager.PLANE_XY, getTransformType(), camera);
} else if (currentAxis.equals(Vector3f.UNIT_Y)) {
pickManager.setTransformation(PickManager.PLANE_YZ, getTransformType(), camera);
} else if (currentAxis.equals(Vector3f.UNIT_Z)) {
pickManager.setTransformation(PickManager.PLANE_XZ, getTransformType(), camera);
}
} else if (axisChanged || numberChanged) {
//update transformation
float number = ShortcutManager.getNumberKey(numberBuilder);
Vector3f translation = currentAxis.mult(number);
finalPosition = startPosition.add(translation);
spatial.setLocalTranslation(finalPosition);
}
}
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
apply();
}
}
@Override
public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
cancel();
}
}
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
if (!pickEnabled) {
if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_X)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Y)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Z)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else {
return;
}
}
if (pickManager.updatePick(camera, screenCoord)) {
//pick update success
Vector3f diff;
if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
diff = pickManager.getTranslation();
} else {
diff = pickManager.getTranslation(currentAxis);
}
Vector3f position = startPosition.add(diff);
finalPosition = position;
toolController.getSelectedSpatial().setLocalTranslation(position);
updateToolsTransformation();
}
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
apply();
}
}
@Override
public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
cancel();
}
}
private class MoveUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Vector3f before = new Vector3f(), after = new Vector3f();
MoveUndo(Spatial spatial, Vector3f before, Vector3f after) {
this.spatial = spatial;
this.before.set(before);
if (after != null) {
this.after.set(after);
}
}
@Override
public void sceneUndo() {
spatial.setLocalTranslation(before);
RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
if (control != null) {
control.setPhysicsLocation(spatial.getWorldTranslation());
}
CharacterControl character = spatial.getControl(CharacterControl.class);
if (character != null) {
character.setPhysicsLocation(spatial.getWorldTranslation());
}
// toolController.selectedSpatialTransformed();
}
@Override
public void sceneRedo() {
spatial.setLocalTranslation(after);
RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
if (control != null) {
control.setPhysicsLocation(spatial.getWorldTranslation());
}
CharacterControl character = spatial.getControl(CharacterControl.class);
if (character != null) {
character.setPhysicsLocation(spatial.getWorldTranslation());
}
//toolController.selectedSpatialTransformed();
}
public void setAfter(Vector3f after) {
this.after.set(after);
}
}
}

@ -0,0 +1,189 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.asset.AssetManager;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneComposerToolController;
import com.jme3.gde.scenecomposer.tools.PickManager;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public class RotateShortcut extends ShortcutTool {
private Vector3f currentAxis;
private StringBuilder numberBuilder;
private Spatial spatial;
private PickManager pickManager;
private boolean pickEnabled;
private Quaternion startRotation;
private Quaternion finalRotation;
@Override
public boolean isActivableBy(KeyInputEvent kie) {
return kie.getKeyCode() == KeyInput.KEY_R;
}
@Override
public void cancel() {
spatial.setLocalRotation(startRotation);
terminate();
}
private void apply() {
actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotation, finalRotation));
terminate();
}
private void init(Spatial selectedSpatial) {
spatial = selectedSpatial;
startRotation = spatial.getLocalRotation().clone();
currentAxis = Vector3f.UNIT_XYZ;
pickManager = Lookup.getDefault().lookup(PickManager.class);
pickEnabled = false;
}
@Override
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
hideMarker();
numberBuilder = new StringBuilder();
if (selectedSpatial == null) {
terminate();
} else {
init(selectedSpatial);
}
}
@Override
public void keyPressed(KeyInputEvent kie) {
if (kie.isPressed()) {
Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
Vector3f axis = new Vector3f();
boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
if (axisChanged) {
currentAxis = axis;
}
boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
boolean enterHit = ShortcutManager.checkEnterHit(kie);
boolean escHit = ShortcutManager.checkEscHit(kie);
if (escHit) {
cancel();
} else if (enterHit) {
apply();
} else if (axisChanged && pickEnabled) {
pickEnabled = false;
spatial.setLocalRotation(startRotation.clone());
} else if (axisChanged || numberChanged) {
//update transformation
/* float number = ShortcutManager.getNumberKey(numberBuilder);
Vector3f translation = currentAxis.mult(number);
finalPosition = startPosition.add(translation);
spatial.setLocalTranslation(finalPosition);
*/
}
}
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
apply();
}
}
@Override
public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
cancel();
}
}
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
if (!pickEnabled) {
if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_X)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Y)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Z)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else {
return;
}
}
if (pickManager.updatePick(camera, screenCoord)) {
Quaternion rotation = startRotation.mult(pickManager.getRotation(startRotation.inverse()));
toolController.getSelectedSpatial().setLocalRotation(rotation);
finalRotation = rotation;
updateToolsTransformation();
}
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
apply();
}
}
@Override
public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
cancel();
}
}
private class RotateUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Quaternion before, after;
RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
this.spatial = spatial;
this.before = before;
this.after = after;
}
@Override
public void sceneUndo() {
spatial.setLocalRotation(before);
toolController.selectedSpatialTransformed();
}
@Override
public void sceneRedo() {
spatial.setLocalRotation(after);
toolController.selectedSpatialTransformed();
}
}
}

@ -0,0 +1,199 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.asset.AssetManager;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.scenecomposer.SceneComposerToolController;
import com.jme3.gde.scenecomposer.tools.PickManager;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public class ScaleShortcut extends ShortcutTool {
private Vector3f currentAxis;
private StringBuilder numberBuilder;
private Spatial spatial;
private PickManager pickManager;
private boolean pickEnabled;
private Vector3f startScale;
private Vector3f finalScale;
@Override
public boolean isActivableBy(KeyInputEvent kie) {
return kie.getKeyCode() == KeyInput.KEY_S;
}
@Override
public void cancel() {
spatial.setLocalScale(startScale);
terminate();
}
private void apply() {
actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startScale, finalScale));
terminate();
}
private void init(Spatial selectedSpatial) {
spatial = selectedSpatial;
startScale = spatial.getLocalScale().clone();
currentAxis = Vector3f.UNIT_XYZ;
pickManager = Lookup.getDefault().lookup(PickManager.class);
pickEnabled = false;
}
@Override
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
hideMarker();
numberBuilder = new StringBuilder();
if (selectedSpatial == null) {
terminate();
} else {
init(selectedSpatial);
}
}
@Override
public void keyPressed(KeyInputEvent kie) {
if (kie.isPressed()) {
Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
Vector3f axis = new Vector3f();
boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
if (axisChanged) {
currentAxis = axis;
}
boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
boolean enterHit = ShortcutManager.checkEnterHit(kie);
boolean escHit = ShortcutManager.checkEscHit(kie);
if (escHit) {
cancel();
} else if (enterHit) {
apply();
} else if (axisChanged && pickEnabled) {
pickEnabled = false;
} else if (axisChanged || numberChanged) {
//update transformation
/* float number = ShortcutManager.getNumberKey(numberBuilder);
Vector3f translation = currentAxis.mult(number);
finalPosition = startPosition.add(translation);
spatial.setLocalTranslation(finalPosition);
*/
}
}
}
@Override
public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
apply();
}
}
@Override
public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
if (pressed) {
cancel();
}
}
@Override
public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
if (!pickEnabled) {
if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_X)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Y)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else if (currentAxis.equals(Vector3f.UNIT_Z)) {
pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
pickEnabled = true;
} else {
return;
}
}
if (pickManager.updatePick(camera, screenCoord)) {
Vector3f scale = startScale;
if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
Vector3f constraintAxis = pickManager.getStartOffset().normalize();
float diff = pickManager.getTranslation(constraintAxis).dot(constraintAxis);
diff *= 0.5f;
scale = startScale.add(new Vector3f(diff, diff, diff));
} else {
// Get the translation in the spatial Space
Quaternion worldToSpatial = toolController.getSelectedSpatial().getWorldRotation().inverse();
Vector3f diff = pickManager.getTranslation(worldToSpatial.mult(currentAxis));
diff.multLocal(0.5f);
scale = startScale.add(diff);
}
finalScale = scale;
toolController.getSelectedSpatial().setLocalScale(scale);
updateToolsTransformation();
}
}
@Override
public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
apply();
}
}
@Override
public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
if (pressed) {
cancel();
}
}
private class ScaleUndo extends AbstractUndoableSceneEdit {
private Spatial spatial;
private Vector3f before, after;
ScaleUndo(Spatial spatial, Vector3f before, Vector3f after) {
this.spatial = spatial;
this.before = before;
this.after = after;
}
@Override
public void sceneUndo() {
spatial.setLocalScale(before);
toolController.selectedSpatialTransformed();
}
@Override
public void sceneRedo() {
spatial.setLocalScale(after);
toolController.selectedSpatialTransformed();
}
}
}

@ -0,0 +1,335 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.gde.scenecomposer.SceneEditTool;
import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.math.Vector3f;
import java.util.ArrayList;
import org.openide.util.Lookup;
import org.openide.util.lookup.ServiceProvider;
/**
*
* @author dokthar
*/
@ServiceProvider(service = ShortcutManager.class)
public class ShortcutManager {
private ShortcutTool currentShortcut;
private ArrayList<ShortcutTool> shortcutList;
private boolean ctrlDown = false;
private boolean shiftDown = false;
private boolean altDown = false;
public ShortcutManager() {
shortcutList = new ArrayList<ShortcutTool>();
shortcutList.add(new MoveShortcut());
shortcutList.add(new RotateShortcut());
shortcutList.add(new ScaleShortcut());
shortcutList.add(new DuplicateShortcut());
shortcutList.add(new DeleteShortcut());
}
/*
Methodes
*/
/**
* This MUST be called by the shortcut tool once the modifications are done.
*/
public void terminate() {
currentShortcut = null;
}
/**
*
* @return true if a shortCutTool is active, else return false.
*/
public boolean isActive() {
return currentShortcut != null;
}
/**
* @return the ctrlDown
*/
public boolean isCtrlDown() {
return ctrlDown;
}
/**
* @return the shiftDown
*/
public boolean isShiftDown() {
return shiftDown;
}
/**
* @return the altDown
*/
public boolean isAltDown() {
return altDown;
}
/**
* Set the current shortcut to <code>shortcut</code>. cancel the current
* shortcut if it was still active
*
* @param shortcut the ShortCutTool to set
*/
public void setShortCut(ShortcutTool shortcut) {
if (isActive()) {
currentShortcut.cancel();
}
currentShortcut = shortcut;
}
/**
* Get the shortcut that can be enable with the given kei, the current
* shortcut cannot be enable twice. This also check for command key used to
* provide isCtrlDown(), isShiftDown() and isAltDown().
*
* @param kie the KeyInputEvent
* @return the activable shortcut else return null
*/
public ShortcutTool getActivableShortcut(KeyInputEvent kie) {
if (checkCommandeKey(kie)) {
return null;
}
for (ShortcutTool s : shortcutList) {
if (s != currentShortcut) {
if (s.isActivableBy(kie)) {
return s;
}
}
}
return null;
}
/**
*
* @return the current active shortcut
*/
public ShortcutTool getActiveShortcut() {
return currentShortcut;
}
/**
*
* @param kie the KeyInputEvent
* @return true if the given Kei can enable a sortcut, else false
*/
public boolean canActivateShortcut(KeyInputEvent kie) {
return getActivableShortcut(kie) != null;
}
/**
* Set the current shortcut with the shortcut one that can be enable with
* the given key
*
* @param kie the KeyInputEvent
* @return true is the shortcut changed, else false
*/
public boolean activateShortcut(KeyInputEvent kie) {
ShortcutTool newShortcut = getActivableShortcut(kie);
if (newShortcut != null) {
currentShortcut = newShortcut;
}
return newShortcut != null;
}
/**
* This should be called to trigger the currentShortcut.keyPressed() method.
* This also check for command key used to provide isCtrlDown(),
* isShiftDown() and isAltDown().
*
* @param kie
*/
public void doKeyPressed(KeyInputEvent kie) {
if (checkCommandeKey(kie)) {
//return;
} else if (isActive()) {
currentShortcut.keyPressed(kie);
}
}
private boolean checkCommandeKey(KeyInputEvent kie) {
if (checkCtrlHit(kie)) {
ctrlDown = kie.isPressed();
return true;
} else if (checkAltHit(kie)) {
altDown = kie.isPressed();
return true;
} else if (checkShiftHit(kie)) {
shiftDown = kie.isPressed();
return true;
}
return false;
}
/*
STATIC
*/
/**
*
* @param kie
* @return true if the given kie is KEY_RETURN
*/
public static boolean checkEnterHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_RETURN) {
return true;
}
return false;
}
/**
*
* @param kie
* @return true if the given kie is KEY_ESCAPE
*/
public static boolean checkEscHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_ESCAPE) {
return true;
}
return false;
}
/**
*
* @param kie
* @return true if the given kie is KEY_LCONTROL || KEY_RCONTROL
*/
public static boolean checkCtrlHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_LCONTROL || kie.getKeyCode() == KeyInput.KEY_RCONTROL) {
return true;
}
return false;
}
/**
*
* @param kie
* @return true if the given kie is KEY_LSHIFT || KEY_RSHIFT
*/
public static boolean checkShiftHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_LSHIFT || kie.getKeyCode() == KeyInput.KEY_RSHIFT) {
return true;
}
return false;
}
/**
*
* @param kie
* @return true if the given kie is KEY_LMENU || KEY_RMENU
*/
public static boolean checkAltHit(KeyInputEvent kie) {
if (kie.getKeyCode() == KeyInput.KEY_LMENU || kie.getKeyCode() == KeyInput.KEY_RMENU) {
return true;
}
return false;
}
/**
* store the number kie into the numberBuilder
*
* @param kie
* @param numberBuilder
* @return true if the given kie is handled as a number key event
*/
public static boolean checkNumberKey(KeyInputEvent kie, StringBuilder numberBuilder) {
if (kie.getKeyCode() == KeyInput.KEY_MINUS) {
if (numberBuilder.length() > 0) {
if (numberBuilder.charAt(0) == '-') {
numberBuilder.replace(0, 1, "");
} else {
numberBuilder.insert(0, '-');
}
} else {
numberBuilder.append('-');
}
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_0 || kie.getKeyCode() == KeyInput.KEY_NUMPAD0) {
numberBuilder.append('0');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_1 || kie.getKeyCode() == KeyInput.KEY_NUMPAD1) {
numberBuilder.append('1');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_2 || kie.getKeyCode() == KeyInput.KEY_NUMPAD2) {
numberBuilder.append('2');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_3 || kie.getKeyCode() == KeyInput.KEY_NUMPAD3) {
numberBuilder.append('3');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_4 || kie.getKeyCode() == KeyInput.KEY_NUMPAD4) {
numberBuilder.append('4');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_5 || kie.getKeyCode() == KeyInput.KEY_NUMPAD5) {
numberBuilder.append('5');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_6 || kie.getKeyCode() == KeyInput.KEY_NUMPAD6) {
numberBuilder.append('6');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_7 || kie.getKeyCode() == KeyInput.KEY_NUMPAD7) {
numberBuilder.append('7');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_8 || kie.getKeyCode() == KeyInput.KEY_NUMPAD8) {
numberBuilder.append('8');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_9 || kie.getKeyCode() == KeyInput.KEY_NUMPAD9) {
numberBuilder.append('9');
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_PERIOD) {
if (numberBuilder.indexOf(".") == -1) { // if it doesn't exist yet
if (numberBuilder.length() == 0
|| (numberBuilder.length() == 1 && numberBuilder.charAt(0) == '-')) {
numberBuilder.append("0.");
} else {
numberBuilder.append(".");
}
}
return true;
}
return false;
}
/**
*
* @param numberBuilder the StringBuilder storing the float number
* @return the float value created from the given StringBuilder
*/
public static float getNumberKey(StringBuilder numberBuilder) {
if (numberBuilder.length() == 0) {
return 0;
} else {
return new Float(numberBuilder.toString());
}
}
/**
* Check for axis input for key X,Y,Z and store the corresponding UNIT_ into
* the axisStore
*
* @param kie
* @param axisStore
* @return true if the given kie is handled as a Axis input
*/
public static boolean checkAxisKey(KeyInputEvent kie, Vector3f axisStore) {
if (kie.getKeyCode() == KeyInput.KEY_X) {
axisStore.set(Vector3f.UNIT_X);
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_Y) {
axisStore.set(Vector3f.UNIT_Y);
return true;
} else if (kie.getKeyCode() == KeyInput.KEY_Z) {
axisStore.set(Vector3f.UNIT_Z);
return true;
}
return false;
}
}

@ -0,0 +1,29 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer.tools.shortcuts;
import com.jme3.gde.scenecomposer.SceneEditTool;
import com.jme3.input.event.KeyInputEvent;
import org.openide.util.Lookup;
/**
*
* @author dokthar
*/
public abstract class ShortcutTool extends SceneEditTool {
public abstract boolean isActivableBy(KeyInputEvent kie);
public abstract void cancel();
protected final void terminate() {
Lookup.getDefault().lookup(ShortcutManager.class).terminate();
}
@Override
public abstract void keyPressed(KeyInputEvent kie);
}
Loading…
Cancel
Save