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