|
|
|
@ -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 |
|
|
|
|