1 package com.jme3.scene.plugins.blender.constraints; 2 3 import com.jme3.animation.Bone; 4 import com.jme3.math.Matrix4f; 5 import com.jme3.math.Quaternion; 6 import com.jme3.math.Transform; 7 import com.jme3.math.Vector3f; 8 import com.jme3.scene.Spatial; 9 import com.jme3.scene.plugins.blender.BlenderContext; 10 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType; 11 import com.jme3.scene.plugins.blender.constraints.Constraint.Space; 12 import com.jme3.scene.plugins.blender.file.DynamicArray; 13 import com.jme3.scene.plugins.blender.file.Structure; 14 15 /** 16 * This class represents either owner or target of the constraint. It has the 17 * common methods that take the evalueation space of the feature. 18 * 19 * @author Marcin Roguski (Kaelthas) 20 */ 21 /* package */class Feature { 22 /** The evalueation space. */ 23 protected Space space; 24 /** Old memory address of the feature. */ 25 protected Long oma; 26 /** The spatial that is hold by the Feature. */ 27 protected Spatial spatial; 28 /** The bone that is hold by the Feature. */ 29 protected Bone bone; 30 /** The blender context. */ 31 protected BlenderContext blenderContext; 32 33 /** 34 * Constructs the feature. This object should be loaded later 35 * when it is read from the blender file. 36 * The update method should be called before the feature is used. 37 * 38 * @param space 39 * the spatial's evaluation space 40 * @param oma 41 * the spatial's old memory address 42 * @param blenderContext 43 * the blender context 44 */ 45 public Feature(Space space, Long oma, BlenderContext blenderContext) { 46 this.space = space; 47 this.oma = oma; 48 this.blenderContext = blenderContext; 49 } 50 51 /** 52 * Constructs the feature based on spatial. 53 * 54 * @param spatial 55 * the spatial 56 * @param space 57 * the spatial's evaluation space 58 * @param oma 59 * the spatial's old memory address 60 * @param blenderContext 61 * the blender context 62 */ 63 public Feature(Spatial spatial, Space space, Long oma, BlenderContext blenderContext) { 64 this(space, oma, blenderContext); 65 this.blenderContext = blenderContext; 66 } 67 68 /** 69 * Constructs the feature based on bone. 70 * 71 * @param bone 72 * the bone 73 * @param space 74 * the bone evaluation space 75 * @param oma 76 * the bone old memory address 77 * @param blenderContext 78 * the blender context 79 */ 80 public Feature(Bone bone, Space space, Long oma, BlenderContext blenderContext) { 81 this(space, oma, blenderContext); 82 this.bone = bone; 83 } 84 85 /** 86 * This method should be called before the feature is used. 87 * It may happen that the object this feature refers to was not yet loaded from blend file 88 * when the instance of this class was created. 89 */ 90 public void update() { 91 Object owner = blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE); 92 if(owner instanceof Spatial) { 93 this.spatial = (Spatial) owner; 94 } else if(owner instanceof Bone) { 95 this.bone = (Bone) owner; 96 } else { 97 throw new IllegalStateException("Unknown type of owner: " + owner.getClass()); 98 } 99 } 100 101 /** 102 * @return the feature's old memory address 103 */ 104 public Long getOma() { 105 return oma; 106 } 107 108 /** 109 * @return the object held by the feature (either bone or spatial) 110 */ 111 public Object getObject() { 112 if (spatial != null) { 113 return spatial; 114 } 115 return bone; 116 } 117 118 /** 119 * @return the feature's transform depending on the evaluation space 120 */ 121 @SuppressWarnings("unchecked") 122 public Transform getTransform() { 123 if (spatial != null) { 124 switch (space) { 125 case CONSTRAINT_SPACE_LOCAL: 126 Structure targetStructure = (Structure) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_STRUCTURE); 127 128 DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc")); 129 Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue()); 130 DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot")); 131 Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() }); 132 DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size")); 133 Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue()); 134 135 if (blenderContext.getBlenderKey().isFixUpAxis()) { 136 float y = loc.y; 137 loc.y = loc.z; 138 loc.z = -y; 139 140 y = rot.getY(); 141 float z = rot.getZ(); 142 rot.set(rot.getX(), z, -y, rot.getW()); 143 144 y = size.y; 145 size.y = size.z; 146 size.z = y; 147 } 148 149 Transform result = new Transform(loc, rot); 150 result.setScale(size); 151 return result; 152 case CONSTRAINT_SPACE_WORLD: 153 return spatial.getWorldTransform(); 154 default: 155 throw new IllegalStateException("Invalid space type for target object: " + space.toString()); 156 } 157 } 158 // Bone 159 switch (space) { 160 case CONSTRAINT_SPACE_LOCAL: 161 Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); 162 localTransform.setScale(bone.getLocalScale()); 163 return localTransform; 164 case CONSTRAINT_SPACE_WORLD: 165 Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation()); 166 worldTransform.setScale(bone.getWorldBindScale()); 167 return worldTransform; 168 case CONSTRAINT_SPACE_POSE: 169 Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); 170 poseTransform.setScale(bone.getLocalScale()); 171 return poseTransform; 172 case CONSTRAINT_SPACE_PARLOCAL: 173 Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); 174 parentLocalTransform.setScale(bone.getLocalScale()); 175 return parentLocalTransform; 176 default: 177 throw new IllegalStateException("Invalid space type for target object: " + space.toString()); 178 } 179 } 180 181 /** 182 * This method applies the given transform to the feature in the proper 183 * evaluation space. 184 * 185 * @param transform 186 * the transform to be applied 187 */ 188 public void applyTransform(Transform transform) { 189 if (spatial != null) { 190 switch (space) { 191 case CONSTRAINT_SPACE_LOCAL: 192 Transform ownerLocalTransform = spatial.getLocalTransform(); 193 ownerLocalTransform.getTranslation().addLocal(transform.getTranslation()); 194 ownerLocalTransform.getRotation().multLocal(transform.getRotation()); 195 ownerLocalTransform.getScale().multLocal(transform.getScale()); 196 break; 197 case CONSTRAINT_SPACE_WORLD: 198 Matrix4f m = this.getParentWorldTransformMatrix(); 199 m.invertLocal(); 200 Matrix4f matrix = this.toMatrix(transform); 201 m.multLocal(matrix); 202 203 float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20); 204 float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21); 205 float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22); 206 207 transform.setTranslation(m.toTranslationVector()); 208 transform.setRotation(m.toRotationQuat()); 209 transform.setScale(scaleX, scaleY, scaleZ); 210 spatial.setLocalTransform(transform); 211 break; 212 case CONSTRAINT_SPACE_PARLOCAL: 213 case CONSTRAINT_SPACE_POSE: 214 throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object."); 215 default: 216 throw new IllegalStateException("Invalid space type for target object: " + space.toString()); 217 } 218 } else {// Bone 219 switch (space) { 220 case CONSTRAINT_SPACE_LOCAL: 221 bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); 222 break; 223 case CONSTRAINT_SPACE_WORLD: 224 Matrix4f m = this.getParentWorldTransformMatrix(); 225 // m.invertLocal(); 226 transform.setTranslation(m.mult(transform.getTranslation())); 227 transform.setRotation(m.mult(transform.getRotation(), null)); 228 transform.setScale(transform.getScale()); 229 bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); 230 // float x = FastMath.HALF_PI/2; 231 // float y = -FastMath.HALF_PI; 232 // float z = -FastMath.HALF_PI/2; 233 // bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1)); 234 break; 235 case CONSTRAINT_SPACE_PARLOCAL: 236 Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation()); 237 Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation()); 238 bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale()); 239 break; 240 case CONSTRAINT_SPACE_POSE: 241 bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); 242 break; 243 default: 244 throw new IllegalStateException("Invalid space type for target object: " + space.toString()); 245 } 246 } 247 } 248 249 /** 250 * @return world transform matrix of the feature 251 */ 252 public Matrix4f getWorldTransformMatrix() { 253 if (spatial != null) { 254 Matrix4f result = new Matrix4f(); 255 Transform t = spatial.getWorldTransform(); 256 result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix()); 257 return result; 258 } 259 // Bone 260 Matrix4f result = new Matrix4f(); 261 result.setTransform(bone.getWorldBindPosition(), bone.getWorldBindScale(), bone.getWorldBindRotation().toRotationMatrix()); 262 return result; 263 } 264 265 /** 266 * @return world transform matrix of the feature's parent or identity matrix 267 * if the feature has no parent 268 */ 269 public Matrix4f getParentWorldTransformMatrix() { 270 Matrix4f result = new Matrix4f(); 271 if (spatial != null) { 272 if (spatial.getParent() != null) { 273 Transform t = spatial.getParent().getWorldTransform(); 274 result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix()); 275 } 276 } else {// Bone 277 Bone parent = bone.getParent(); 278 if (parent != null) { 279 result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix()); 280 } 281 } 282 return result; 283 } 284 285 /** 286 * Converts given transform to the matrix. 287 * 288 * @param transform 289 * the transform to be converted 290 * @return 4x4 matri that represents the given transform 291 */ 292 protected Matrix4f toMatrix(Transform transform) { 293 Matrix4f result = Matrix4f.IDENTITY; 294 if (transform != null) { 295 result = new Matrix4f(); 296 result.setTranslation(transform.getTranslation()); 297 result.setRotationQuaternion(transform.getRotation()); 298 result.setScale(transform.getScale()); 299 } 300 return result; 301 } 302 } 303