159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.scene.plugins.blender.modifiers; 259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.ByteBuffer; 459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.FloatBuffer; 559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList; 659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap; 759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List; 859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map; 959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level; 1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger; 1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.AnimControl; 1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.Animation; 1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.Bone; 1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.BoneTrack; 1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.Skeleton; 1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.SkeletonControl; 1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Matrix4f; 1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Geometry; 2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Mesh; 2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Node; 2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer; 2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Format; 2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Type; 2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Usage; 2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.BlenderContext; 2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType; 2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.animations.ArmatureHelper; 2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.constraints.Constraint; 3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.FileBlockHeader; 3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Pointer; 3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Structure; 3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.meshes.MeshContext; 3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.objects.ObjectHelper; 3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.ogre.AnimData; 3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.BufferUtils; 3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/** 4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This modifier allows to add bone animation to the object. 4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Marcin Roguski (Kaelthas) 4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/* package */class ArmatureModifier extends Modifier { 4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName()); 4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; 4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight 4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // variable in mesh, 4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // i guess this limitation has no sense for the blender loader...so i guess 5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // it's up to you. You'll have to deternine the max weight according to the 5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // provided blend file 5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // I added a check to avoid crash when loading a model that has more than 4 5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // weight per vertex on line 258 5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // If you decide to remove this limitation, remove this code. 5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Rémy 5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Loaded animation data. */ 5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private AnimData animData; 5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Old memory address of the mesh that will have the skeleton applied. */ 6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Long meshOMA; 6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The maxiumum amount of bone groups applied to a single vertex (max = 6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * MAXIMUM_WEIGHTS_PER_VERTEX). 6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int boneGroups; 6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The weights of vertices. */ 6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private VertexBuffer verticesWeights; 6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The indexes of bones applied to vertices. */ 6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private VertexBuffer verticesWeightsIndices; 7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This constructor reads animation data from the object structore. The 7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * stored data is the AnimData and additional data is armature's OMA. 7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param objectStructure 7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the structure of the object 7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param modifierStructure 7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the structure of the modifier 7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this exception is thrown when the blender file is somehow 8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * corrupted 8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { 8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0); 8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert 8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // = 8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // DeformVERTices 9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // if pDvert==null then there are not vertex groups and no need to load 9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // skeleton (untill bone envelopes are supported) 9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) { 9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object"); 9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (pArmatureObject.isNotNull()) { 9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); 9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0); 9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // load skeleton 10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0); 10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0); 10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext); 10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size()); 10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure poseChannel : chanbase) { 10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pBone = (Pointer) poseChannel.getFieldValue("bone"); 10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel); 11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); 11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true); 11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal(); 11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix); 11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext); 11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Bone> bonesList = new ArrayList<Bone>(); 11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < bonebase.size(); ++i) { 12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext); 12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bonesList.add(0, new Bone("")); 12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]); 12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Skeleton skeleton = new Skeleton(bones); 12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // read mesh indexes 12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.meshOMA = meshStructure.getOldMemoryAddress(); 12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext); 12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // read animations 13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ArrayList<Animation> animations = new ArrayList<Animation>(); 13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); 13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (actionHeaders != null) {// it may happen that the model has 13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // armature with no actions 13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (FileBlockHeader header : actionHeaders) { 13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure actionStructure = header.getStructure(blenderContext); 13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta String actionName = actionStructure.getName(); 13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext); 14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if(tracks != null && tracks.length > 0) { 14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // determining the animation time 14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float maximumTrackLength = 0; 14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (BoneTrack track : tracks) { 14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float length = track.getLength(); 14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (length > maximumTrackLength) { 14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maximumTrackLength = length; 14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Animation boneAnimation = new Animation(actionName, maximumTrackLength); 15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta boneAnimation.setTracks(tracks); 15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta animations.add(boneAnimation); 15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta animData = new AnimData(skeleton, animations); 15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // store the animation data for each bone 15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Bone bone : bones) { 16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Long boneOma = armatureHelper.getBoneOMA(bone); 16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (boneOma != null) { 16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta blenderContext.setAnimData(boneOma, animData); 16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Node apply(Node node, BlenderContext blenderContext) { 17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (invalid) { 17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName()); 17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta }// if invalid, animData will be null 17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (animData == null) { 17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return node; 17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // setting weights for bones 18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE); 18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Geometry geom : geomList) { 18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Mesh mesh = geom.getMesh(); 18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (this.verticesWeights != null) { 18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta mesh.setMaxNumWeights(this.boneGroups); 18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta mesh.setBuffer(this.verticesWeights); 18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta mesh.setBuffer(this.verticesWeightsIndices); 18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // applying constraints to Bones 19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); 19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < animData.skeleton.getBoneCount(); ++i) { 19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Long boneOMA = armatureHelper.getBoneOMA(animData.skeleton.getBone(i)); 19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Constraint> constraints = blenderContext.getConstraints(boneOMA); 19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (constraints != null && constraints.size() > 0) { 19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Constraint constraint : constraints) { 19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta constraint.bake(); 19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // applying animations 20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta AnimControl control = new AnimControl(animData.skeleton); 20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ArrayList<Animation> animList = animData.anims; 20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (animList != null && animList.size() > 0) { 20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size()); 20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < animList.size(); ++i) { 20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Animation animation = animList.get(i); 20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta anims.put(animation.getName(), animation); 21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta control.setAnimations(anims); 21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta node.addControl(control); 21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta node.addControl(new SkeletonControl(animData.skeleton)); 21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return node; 21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method reads mesh indexes 22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param objectStructure 22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * structure of the object that has the armature modifier applied 22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param meshStructure 22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the structure of the object's mesh 22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this exception is thrown when the blend file structure is 23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * somehow invalid or corrupted 23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException { 23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); 23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure defBase = (Structure) objectStructure.getFieldValue("defbase"); 23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext); 23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int[] bonesGroups = new int[] { 0 }; 23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress()); 23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext); 24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.verticesWeights = boneWeightsAndIndex[0]; 24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.verticesWeightsIndices = boneWeightsAndIndex[1]; 24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.boneGroups = bonesGroups[0]; 24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns an array of size 2. The first element is a vertex 24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * buffer holding bone weights for every vertex in the model. The second 24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * element is a vertex buffer holding bone indices for vertices (the indices 25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * of bones the vertices are assigned to). 25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param meshStructure 25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the mesh structure object 25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param vertexListSize 25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * a number of vertices in the model 25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param bonesGroups 25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this is an output parameter, it should be a one-sized array; 25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the maximum amount of weights per vertex (up to 25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * MAXIMUM_WEIGHTS_PER_VERTEX) is stored there 26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param vertexReferenceMap 26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this reference map allows to map the original vertices read 26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * from blender to vertices that are really in the model; one 26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * vertex may appear several times in the result model 26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param groupToBoneIndexMap 26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this object maps the group index (to which a vertices in 26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * blender belong) to bone index of the model 26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return arrays of vertices weights and their bone indices and (as an 27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * output parameter) the maximum amount of weights for a vertex 27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this exception is thrown when the blend file structure is 27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * somehow invalid or corrupted 27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext) 27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throws BlenderFileException { 27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices 27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); 27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); 28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (pDvert.isNotNull()) {// assigning weights and bone indices 28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per 28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // vertex in blender) 28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int vertexIndex = 0; 28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure dvert : dverts) { 28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex 28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // (max. 4 in JME) 28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pDW = (Pointer) dvert.getFieldValue("dw"); 28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here 28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :) 29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int weightIndex = 0; 29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> dw = pDW.fetchData(blenderContext.getInputStream()); 29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure deformWeight : dw) { 29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); 29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Remove this code if 4 weights limitation is removed 29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (weightIndex == 4) { 29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex }); 29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // null here means that we came accross group that has no bone attached to 30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (boneIndex != null) { 30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); 30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (weight == 0.0f) { 30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weight = 1; 30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta boneIndex = Integer.valueOf(0); 30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // we apply the weight to all referenced vertices 30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Integer index : vertexIndices) { 31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); 31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); 31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ++weightIndex; 31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Integer index : vertexIndices) { 31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); 31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); 32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ++vertexIndex; 32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // always bind all vertices to 0-indexed bone 32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // this bone makes the model look normally if vertices have no bone 32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // assigned 32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // and it is used in object animation, so if we come accross object 32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // animation 33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // we can use the 0-indexed bone for this 33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (List<Integer> vertexIndexList : vertexReferenceMap.values()) { 33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // we apply the weight to all referenced vertices 33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Integer index : vertexIndexList) { 33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f); 33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0); 33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData); 34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight); 34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData); 34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex); 34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData); 34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return new VertexBuffer[] { verticesWeights, verticesWeightsIndices }; 34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Normalizes weights if needed and finds largest amount of weights used for 35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * all vertices in the buffer. 35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param vertCount 35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * amount of vertices 35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param weightsFloatData 35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * weights for vertices 35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) { 35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int maxWeightsPerVert = 0; 36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.rewind(); 36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int v = 0; v < vertCount; ++v) { 36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get(); 36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (w3 != 0) { 36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maxWeightsPerVert = Math.max(maxWeightsPerVert, 4); 36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (w2 != 0) { 36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maxWeightsPerVert = Math.max(maxWeightsPerVert, 3); 36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (w1 != 0) { 36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maxWeightsPerVert = Math.max(maxWeightsPerVert, 2); 37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (w0 != 0) { 37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maxWeightsPerVert = Math.max(maxWeightsPerVert, 1); 37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float sum = w0 + w1 + w2 + w3; 37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (sum != 1f && sum != 0.0f) { 37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.position(weightsFloatData.position() - 4); 37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // compute new vals based on sum 37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float sumToB = 1f / sum; 37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(w0 * sumToB); 38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(w1 * sumToB); 38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(w2 * sumToB); 38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.put(w3 * sumToB); 38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta weightsFloatData.rewind(); 38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return maxWeightsPerVert; 38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String getType() { 39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return Modifier.ARMATURE_MODIFIER_DATA; 39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta} 394