159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.scene.plugins.blender.animations; 259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList; 459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List; 559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map; 659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.Bone; 859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Matrix4f; 959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Quaternion; 1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Transform; 1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector3f; 1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.BlenderContext; 1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.DynamicArray; 1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Structure; 1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.objects.ObjectHelper; 1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/** 1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This class holds the basic data that describes a bone. 2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Marcin Roguski (Kaelthas) 2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class BoneContext { 2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The structure of the bone. */ 2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Structure boneStructure; 2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Bone's pose channel structure. */ 2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Structure poseChannel; 2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Bone's name. */ 2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private String boneName; 3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** This variable indicates if the Y axis should be the UP axis. */ 3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private boolean fixUpAxis; 3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The bone's armature matrix. */ 3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Matrix4f armatureMatrix; 3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The parent context. */ 3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private BoneContext parent; 3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The children of this context. */ 3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private List<BoneContext> children = new ArrayList<BoneContext>(); 3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Created bone (available after calling 'buildBone' method). */ 3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Bone bone; 4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Bone's pose transform (available after calling 'buildBone' method). */ 4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Transform poseTransform = new Transform(); 4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The bone's rest matrix. */ 4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Matrix4f restMatrix; 4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Bone's total inverse transformation. */ 4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Matrix4f inverseTotalTransformation; 4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Bone's parent inverse matrix. */ 4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Matrix4f inverseParentMatrix; 4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Constructor. Creates the basic set of bone's data. 5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param boneStructure 5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the bone's structure 5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param objectToArmatureMatrix 5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * object-to-armature transformation matrix 5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param bonesPoseChannels 5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * a map of pose channels for each bone OMA 5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * an exception is thrown when problem with blender data reading 6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * occurs 6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public BoneContext(Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { 6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this(boneStructure, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext); 6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Constructor. Creates the basic set of bone's data. 7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param boneStructure 7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the bone's structure 7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param parent 7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * bone's parent (null if the bone is the root bone) 7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param objectToArmatureMatrix 7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * object-to-armature transformation matrix 7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param bonesPoseChannels 7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * a map of pose channels for each bone OMA 7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * an exception is thrown when problem with blender data reading 8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * occurs 8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private BoneContext(Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { 8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.parent = parent; 8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.boneStructure = boneStructure; 8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta boneName = boneStructure.getFieldValue("name").toString(); 8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); 9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true); 9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); 9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.computeRestMatrix(objectToArmatureMatrix); 9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext); 9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure child : childbase) { 9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.children.add(new BoneContext(child, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext)); 9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress()); 10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this); 10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method computes the rest matrix for the bone. 10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param objectToArmatureMatrix 10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * object-to-armature transformation matrix 10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private void computeRestMatrix(Matrix4f objectToArmatureMatrix) { 11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (parent != null) { 11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta inverseParentMatrix = parent.inverseTotalTransformation.clone(); 11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (fixUpAxis) { 11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta inverseParentMatrix = objectToArmatureMatrix.clone(); 11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta inverseParentMatrix = Matrix4f.IDENTITY.clone(); 11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta restMatrix = armatureMatrix.clone(); 12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta inverseTotalTransformation = restMatrix.invert(); 12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta restMatrix = inverseParentMatrix.mult(restMatrix); 12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (BoneContext child : this.children) { 12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta child.computeRestMatrix(objectToArmatureMatrix); 12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method computes the pose transform for the bone. 13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private void computePoseTransform() { 13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc"); 13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size"); 13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat"); 13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (fixUpAxis) { 13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue()); 13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue())); 14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue()); 14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue()); 14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue())); 14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue()); 14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); 14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta localTransform.setScale(bone.getLocalScale()); 14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta localTransform.getTranslation().addLocal(poseTransform.getTranslation()); 15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta localTransform.getRotation().multLocal(poseTransform.getRotation()); 15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta localTransform.getScale().multLocal(poseTransform.getScale()); 15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta poseTransform.set(localTransform); 15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method builds the bone. It recursively builds the bone's children. 15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param bones 16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * a list of bones where the newly created bone will be added 16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param boneOMAs 16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the map between bone and its old memory address 16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return newly created bone 16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) { 16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Long boneOMA = boneStructure.getOldMemoryAddress(); 16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bone = new Bone(boneName); 17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bones.add(bone); 17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta boneOMAs.put(bone, boneOMA); 17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone); 17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Matrix4f pose = this.restMatrix.clone(); 17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); 17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Vector3f poseLocation = pose.toTranslationVector(); 17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Quaternion rotation = pose.toRotationQuat(); 17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Vector3f scale = objectHelper.getScale(pose); 18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bone.setBindTransforms(poseLocation, rotation, scale); 18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (BoneContext child : children) { 18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bone.addChild(child.buildBone(bones, boneOMAs, blenderContext)); 18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.computePoseTransform(); 18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return bone; 18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return bone's pose transformation 19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Transform getPoseTransform() { 19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return poseTransform; 19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return built bone (available after calling 'buildBone' method) 20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Bone getBone() { 20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return bone; 20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta} 205