|
@@ -56,6 +56,7 @@ import com.jme3.effect.shapes.EmitterMeshVertexShape;
|
|
|
import com.jme3.effect.shapes.EmitterShape;
|
|
|
import com.jme3.material.Material;
|
|
|
import com.jme3.math.Matrix4f;
|
|
|
+import com.jme3.math.Transform;
|
|
|
import com.jme3.math.Vector3f;
|
|
|
import com.jme3.scene.Geometry;
|
|
|
import com.jme3.scene.Mesh;
|
|
@@ -67,6 +68,7 @@ import com.jme3.scene.plugins.blender.data.Structure;
|
|
|
import com.jme3.scene.plugins.blender.exception.BlenderFileException;
|
|
|
import com.jme3.scene.plugins.blender.helpers.ParticlesHelper;
|
|
|
import com.jme3.scene.plugins.blender.structures.Constraint;
|
|
|
+import com.jme3.scene.plugins.blender.structures.Ipo;
|
|
|
import com.jme3.scene.plugins.blender.structures.Modifier;
|
|
|
import com.jme3.scene.plugins.blender.utils.AbstractBlenderHelper;
|
|
|
import com.jme3.scene.plugins.blender.utils.DataRepository;
|
|
@@ -107,6 +109,8 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
|
|
public Node applyModifier(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
return this.applyArmatureModifierData(node, modifier, dataRepository);
|
|
|
+ } else if (Modifier.OBJECT_ANIMATION_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
+ return this.applyObjectAnimationModifier(node, modifier, dataRepository);
|
|
|
} else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
return this.applyArrayModifierData(node, modifier, dataRepository);
|
|
|
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {
|
|
@@ -227,17 +231,20 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
|
|
Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(), 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);
|
|
|
}
|
|
|
modifierAdditionalData = armatureObject.getOldMemoryAddress();
|
|
|
ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
|
|
|
|
|
|
- // changing bones matrices so that they fit the current object (taht is why we need a copy of a skeleton)
|
|
|
+ // changing bones matrices so that they fit the current object (that is why we need a copy of a skeleton)
|
|
|
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
|
|
|
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
|
|
|
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
|
|
|
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
|
|
|
|
|
|
+ //setting the bones structure inside the skeleton (thus completing its loading)
|
|
|
+ Skeleton skeleton = new Skeleton(bones);
|
|
|
+ dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton);
|
|
|
+
|
|
|
String objectName = objectStructure.getName();
|
|
|
Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
|
|
|
if (animationNames != null && animationNames.size() > 0) {
|
|
@@ -275,6 +282,81 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
|
|
modifierAdditionalData = null;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ //at the end read object's animation modifier
|
|
|
+ Modifier objectAnimationModifier = this.readObjectAnimation(objectStructure, dataRepository);
|
|
|
+ if(objectAnimationModifier != null) {
|
|
|
+ dataRepository.addModifier(objectStructure.getOldMemoryAddress(),
|
|
|
+ objectAnimationModifier.getType(),
|
|
|
+ objectAnimationModifier.getJmeModifierRepresentation(),
|
|
|
+ objectAnimationModifier.getAdditionalData());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method reads animation of the object itself (without bones) and stores it as an ArmatureModifierData
|
|
|
+ * modifier. The animation is returned as a modifier. It should be later applied regardless other modifiers. The
|
|
|
+ * reason for this is that object may not have modifiers added but it's animation should be working.
|
|
|
+ * @param objectStructure
|
|
|
+ * the structure of the object
|
|
|
+ * @param dataRepository
|
|
|
+ * the data repository
|
|
|
+ * @return animation modifier is returned, it should be separately applied when the object is loaded
|
|
|
+ * @throws BlenderFileException
|
|
|
+ * this exception is thrown when the blender file is somehow corrupted
|
|
|
+ */
|
|
|
+ protected Modifier readObjectAnimation(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
|
|
|
+ Pointer pIpo = (Pointer)objectStructure.getFieldValue("ipo");
|
|
|
+ if(pIpo.isNotNull()) {
|
|
|
+ //check if there is an action name connected with this ipo
|
|
|
+ String objectAnimationName = null;
|
|
|
+ List<FileBlockHeader> actionBlocks = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
|
|
+ for(FileBlockHeader actionBlock : actionBlocks) {
|
|
|
+ Structure action = actionBlock.getStructure(dataRepository);
|
|
|
+ List<Structure> actionChannels = ((Structure)action.getFieldValue("chanbase")).evaluateListBase(dataRepository);
|
|
|
+ if(actionChannels.size() == 1) {//object's animtion action has only one channel
|
|
|
+ Pointer pChannelIpo = (Pointer)actionChannels.get(0).getFieldValue("ipo");
|
|
|
+ if(pChannelIpo.equals(pIpo)) {
|
|
|
+ objectAnimationName = action.getName();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ String objectName = objectStructure.getName();
|
|
|
+ if(objectAnimationName == null) {//set the object's animation name to object's name
|
|
|
+ objectAnimationName = objectName;
|
|
|
+ }
|
|
|
+
|
|
|
+ IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);
|
|
|
+ Structure ipoStructure = pIpo.fetchData(dataRepository.getInputStream()).get(0);
|
|
|
+ Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository);
|
|
|
+ int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, objectAnimationName);
|
|
|
+ if(animationFrames == null) {//if the name was created here there are no frames set for the animation
|
|
|
+ animationFrames = new int[] {1, ipo.getLastFrame()};
|
|
|
+ }
|
|
|
+ int fps = dataRepository.getBlenderKey().getFps();
|
|
|
+ float start = (float)animationFrames[0] / (float)fps;
|
|
|
+ float stop = (float)animationFrames[1] / (float)fps;
|
|
|
+
|
|
|
+ //calculating track for the only bone in this skeleton
|
|
|
+ BoneTrack[] tracks = new BoneTrack[1];
|
|
|
+ tracks[0] = ipo.calculateTrack(0, animationFrames[0], animationFrames[1], fps);
|
|
|
+
|
|
|
+ BoneAnimation boneAnimation = new BoneAnimation(objectAnimationName, stop - start);
|
|
|
+ boneAnimation.setTracks(tracks);
|
|
|
+ ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>(1);
|
|
|
+ animations.add(boneAnimation);
|
|
|
+
|
|
|
+ //preparing the object's bone
|
|
|
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
+ Transform t = objectHelper.getTransformation(objectStructure);
|
|
|
+ Bone bone = new Bone(null);
|
|
|
+ bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
|
|
+
|
|
|
+ return new Modifier(Modifier.OBJECT_ANIMATION_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[] {bone}), animations), objectStructure.getOldMemoryAddress());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -404,6 +486,12 @@ public class ModifierHelper extends AbstractBlenderHelper {
|
|
|
return node;
|
|
|
}
|
|
|
|
|
|
+ protected Node applyObjectAnimationModifier(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
+ AnimData ad = (AnimData) modifier.getJmeModifierRepresentation();
|
|
|
+ ad.skeleton.getBone(0).setAttachNode(node);
|
|
|
+ return this.applyArmatureModifierData(node, modifier, dataRepository);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* This method applies the array modifier to the node.
|
|
|
* @param node
|