Armature loading refactoring and bugfixing.
- animations should now load properly - armature loading moved to ArmatureModifier (no bones are touched inside MeshHelper so the code should be a little more clear now) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8238 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
64c61c86e4
commit
3402f51824
@ -49,6 +49,7 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
|||||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||||
|
import com.jme3.scene.plugins.blender.meshes.MeshHelper.VertexData;
|
||||||
import com.jme3.scene.plugins.blender.modifiers.Modifier;
|
import com.jme3.scene.plugins.blender.modifiers.Modifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,6 +90,8 @@ public class DataRepository {
|
|||||||
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
|
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
|
||||||
/** A list of constraints for the specified object. */
|
/** A list of constraints for the specified object. */
|
||||||
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
|
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
|
||||||
|
/** Vertex data for a mesh specified by OMA. */
|
||||||
|
protected Map<Long, VertexData> vertexData = new HashMap<Long, VertexData>();
|
||||||
/** A map of material contexts. */
|
/** A map of material contexts. */
|
||||||
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
|
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
|
||||||
/** A map og helpers that perform loading. */
|
/** A map og helpers that perform loading. */
|
||||||
@ -398,6 +401,29 @@ public class DataRepository {
|
|||||||
return constraints.get(objectOMA);
|
return constraints.get(objectOMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the vertex data for the specified mesh. Attention!!! If
|
||||||
|
* vertex data is already set it will be overwritten.
|
||||||
|
* @param meshOMA
|
||||||
|
* old memeory address of mesh
|
||||||
|
* @param vertexData
|
||||||
|
* mesh's vertex data
|
||||||
|
*/
|
||||||
|
public void setVertexData(Long meshOMA, VertexData vertexData) {
|
||||||
|
this.vertexData.put(meshOMA, vertexData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns vertex data for a mesh with the specified old memory
|
||||||
|
* address. If no data is registered then null is returned.
|
||||||
|
* @param meshOMA
|
||||||
|
* old memeory address of mesh
|
||||||
|
* @return mesh's vertex data
|
||||||
|
*/
|
||||||
|
public VertexData getVertexData(Long meshOMA) {
|
||||||
|
return vertexData.get(meshOMA);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method sets the material context for the given material.
|
* This method sets the material context for the given material.
|
||||||
* If the context is already set it will be replaced.
|
* If the context is already set it will be replaced.
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene.plugins.blender.animations;
|
package com.jme3.scene.plugins.blender.animations;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -41,12 +40,8 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import com.jme3.animation.Bone;
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.animation.BoneTrack;
|
import com.jme3.animation.BoneTrack;
|
||||||
import com.jme3.animation.Skeleton;
|
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.Mesh;
|
|
||||||
import com.jme3.scene.VertexBuffer;
|
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
|
||||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository;
|
import com.jme3.scene.plugins.blender.DataRepository;
|
||||||
import com.jme3.scene.plugins.blender.curves.BezierCurve;
|
import com.jme3.scene.plugins.blender.curves.BezierCurve;
|
||||||
@ -100,27 +95,6 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method reads the bones and returns an empty skeleton. Bones should be assigned later.
|
|
||||||
* @param structure
|
|
||||||
* armature structure
|
|
||||||
* @param dataRepository
|
|
||||||
* the data repository
|
|
||||||
* @return an empty skeleton, bones are stored within the helper object
|
|
||||||
* @throws BlenderFileException
|
|
||||||
* this exception is thrown when the blender file is somehow corrupted
|
|
||||||
*/
|
|
||||||
public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException {
|
|
||||||
LOGGER.log(Level.INFO, "Converting structure to Armature!");
|
|
||||||
Structure bonebase = (Structure) structure.getFieldValue("bonebase");
|
|
||||||
List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);
|
|
||||||
for (Structure boneStructure : bonesStructures) {
|
|
||||||
BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository);
|
|
||||||
boneDataRoots.add(rootBoneTransformationData);
|
|
||||||
}
|
|
||||||
return new Skeleton();//bones are assigned later
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns a map where the key is the object's group index that is used by a bone and the key is the
|
* This method returns a map where the key is the object's group index that is used by a bone and the key is the
|
||||||
* bone index in the armature.
|
* bone index in the armature.
|
||||||
@ -135,7 +109,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
if (bonesMap != null && bonesMap.size() != 0) {
|
if (bonesMap != null && bonesMap.size() != 0) {
|
||||||
result = new HashMap<Integer, Integer>();
|
result = new HashMap<Integer, Integer>();
|
||||||
List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup
|
List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup
|
||||||
int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!!
|
int groupIndex = 0;
|
||||||
for (Structure deformGroup : deformGroups) {
|
for (Structure deformGroup : deformGroups) {
|
||||||
String deformGroupName = deformGroup.getFieldValue("name").toString();
|
String deformGroupName = deformGroup.getFieldValue("name").toString();
|
||||||
Integer boneIndex = bonesMap.get(deformGroupName);
|
Integer boneIndex = bonesMap.get(deformGroupName);
|
||||||
@ -179,7 +153,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
* this exception is thrown when the blender file is somehow corrupted
|
* this exception is thrown when the blender file is somehow corrupted
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {
|
public BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {
|
||||||
String name = boneStructure.getFieldValue("name").toString();
|
String name = boneStructure.getFieldValue("name").toString();
|
||||||
Bone bone = new Bone(name);
|
Bone bone = new Bone(name);
|
||||||
int bonesAmount = bonesOMAs.size();
|
int bonesAmount = bonesOMAs.size();
|
||||||
@ -230,6 +204,10 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addBoneDataRoot(BoneTransformationData dataRoot) {
|
||||||
|
this.boneDataRoots.add(dataRoot);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns bone transformation data for the bone of a given index.
|
* This method returns bone transformation data for the bone of a given index.
|
||||||
* @param index
|
* @param index
|
||||||
@ -252,7 +230,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
* This class holds the data needed later for bone transformation calculation and to bind parent with children.
|
* This class holds the data needed later for bone transformation calculation and to bind parent with children.
|
||||||
* @author Marcin Roguski
|
* @author Marcin Roguski
|
||||||
*/
|
*/
|
||||||
private static class BoneTransformationData {
|
public static class BoneTransformationData {
|
||||||
|
|
||||||
/** Inverse matrix of bone's parent bone. */
|
/** Inverse matrix of bone's parent bone. */
|
||||||
private Matrix4f totalInverseBoneParentMatrix;
|
private Matrix4f totalInverseBoneParentMatrix;
|
||||||
@ -299,7 +277,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
* additional bone transformation which indicates it's mesh parent and armature object transformations
|
* additional bone transformation which indicates it's mesh parent and armature object transformations
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: consider many skeletons ???
|
public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {
|
||||||
List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);
|
List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);
|
||||||
bones.add(new Bone(""));
|
bones.add(new Bone(""));
|
||||||
for (BoneTransformationData btd : boneDataRoots) {
|
for (BoneTransformationData btd : boneDataRoots) {
|
||||||
@ -308,25 +286,6 @@ public class ArmatureHelper extends AbstractBlenderHelper {
|
|||||||
return bones.toArray(new Bone[bones.size()]);
|
return bones.toArray(new Bone[bones.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the
|
|
||||||
* value -1.
|
|
||||||
* @param immovableBoneIndex
|
|
||||||
* the ondex of immovable bone
|
|
||||||
* @param meshes
|
|
||||||
* a list of meshes whose vertices will be assigned to immovable bone
|
|
||||||
*/
|
|
||||||
public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) {
|
|
||||||
//bone indices are common for all the object's meshes (vertex indices specify which are to be used)
|
|
||||||
VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes
|
|
||||||
ByteBuffer data = (ByteBuffer) boneIndices.getData();
|
|
||||||
for (int i = 0; i < boneIndices.getNumElements(); ++i) {
|
|
||||||
if (data.get(i) == -1) {
|
|
||||||
data.put(i, immovableBoneIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearState() {
|
public void clearState() {
|
||||||
bonesMap.clear();
|
bonesMap.clear();
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene.plugins.blender.meshes;
|
package com.jme3.scene.plugins.blender.meshes;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -41,9 +40,6 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||||
import com.jme3.bounding.BoundingBox;
|
|
||||||
import com.jme3.bounding.BoundingSphere;
|
|
||||||
import com.jme3.bounding.BoundingVolume;
|
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
@ -58,7 +54,6 @@ import com.jme3.scene.VertexBuffer.Usage;
|
|||||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository;
|
import com.jme3.scene.plugins.blender.DataRepository;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
||||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
|
||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
@ -77,8 +72,6 @@ import com.jme3.util.BufferUtils;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public class MeshHelper extends AbstractBlenderHelper {
|
public class MeshHelper extends AbstractBlenderHelper {
|
||||||
protected static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
|
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
|
||||||
* versions.
|
* versions.
|
||||||
@ -250,6 +243,8 @@ public class MeshHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dataRepository.setVertexData(structure.getOldMemoryAddress(), new VertexData(vertexList, vertexReferenceMap));
|
||||||
|
|
||||||
Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
|
Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
|
||||||
|
|
||||||
// reading vertices groups (from the parent)
|
// reading vertices groups (from the parent)
|
||||||
@ -262,18 +257,6 @@ public class MeshHelper extends AbstractBlenderHelper {
|
|||||||
verticesGroups[defIndex++] = def.getFieldValue("name").toString();
|
verticesGroups[defIndex++] = def.getFieldValue("name").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// vertices bone weights and indices
|
|
||||||
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
|
|
||||||
Structure defBase = (Structure) parent.getFieldValue("defbase");
|
|
||||||
Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);
|
|
||||||
|
|
||||||
VertexBuffer verticesWeights = null, verticesWeightsIndices = null;
|
|
||||||
int[] bonesGroups = new int[] { 0 };
|
|
||||||
VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups,
|
|
||||||
vertexReferenceMap, groupToBoneIndexMap, dataRepository);
|
|
||||||
verticesWeights = boneWeightsAndIndex[0];
|
|
||||||
verticesWeightsIndices = boneWeightsAndIndex[1];
|
|
||||||
|
|
||||||
// reading materials
|
// reading materials
|
||||||
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
||||||
Material[] materials = null;
|
Material[] materials = null;
|
||||||
@ -333,13 +316,6 @@ public class MeshHelper extends AbstractBlenderHelper {
|
|||||||
mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
|
mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// setting weights for bones
|
|
||||||
if (verticesWeights != null) {
|
|
||||||
mesh.setMaxNumWeights(bonesGroups[0]);
|
|
||||||
mesh.setBuffer(verticesWeights);
|
|
||||||
mesh.setBuffer(verticesWeightsIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// setting faces' normals
|
// setting faces' normals
|
||||||
mesh.setBuffer(normalsBuffer);
|
mesh.setBuffer(normalsBuffer);
|
||||||
mesh.setBuffer(normalsBind);
|
mesh.setBuffer(normalsBind);
|
||||||
@ -418,22 +394,6 @@ public class MeshHelper extends AbstractBlenderHelper {
|
|||||||
return geometries;
|
return geometries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns bounding shpere of a given mesh.
|
|
||||||
* @param mesh the mesh to read the bounding sphere from
|
|
||||||
* @return the bounding sphere of the given mesh
|
|
||||||
*/
|
|
||||||
protected BoundingSphere getBoundingSphere(Mesh mesh) {
|
|
||||||
BoundingVolume bv = mesh.getBound();
|
|
||||||
if(bv instanceof BoundingSphere) {
|
|
||||||
return (BoundingSphere)bv;
|
|
||||||
} else if(bv instanceof BoundingBox) {
|
|
||||||
BoundingBox bb = (BoundingBox)bv;
|
|
||||||
return new BoundingSphere(bb.getCenter().subtract(bb.getMin(null)).length(), bb.getCenter());
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
* This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
||||||
*
|
*
|
||||||
@ -535,136 +495,26 @@ public class MeshHelper extends AbstractBlenderHelper {
|
|||||||
return vertices;
|
return vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The
|
|
||||||
* second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).
|
|
||||||
*
|
|
||||||
* @param meshStructure
|
|
||||||
* the mesh structure object
|
|
||||||
* @param vertexListSize
|
|
||||||
* a number of vertices in the model
|
|
||||||
* @param bonesGroups
|
|
||||||
* this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to
|
|
||||||
* MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
|
|
||||||
* @param vertexReferenceMap
|
|
||||||
* this reference map allows to map the original vertices read from blender to vertices that are really in the model; one
|
|
||||||
* vertex may appear several times in the result model
|
|
||||||
* @param groupToBoneIndexMap
|
|
||||||
* this object maps the group index (to which a vertices in blender belong) to bone index of the model
|
|
||||||
* @param dataRepository
|
|
||||||
* the data repository
|
|
||||||
* @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex
|
|
||||||
* @throws BlenderFileException
|
|
||||||
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
|
||||||
*/
|
|
||||||
public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,
|
|
||||||
Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)
|
|
||||||
throws BlenderFileException {
|
|
||||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
|
||||||
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
|
||||||
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
|
||||||
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
|
||||||
List<Structure> dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per
|
|
||||||
// vertex in blender)
|
|
||||||
int vertexIndex = 0;
|
|
||||||
for (Structure dvert : dverts) {
|
|
||||||
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex
|
|
||||||
// (max. 4 in JME)
|
|
||||||
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
|
||||||
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
|
|
||||||
if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
|
|
||||||
int weightIndex = 0;
|
|
||||||
List<Structure> dw = pDW.fetchData(dataRepository.getInputStream());
|
|
||||||
for (Structure deformWeight : dw) {
|
|
||||||
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
|
|
||||||
if (boneIndex != null) {// null here means that we came accross group that has no bone attached to
|
|
||||||
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
|
||||||
if (weight == 0.0f) {
|
|
||||||
weight = 1;
|
|
||||||
boneIndex = Integer.valueOf(0);
|
|
||||||
}
|
|
||||||
// we apply the weight to all referenced vertices
|
|
||||||
for (Integer index : vertexIndices) {
|
|
||||||
// all indices are always assigned to 0-indexed bone
|
|
||||||
// weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f);
|
|
||||||
// indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0);
|
|
||||||
// if(weight != 0.0f) {
|
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
|
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++weightIndex;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (Integer index : vertexIndices) {
|
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
|
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++vertexIndex;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// always bind all vertices to 0-indexed bone
|
|
||||||
// this bone makes the model look normally if vertices have no bone assigned
|
|
||||||
// and it is used in object animation, so if we come accross object animation
|
|
||||||
// we can use the 0-indexed bone for this
|
|
||||||
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
|
|
||||||
// we apply the weight to all referenced vertices
|
|
||||||
for (Integer index : vertexIndexList) {
|
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
|
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
|
|
||||||
VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
|
|
||||||
verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
|
|
||||||
|
|
||||||
VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
|
|
||||||
verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
|
|
||||||
return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.
|
|
||||||
*/
|
|
||||||
protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
|
|
||||||
int maxWeightsPerVert = 0;
|
|
||||||
weightsFloatData.rewind();
|
|
||||||
for (int v = 0; v < vertCount; ++v) {
|
|
||||||
float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
|
|
||||||
|
|
||||||
if (w3 != 0) {
|
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
|
|
||||||
} else if (w2 != 0) {
|
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
|
|
||||||
} else if (w1 != 0) {
|
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
|
|
||||||
} else if (w0 != 0) {
|
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
float sum = w0 + w1 + w2 + w3;
|
|
||||||
if (sum != 1f && sum != 0.0f) {
|
|
||||||
weightsFloatData.position(weightsFloatData.position() - 4);
|
|
||||||
// compute new vals based on sum
|
|
||||||
float sumToB = 1f / sum;
|
|
||||||
weightsFloatData.put(w0 * sumToB);
|
|
||||||
weightsFloatData.put(w1 * sumToB);
|
|
||||||
weightsFloatData.put(w2 * sumToB);
|
|
||||||
weightsFloatData.put(w3 * sumToB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
weightsFloatData.rewind();
|
|
||||||
|
|
||||||
// mesh.setMaxNumWeights(maxWeightsPerVert);
|
|
||||||
return maxWeightsPerVert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class VertexData {
|
||||||
|
private List<Vector3f> vertexList;
|
||||||
|
private Map<Integer, List<Integer>> vertexReferenceMap;
|
||||||
|
|
||||||
|
public VertexData(List<Vector3f> vertexList, Map<Integer, List<Integer>> vertexReferenceMap) {
|
||||||
|
this.vertexList = vertexList;
|
||||||
|
this.vertexReferenceMap = vertexReferenceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Vector3f> getVertexList() {
|
||||||
|
return vertexList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, List<Integer>> getVertexReferenceMap() {
|
||||||
|
return vertexReferenceMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package com.jme3.scene.plugins.blender.modifiers;
|
package com.jme3.scene.plugins.blender.modifiers;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.jme3.animation.AnimControl;
|
import com.jme3.animation.AnimControl;
|
||||||
import com.jme3.animation.Animation;
|
import com.jme3.animation.Animation;
|
||||||
import com.jme3.animation.Bone;
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.animation.BoneAnimation;
|
import com.jme3.animation.BoneAnimation;
|
||||||
import com.jme3.animation.BoneTrack;
|
|
||||||
import com.jme3.animation.Skeleton;
|
import com.jme3.animation.Skeleton;
|
||||||
import com.jme3.animation.SkeletonControl;
|
import com.jme3.animation.SkeletonControl;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
@ -18,16 +19,23 @@ import com.jme3.scene.Geometry;
|
|||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.scene.VertexBuffer.Format;
|
||||||
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
|
import com.jme3.scene.VertexBuffer.Usage;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository;
|
import com.jme3.scene.plugins.blender.DataRepository;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
||||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.ArmatureHelper.BoneTransformationData;
|
||||||
import com.jme3.scene.plugins.blender.constraints.Constraint;
|
import com.jme3.scene.plugins.blender.constraints.Constraint;
|
||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
import com.jme3.scene.plugins.blender.meshes.MeshHelper.VertexData;
|
||||||
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||||
import com.jme3.scene.plugins.ogre.AnimData;
|
import com.jme3.scene.plugins.ogre.AnimData;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This modifier allows to add bone animation to the object.
|
* This modifier allows to add bone animation to the object.
|
||||||
@ -35,6 +43,18 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ArmatureModifier extends Modifier {
|
/* package */class ArmatureModifier extends Modifier {
|
||||||
|
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ?
|
||||||
|
|
||||||
|
/** Old memory address of the armature's object. */
|
||||||
|
private Long armatureObjectOMA;
|
||||||
|
/** Old memory address of the mesh that will have the skeleton applied. */
|
||||||
|
private Long meshOMA;
|
||||||
|
/** The maxiumum amount of bone groups applied to a single vertex (max = MAXIMUM_WEIGHTS_PER_VERTEX). */
|
||||||
|
private int boneGroups;
|
||||||
|
/** The weights of vertices. */
|
||||||
|
private VertexBuffer verticesWeights;
|
||||||
|
/** The indexes of bones applied to vertices. */
|
||||||
|
private VertexBuffer verticesWeightsIndices;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor is only temporary. It will be removed when object
|
* This constructor is only temporary. It will be removed when object
|
||||||
@ -61,29 +81,31 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
|
Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
|
||||||
if (pArmatureObject.isNotNull()) {
|
if (pArmatureObject.isNotNull()) {
|
||||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||||
Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(),
|
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
|
||||||
LoadedFeatureDataType.LOADED_STRUCTURE);
|
|
||||||
if (armatureObject == null) {// we check this first not to fetch the
|
|
||||||
// structure unnecessary
|
|
||||||
armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);
|
|
||||||
objectHelper.toObject(armatureObject, dataRepository);
|
|
||||||
}
|
|
||||||
additionalData = armatureObject.getOldMemoryAddress();
|
|
||||||
ArmatureHelper armatureHelper = dataRepository
|
|
||||||
.getHelper(ArmatureHelper.class);
|
|
||||||
|
|
||||||
// changing bones matrices so that they fit the current object (that
|
Structure armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);
|
||||||
// is why we need a copy of a skeleton)
|
this.armatureObjectOMA = armatureObject.getOldMemoryAddress();
|
||||||
|
|
||||||
|
//read skeleton
|
||||||
|
// changing bones matrices so that they fit the current object
|
||||||
|
Structure armatureStructure = ((Pointer)armatureObject.getFieldValue("data")).fetchData(dataRepository.getInputStream()).get(0);
|
||||||
|
Structure bonebase = (Structure) armatureStructure.getFieldValue("bonebase");
|
||||||
|
List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);
|
||||||
|
for (Structure boneStructure : bonesStructures) {
|
||||||
|
BoneTransformationData rootBoneTransformationData = armatureHelper.readBoneAndItsChildren(boneStructure, null, dataRepository);
|
||||||
|
armatureHelper.addBoneDataRoot(rootBoneTransformationData);
|
||||||
|
}
|
||||||
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
|
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
|
||||||
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
|
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
|
||||||
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
|
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
|
||||||
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
|
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
|
||||||
|
|
||||||
// setting the bones structure inside the skeleton (thus completing
|
//read mesh indexes
|
||||||
// its loading)
|
Structure meshStructure = ((Pointer)objectStructure.getFieldValue("data")).fetchData(dataRepository.getInputStream()).get(0);
|
||||||
Skeleton skeleton = new Skeleton(bones);
|
this.meshOMA = meshStructure.getOldMemoryAddress();
|
||||||
dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton);
|
this.readVerticesWeightsData(objectStructure, meshStructure, dataRepository);
|
||||||
|
|
||||||
|
//read animations
|
||||||
String objectName = objectStructure.getName();
|
String objectName = objectStructure.getName();
|
||||||
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
|
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
|
||||||
if (animationNames != null && animationNames.size() > 0) {
|
if (animationNames != null && animationNames.size() > 0) {
|
||||||
@ -108,15 +130,27 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Node apply(Node node, DataRepository dataRepository) {
|
public Node apply(Node node, DataRepository dataRepository) {
|
||||||
if(jmeModifierRepresentation == null) {
|
if(jmeModifierRepresentation == null) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setting weights for bones
|
||||||
|
List<Geometry> geomList = (List<Geometry>) dataRepository.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
|
for(Geometry geom : geomList) {
|
||||||
|
Mesh mesh = geom.getMesh();
|
||||||
|
if (this.verticesWeights != null) {
|
||||||
|
mesh.setMaxNumWeights(this.boneGroups);
|
||||||
|
mesh.setBuffer(this.verticesWeights);
|
||||||
|
mesh.setBuffer(this.verticesWeightsIndices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AnimData ad = (AnimData) jmeModifierRepresentation;
|
AnimData ad = (AnimData) jmeModifierRepresentation;
|
||||||
ArrayList<Animation> animList = ad.anims;
|
ArrayList<Animation> animList = ad.anims;
|
||||||
Long modifierArmatureObject = (Long) additionalData;
|
|
||||||
if (animList != null && animList.size() > 0) {
|
if (animList != null && animList.size() > 0) {
|
||||||
List<Constraint> constraints = dataRepository.getConstraints(modifierArmatureObject);
|
List<Constraint> constraints = dataRepository.getConstraints(this.armatureObjectOMA);
|
||||||
HashMap<String, Animation> anims = new HashMap<String, Animation>();
|
HashMap<String, Animation> anims = new HashMap<String, Animation>();
|
||||||
for (int i = 0; i < animList.size(); ++i) {
|
for (int i = 0; i < animList.size(); ++i) {
|
||||||
BoneAnimation boneAnimation = (BoneAnimation) animList.get(i).clone();
|
BoneAnimation boneAnimation = (BoneAnimation) animList.get(i).clone();
|
||||||
@ -146,39 +180,8 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
|
|
||||||
// applying the control to the node
|
// applying the control to the node
|
||||||
SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
|
SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
|
||||||
AnimControl control = node.getControl(AnimControl.class);
|
AnimControl control = new AnimControl(ad.skeleton);
|
||||||
|
|
||||||
if (control == null) {
|
|
||||||
control = new AnimControl(ad.skeleton);
|
|
||||||
} else {
|
|
||||||
// merging skeletons
|
|
||||||
Skeleton controlSkeleton = control.getSkeleton();
|
|
||||||
int boneIndexIncrease = controlSkeleton.getBoneCount();
|
|
||||||
Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);
|
|
||||||
|
|
||||||
// merging animations
|
|
||||||
HashMap<String, Animation> animations = new HashMap<String, Animation>();
|
|
||||||
for (String animationName : control.getAnimationNames()) {
|
|
||||||
animations.put(animationName,
|
|
||||||
control.getAnim(animationName));
|
|
||||||
}
|
|
||||||
for (Entry<String, Animation> animEntry : anims.entrySet()) {
|
|
||||||
BoneAnimation ba = (BoneAnimation) animEntry.getValue();
|
|
||||||
for (int i = 0; i < ba.getTracks().length; ++i) {
|
|
||||||
BoneTrack bt = ba.getTracks()[i];
|
|
||||||
int newBoneIndex = bt.getTargetBoneIndex()
|
|
||||||
+ boneIndexIncrease;
|
|
||||||
ba.getTracks()[i] = new BoneTrack(newBoneIndex,
|
|
||||||
bt.getTimes(), bt.getTranslations(),
|
|
||||||
bt.getRotations(), bt.getScales());
|
|
||||||
}
|
|
||||||
animations.put(animEntry.getKey(), animEntry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// replacing the control
|
|
||||||
node.removeControl(control);
|
|
||||||
control = new AnimControl(skeleton);
|
|
||||||
}
|
|
||||||
control.setAnimations(anims);
|
control.setAnimations(anims);
|
||||||
node.addControl(control);
|
node.addControl(control);
|
||||||
node.addControl(skeletonControl);
|
node.addControl(skeletonControl);
|
||||||
@ -186,32 +189,157 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method reads mesh indexes
|
||||||
|
* @param objectStructure structure of the object that has the armature modifier applied
|
||||||
|
* @param meshStructure the structure of the object's mesh
|
||||||
|
* @param dataRepository the data repository
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
||||||
|
*/
|
||||||
|
private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {
|
||||||
|
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
|
||||||
|
Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
|
||||||
|
Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);
|
||||||
|
|
||||||
|
int[] bonesGroups = new int[] { 0 };
|
||||||
|
|
||||||
|
VertexData vertexData = dataRepository.getVertexData(meshStructure.getOldMemoryAddress());
|
||||||
|
|
||||||
|
VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, vertexData.getVertexList().size(), bonesGroups,
|
||||||
|
vertexData.getVertexReferenceMap(), groupToBoneIndexMap, dataRepository);
|
||||||
|
this.verticesWeights = boneWeightsAndIndex[0];
|
||||||
|
this.verticesWeightsIndices = boneWeightsAndIndex[1];
|
||||||
|
this.boneGroups = bonesGroups[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The
|
||||||
|
* second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).
|
||||||
|
*
|
||||||
|
* @param meshStructure
|
||||||
|
* the mesh structure object
|
||||||
|
* @param vertexListSize
|
||||||
|
* a number of vertices in the model
|
||||||
|
* @param bonesGroups
|
||||||
|
* this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to
|
||||||
|
* MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
|
||||||
|
* @param vertexReferenceMap
|
||||||
|
* this reference map allows to map the original vertices read from blender to vertices that are really in the model; one
|
||||||
|
* vertex may appear several times in the result model
|
||||||
|
* @param groupToBoneIndexMap
|
||||||
|
* this object maps the group index (to which a vertices in blender belong) to bone index of the model
|
||||||
|
* @param dataRepository
|
||||||
|
* the data repository
|
||||||
|
* @return arrays of vertices weights and their bone indices and (as an output parameter) the maximum amount of weights for a vertex
|
||||||
|
* @throws BlenderFileException
|
||||||
|
* this exception is thrown when the blend file structure is somehow invalid or corrupted
|
||||||
|
*/
|
||||||
|
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,
|
||||||
|
Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)
|
||||||
|
throws BlenderFileException {
|
||||||
|
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
||||||
|
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||||
|
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||||
|
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
||||||
|
List<Structure> dverts = pDvert.fetchData(dataRepository.getInputStream());// dverts.size() == verticesAmount (one dvert per
|
||||||
|
// vertex in blender)
|
||||||
|
int vertexIndex = 0;
|
||||||
|
for (Structure dvert : dverts) {
|
||||||
|
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex
|
||||||
|
// (max. 4 in JME)
|
||||||
|
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
||||||
|
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
|
||||||
|
if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
|
||||||
|
int weightIndex = 0;
|
||||||
|
List<Structure> dw = pDW.fetchData(dataRepository.getInputStream());
|
||||||
|
for (Structure deformWeight : dw) {
|
||||||
|
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
|
||||||
|
if (boneIndex != null) {// null here means that we came accross group that has no bone attached to
|
||||||
|
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
||||||
|
if (weight == 0.0f) {
|
||||||
|
weight = 1;
|
||||||
|
boneIndex = Integer.valueOf(0);
|
||||||
|
}
|
||||||
|
// we apply the weight to all referenced vertices
|
||||||
|
for (Integer index : vertexIndices) {
|
||||||
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
|
||||||
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++weightIndex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Integer index : vertexIndices) {
|
||||||
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
|
||||||
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++vertexIndex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// always bind all vertices to 0-indexed bone
|
||||||
|
// this bone makes the model look normally if vertices have no bone assigned
|
||||||
|
// and it is used in object animation, so if we come accross object animation
|
||||||
|
// we can use the 0-indexed bone for this
|
||||||
|
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
|
||||||
|
// we apply the weight to all referenced vertices
|
||||||
|
for (Integer index : vertexIndexList) {
|
||||||
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
|
||||||
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
|
||||||
|
VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
|
||||||
|
verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
|
||||||
|
|
||||||
|
VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
|
||||||
|
verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
|
||||||
|
return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.
|
||||||
|
* @param vertCount amount of vertices
|
||||||
|
* @param weightsFloatData weights for vertices
|
||||||
|
*/
|
||||||
|
private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
|
||||||
|
int maxWeightsPerVert = 0;
|
||||||
|
weightsFloatData.rewind();
|
||||||
|
for (int v = 0; v < vertCount; ++v) {
|
||||||
|
float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
|
||||||
|
|
||||||
|
if (w3 != 0) {
|
||||||
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
|
||||||
|
} else if (w2 != 0) {
|
||||||
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
|
||||||
|
} else if (w1 != 0) {
|
||||||
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
|
||||||
|
} else if (w0 != 0) {
|
||||||
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sum = w0 + w1 + w2 + w3;
|
||||||
|
if (sum != 1f && sum != 0.0f) {
|
||||||
|
weightsFloatData.position(weightsFloatData.position() - 4);
|
||||||
|
// compute new vals based on sum
|
||||||
|
float sumToB = 1f / sum;
|
||||||
|
weightsFloatData.put(w0 * sumToB);
|
||||||
|
weightsFloatData.put(w1 * sumToB);
|
||||||
|
weightsFloatData.put(w2 * sumToB);
|
||||||
|
weightsFloatData.put(w3 * sumToB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
weightsFloatData.rewind();
|
||||||
|
|
||||||
|
// mesh.setMaxNumWeights(maxWeightsPerVert);
|
||||||
|
return maxWeightsPerVert;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return Modifier.ARMATURE_MODIFIER_DATA;
|
return Modifier.ARMATURE_MODIFIER_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method merges two skeletons into one. I assume that each skeleton's
|
|
||||||
* 0-indexed bone is objectAnimationBone so only one such bone should be
|
|
||||||
* placed in the result
|
|
||||||
*
|
|
||||||
* @param s1
|
|
||||||
* first skeleton
|
|
||||||
* @param s2
|
|
||||||
* second skeleton
|
|
||||||
* @return merged skeleton
|
|
||||||
*/
|
|
||||||
protected Skeleton merge(Skeleton s1, Skeleton s2) {
|
|
||||||
List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount()
|
|
||||||
+ s2.getBoneCount());
|
|
||||||
for (int i = 0; i < s1.getBoneCount(); ++i) {
|
|
||||||
bones.add(s1.getBone(i));
|
|
||||||
}
|
|
||||||
for (int i = 1; i < s2.getBoneCount(); ++i) {// ommit
|
|
||||||
// objectAnimationBone
|
|
||||||
bones.add(s2.getBone(i));
|
|
||||||
}
|
|
||||||
return new Skeleton(bones.toArray(new Bone[bones.size()]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ import com.jme3.scene.Spatial.CullHint;
|
|||||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository;
|
import com.jme3.scene.plugins.blender.DataRepository;
|
||||||
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
|
||||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
|
||||||
import com.jme3.scene.plugins.blender.cameras.CameraHelper;
|
import com.jme3.scene.plugins.blender.cameras.CameraHelper;
|
||||||
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
import com.jme3.scene.plugins.blender.curves.CurvesHelper;
|
import com.jme3.scene.plugins.blender.curves.CurvesHelper;
|
||||||
@ -141,9 +140,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dataRepository.pushParent(objectStructure);
|
dataRepository.pushParent(objectStructure);
|
||||||
|
|
||||||
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
||||||
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
|
|
||||||
|
|
||||||
//get object data
|
//get object data
|
||||||
int type = ((Number)objectStructure.getFieldValue("type")).intValue();
|
int type = ((Number)objectStructure.getFieldValue("type")).intValue();
|
||||||
@ -161,7 +158,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
|
Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
|
||||||
Object parent = dataRepository.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
Object parent = dataRepository.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
if(parent == null && pParent.isNotNull()) {
|
if(parent == null && pParent.isNotNull()) {
|
||||||
Structure parentStructure = pParent.fetchData(dataRepository.getInputStream()).get(0);//TODO: moze byc wiecej rodzicow
|
Structure parentStructure = pParent.fetchData(dataRepository.getInputStream()).get(0);//TODO: what if there are more parents ??
|
||||||
parent = this.toObject(parentStructure, dataRepository);
|
parent = this.toObject(parentStructure, dataRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,10 +260,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OBJECT_TYPE_ARMATURE:
|
case OBJECT_TYPE_ARMATURE:
|
||||||
LOGGER.log(Level.INFO, "Importing armature.");
|
//Do not do anything, the object with all needed data is loaded when armature modifier loads
|
||||||
Pointer pArmature = (Pointer)objectStructure.getFieldValue("data");
|
|
||||||
List<Structure> armaturesArray = pArmature.fetchData(dataRepository.getInputStream());//TODO: moze byc wiecej???
|
|
||||||
result = armatureHelper.toArmature(armaturesArray.get(0), dataRepository);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
|
LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
|
||||||
@ -275,13 +269,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
dataRepository.popParent();
|
dataRepository.popParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(result != null) {
|
||||||
//reading custom properties
|
//reading custom properties
|
||||||
Properties properties = this.loadProperties(objectStructure, dataRepository);
|
Properties properties = this.loadProperties(objectStructure, dataRepository);
|
||||||
if(result instanceof Spatial && properties != null && properties.getValue() != null) {
|
if(result instanceof Spatial && properties != null && properties.getValue() != null) {
|
||||||
((Spatial)result).setUserData("properties", properties);
|
((Spatial)result).setUserData("properties", properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != null) {
|
|
||||||
dataRepository.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
|
dataRepository.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user