package com.jme3.scene.plugins.blender.constraints; import com.jme3.animation.Bone; import com.jme3.math.Matrix4f; import com.jme3.math.Quaternion; import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.Spatial; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType; import com.jme3.scene.plugins.blender.constraints.Constraint.Space; import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.Structure; /** * This class represents either owner or target of the constraint. It has the * common methods that take the evalueation space of the feature. * * @author Marcin Roguski (Kaelthas) */ /* package */class Feature { /** The evalueation space. */ protected Space space; /** Old memory address of the feature. */ protected Long oma; /** The spatial that is hold by the Feature. */ protected Spatial spatial; /** The bone that is hold by the Feature. */ protected Bone bone; /** The blender context. */ protected BlenderContext blenderContext; /** * Constructs the feature. This object should be loaded later * when it is read from the blender file. * The update method should be called before the feature is used. * * @param space * the spatial's evaluation space * @param oma * the spatial's old memory address * @param blenderContext * the blender context */ public Feature(Space space, Long oma, BlenderContext blenderContext) { this.space = space; this.oma = oma; this.blenderContext = blenderContext; } /** * Constructs the feature based on spatial. * * @param spatial * the spatial * @param space * the spatial's evaluation space * @param oma * the spatial's old memory address * @param blenderContext * the blender context */ public Feature(Spatial spatial, Space space, Long oma, BlenderContext blenderContext) { this(space, oma, blenderContext); this.blenderContext = blenderContext; } /** * Constructs the feature based on bone. * * @param bone * the bone * @param space * the bone evaluation space * @param oma * the bone old memory address * @param blenderContext * the blender context */ public Feature(Bone bone, Space space, Long oma, BlenderContext blenderContext) { this(space, oma, blenderContext); this.bone = bone; } /** * This method should be called before the feature is used. * It may happen that the object this feature refers to was not yet loaded from blend file * when the instance of this class was created. */ public void update() { Object owner = blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE); if(owner instanceof Spatial) { this.spatial = (Spatial) owner; } else if(owner instanceof Bone) { this.bone = (Bone) owner; } else { throw new IllegalStateException("Unknown type of owner: " + owner.getClass()); } } /** * @return the feature's old memory address */ public Long getOma() { return oma; } /** * @return the object held by the feature (either bone or spatial) */ public Object getObject() { if (spatial != null) { return spatial; } return bone; } /** * @return the feature's transform depending on the evaluation space */ @SuppressWarnings("unchecked") public Transform getTransform() { if (spatial != null) { switch (space) { case CONSTRAINT_SPACE_LOCAL: Structure targetStructure = (Structure) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_STRUCTURE); DynamicArray locArray = ((DynamicArray) targetStructure.getFieldValue("loc")); Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue()); DynamicArray rotArray = ((DynamicArray) targetStructure.getFieldValue("rot")); Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() }); DynamicArray sizeArray = ((DynamicArray) targetStructure.getFieldValue("size")); Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue()); if (blenderContext.getBlenderKey().isFixUpAxis()) { float y = loc.y; loc.y = loc.z; loc.z = -y; y = rot.getY(); float z = rot.getZ(); rot.set(rot.getX(), z, -y, rot.getW()); y = size.y; size.y = size.z; size.z = y; } Transform result = new Transform(loc, rot); result.setScale(size); return result; case CONSTRAINT_SPACE_WORLD: return spatial.getWorldTransform(); default: throw new IllegalStateException("Invalid space type for target object: " + space.toString()); } } // Bone switch (space) { case CONSTRAINT_SPACE_LOCAL: Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); localTransform.setScale(bone.getLocalScale()); return localTransform; case CONSTRAINT_SPACE_WORLD: Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation()); worldTransform.setScale(bone.getWorldBindScale()); return worldTransform; case CONSTRAINT_SPACE_POSE: Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); poseTransform.setScale(bone.getLocalScale()); return poseTransform; case CONSTRAINT_SPACE_PARLOCAL: Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); parentLocalTransform.setScale(bone.getLocalScale()); return parentLocalTransform; default: throw new IllegalStateException("Invalid space type for target object: " + space.toString()); } } /** * This method applies the given transform to the feature in the proper * evaluation space. * * @param transform * the transform to be applied */ public void applyTransform(Transform transform) { if (spatial != null) { switch (space) { case CONSTRAINT_SPACE_LOCAL: Transform ownerLocalTransform = spatial.getLocalTransform(); ownerLocalTransform.getTranslation().addLocal(transform.getTranslation()); ownerLocalTransform.getRotation().multLocal(transform.getRotation()); ownerLocalTransform.getScale().multLocal(transform.getScale()); break; case CONSTRAINT_SPACE_WORLD: Matrix4f m = this.getParentWorldTransformMatrix(); m.invertLocal(); Matrix4f matrix = this.toMatrix(transform); m.multLocal(matrix); float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20); float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21); float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22); transform.setTranslation(m.toTranslationVector()); transform.setRotation(m.toRotationQuat()); transform.setScale(scaleX, scaleY, scaleZ); spatial.setLocalTransform(transform); break; case CONSTRAINT_SPACE_PARLOCAL: case CONSTRAINT_SPACE_POSE: throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object."); default: throw new IllegalStateException("Invalid space type for target object: " + space.toString()); } } else {// Bone switch (space) { case CONSTRAINT_SPACE_LOCAL: bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); break; case CONSTRAINT_SPACE_WORLD: Matrix4f m = this.getParentWorldTransformMatrix(); // m.invertLocal(); transform.setTranslation(m.mult(transform.getTranslation())); transform.setRotation(m.mult(transform.getRotation(), null)); transform.setScale(transform.getScale()); bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); // float x = FastMath.HALF_PI/2; // float y = -FastMath.HALF_PI; // float z = -FastMath.HALF_PI/2; // bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1)); break; case CONSTRAINT_SPACE_PARLOCAL: Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation()); Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation()); bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale()); break; case CONSTRAINT_SPACE_POSE: bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); break; default: throw new IllegalStateException("Invalid space type for target object: " + space.toString()); } } } /** * @return world transform matrix of the feature */ public Matrix4f getWorldTransformMatrix() { if (spatial != null) { Matrix4f result = new Matrix4f(); Transform t = spatial.getWorldTransform(); result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix()); return result; } // Bone Matrix4f result = new Matrix4f(); result.setTransform(bone.getWorldBindPosition(), bone.getWorldBindScale(), bone.getWorldBindRotation().toRotationMatrix()); return result; } /** * @return world transform matrix of the feature's parent or identity matrix * if the feature has no parent */ public Matrix4f getParentWorldTransformMatrix() { Matrix4f result = new Matrix4f(); if (spatial != null) { if (spatial.getParent() != null) { Transform t = spatial.getParent().getWorldTransform(); result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix()); } } else {// Bone Bone parent = bone.getParent(); if (parent != null) { result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix()); } } return result; } /** * Converts given transform to the matrix. * * @param transform * the transform to be converted * @return 4x4 matri that represents the given transform */ protected Matrix4f toMatrix(Transform transform) { Matrix4f result = Matrix4f.IDENTITY; if (transform != null) { result = new Matrix4f(); result.setTranslation(transform.getTranslation()); result.setRotationQuaternion(transform.getRotation()); result.setScale(transform.getScale()); } return result; } }