|
@@ -60,7 +60,6 @@ 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.Type;
|
|
import com.jme3.scene.VertexBuffer.Type;
|
|
|
import com.jme3.scene.plugins.blender.data.FileBlockHeader;
|
|
import com.jme3.scene.plugins.blender.data.FileBlockHeader;
|
|
|
import com.jme3.scene.plugins.blender.data.Structure;
|
|
import com.jme3.scene.plugins.blender.data.Structure;
|
|
@@ -81,537 +80,586 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|
|
*/
|
|
*/
|
|
|
public class ModifierHelper extends AbstractBlenderHelper {
|
|
public class ModifierHelper extends AbstractBlenderHelper {
|
|
|
|
|
|
|
|
- private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This constructor parses the given blender version and stores the result. Some functionalities may differ in
|
|
|
|
|
- * different blender versions.
|
|
|
|
|
- * @param blenderVersion
|
|
|
|
|
- * the version read from the blend file
|
|
|
|
|
- */
|
|
|
|
|
- public ModifierHelper(String blenderVersion) {
|
|
|
|
|
- super(blenderVersion);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method applies modifier to the object.
|
|
|
|
|
- * @param node
|
|
|
|
|
- * the loaded object
|
|
|
|
|
- * @param modifier
|
|
|
|
|
- * the modifier to apply
|
|
|
|
|
- * @param dataRepository
|
|
|
|
|
- * the data repository
|
|
|
|
|
- * @return the node to whom the modifier was applied
|
|
|
|
|
- */
|
|
|
|
|
- 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.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
- return this.applyArrayModifierData(node, modifier, dataRepository);
|
|
|
|
|
- } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
- return this.applyParticleSystemModifierData(node, modifier, dataRepository);
|
|
|
|
|
- } else if(Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
- return this.applyMirrorModifierData(node, modifier, dataRepository);
|
|
|
|
|
- } else {
|
|
|
|
|
- LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!");
|
|
|
|
|
- return node;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method reads the given object's modifiers.
|
|
|
|
|
- * @param objectStructure
|
|
|
|
|
- * the object structure
|
|
|
|
|
- * @param dataRepository
|
|
|
|
|
- * the data repository
|
|
|
|
|
- * @param converter
|
|
|
|
|
- * the converter object (in some cases we need to read an object first before loading the modifier)
|
|
|
|
|
- * @throws BlenderFileException
|
|
|
|
|
- * this exception is thrown when the blender file is somehow corrupted
|
|
|
|
|
- */
|
|
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
|
|
- public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
|
|
|
|
|
- Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
|
|
|
|
|
- List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository);
|
|
|
|
|
- for (Structure modifier : modifiers) {
|
|
|
|
|
- Object loadedModifier = null;
|
|
|
|
|
- Object modifierAdditionalData = null;
|
|
|
|
|
- if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {//****************ARRAY MODIFIER
|
|
|
|
|
- Map<String, Object> params = new HashMap<String, Object>();
|
|
|
|
|
-
|
|
|
|
|
- Number fittype = (Number) modifier.getFieldValue("fit_type");
|
|
|
|
|
- params.put("fittype", fittype);
|
|
|
|
|
- switch (fittype.intValue()) {
|
|
|
|
|
- case 0://FIXED COUNT
|
|
|
|
|
- params.put("count", modifier.getFieldValue("count"));
|
|
|
|
|
- break;
|
|
|
|
|
- case 1://FIXED LENGTH
|
|
|
|
|
- params.put("length", modifier.getFieldValue("length"));
|
|
|
|
|
- break;
|
|
|
|
|
- case 2://FITCURVE
|
|
|
|
|
- //TODO: implement after loading curves is added; warning will be generated during modifier applying
|
|
|
|
|
- break;
|
|
|
|
|
- default:
|
|
|
|
|
- assert false : "Unknown array modifier fit type: " + fittype;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //offset parameters
|
|
|
|
|
- int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue();
|
|
|
|
|
- if ((offsettype & 0x01) != 0) {//Constant offset
|
|
|
|
|
- DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset");
|
|
|
|
|
- float[] offset = new float[]{offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue()};
|
|
|
|
|
- params.put("offset", offset);
|
|
|
|
|
- }
|
|
|
|
|
- if ((offsettype & 0x02) != 0) {//Relative offset
|
|
|
|
|
- DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale");
|
|
|
|
|
- float[] scale = new float[]{scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()};
|
|
|
|
|
- params.put("scale", scale);
|
|
|
|
|
- }
|
|
|
|
|
- if ((offsettype & 0x04) != 0) {//Object offset
|
|
|
|
|
- Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob");
|
|
|
|
|
- if (!pOffsetObject.isNull()) {
|
|
|
|
|
- params.put("offsetob", pOffsetObject);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //start cap and end cap
|
|
|
|
|
- Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap");
|
|
|
|
|
- if (!pStartCap.isNull()) {
|
|
|
|
|
- params.put("startcap", pStartCap);
|
|
|
|
|
- }
|
|
|
|
|
- Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap");
|
|
|
|
|
- if (!pEndCap.isNull()) {
|
|
|
|
|
- params.put("endcap", pEndCap);
|
|
|
|
|
- }
|
|
|
|
|
- loadedModifier = params;
|
|
|
|
|
- } if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) {//****************MIRROR MODIFIER
|
|
|
|
|
- Map<String, Object> params = new HashMap<String, Object>();
|
|
|
|
|
-
|
|
|
|
|
- params.put("flag", modifier.getFieldValue("flag"));
|
|
|
|
|
- params.put("tolerance", modifier.getFieldValue("tolerance"));
|
|
|
|
|
- Pointer pMirrorOb = (Pointer) modifier.getFieldValue("mirror_ob");
|
|
|
|
|
- if (!pMirrorOb.isNull()) {
|
|
|
|
|
- params.put("mirrorob", pMirrorOb);
|
|
|
|
|
- }
|
|
|
|
|
- loadedModifier = params;
|
|
|
|
|
- } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {//****************ARMATURE MODIFIER
|
|
|
|
|
- Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object");
|
|
|
|
|
- if (!pArmatureObject.isNull()) {
|
|
|
|
|
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
- 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)
|
|
|
|
|
- Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
|
|
|
|
|
- Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
|
|
|
|
|
- Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
|
|
|
|
|
- Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
|
|
|
|
|
-
|
|
|
|
|
- String objectName = objectStructure.getName();
|
|
|
|
|
- Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
|
|
|
|
|
- if (animationNames != null && animationNames.size() > 0) {
|
|
|
|
|
- ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
|
|
|
|
|
- List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
|
|
|
|
- for (FileBlockHeader header : actionHeaders) {
|
|
|
|
|
- Structure actionStructure = header.getStructure(dataRepository);
|
|
|
|
|
- String actionName = actionStructure.getName();
|
|
|
|
|
- if (animationNames.contains(actionName)) {
|
|
|
|
|
- int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName);
|
|
|
|
|
- int fps = dataRepository.getBlenderKey().getFps();
|
|
|
|
|
- float start = (float) animationFrames[0] / (float) fps;
|
|
|
|
|
- float stop = (float) animationFrames[1] / (float) fps;
|
|
|
|
|
- BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start);
|
|
|
|
|
- boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName));
|
|
|
|
|
- animations.add(boneAnimation);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- loadedModifier = new AnimData(new Skeleton(bones), animations);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- LOGGER.warning("Unsupported modifier type: " + modifier.getType());
|
|
|
|
|
- }
|
|
|
|
|
- } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {//****************PARTICLES MODIFIER
|
|
|
|
|
- Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys");
|
|
|
|
|
- if (!pParticleSystem.isNull()) {
|
|
|
|
|
- ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class);
|
|
|
|
|
- Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0);
|
|
|
|
|
- loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- //adding modifier to the modifier's lists
|
|
|
|
|
- if (loadedModifier != null) {
|
|
|
|
|
- dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData);
|
|
|
|
|
- modifierAdditionalData = null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method applies particles emitter to the given node.
|
|
|
|
|
- * @param node the particles emitter node
|
|
|
|
|
- * @param modifier the modifier containing the emitter data
|
|
|
|
|
- * @param dataRepository the data repository
|
|
|
|
|
- * @return node with particles' emitter applied
|
|
|
|
|
- */
|
|
|
|
|
- protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
- MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
|
|
|
|
- ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();
|
|
|
|
|
- emitter = emitter.clone();
|
|
|
|
|
-
|
|
|
|
|
- //veryfying the alpha function for particles' texture
|
|
|
|
|
- Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;
|
|
|
|
|
- char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1);
|
|
|
|
|
- if (nameSuffix == 'B' || nameSuffix == 'N') {
|
|
|
|
|
- alphaFunction = MaterialHelper.ALPHA_MASK_NONE;
|
|
|
|
|
- }
|
|
|
|
|
- //removing the type suffix from the name
|
|
|
|
|
- emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1));
|
|
|
|
|
-
|
|
|
|
|
- //applying emitter shape
|
|
|
|
|
- EmitterShape emitterShape = emitter.getShape();
|
|
|
|
|
- List<Mesh> meshes = new ArrayList<Mesh>();
|
|
|
|
|
- for (Spatial spatial : node.getChildren()) {
|
|
|
|
|
- if (spatial instanceof Geometry) {
|
|
|
|
|
- Mesh mesh = ((Geometry) spatial).getMesh();
|
|
|
|
|
- if (mesh != null) {
|
|
|
|
|
- meshes.add(mesh);
|
|
|
|
|
- Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);
|
|
|
|
|
- emitter.setMaterial(material);//TODO: divide into several pieces
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
|
|
|
|
|
- ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- node.attachChild(emitter);
|
|
|
|
|
- return node;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method applies ArmatureModifierData to the loaded object.
|
|
|
|
|
- * @param node
|
|
|
|
|
- * the loaded object
|
|
|
|
|
- * @param modifier
|
|
|
|
|
- * the modifier to apply
|
|
|
|
|
- * @param dataRepository
|
|
|
|
|
- * the data repository
|
|
|
|
|
- * @return the node to whom the modifier was applied
|
|
|
|
|
- */
|
|
|
|
|
- protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
- AnimData ad = (AnimData) modifier.getJmeModifierRepresentation();
|
|
|
|
|
- ArrayList<BoneAnimation> animList = ad.anims;
|
|
|
|
|
- Long modifierArmatureObject = (Long) modifier.getAdditionalData();
|
|
|
|
|
- if (animList != null && animList.size() > 0) {
|
|
|
|
|
- ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class);
|
|
|
|
|
- Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject);
|
|
|
|
|
- HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
|
|
|
|
|
- for (int i = 0; i < animList.size(); ++i) {
|
|
|
|
|
- BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i));
|
|
|
|
|
-
|
|
|
|
|
- //baking constraints into animations
|
|
|
|
|
- if (constraints != null && constraints.length > 0) {
|
|
|
|
|
- for (Constraint constraint : constraints) {
|
|
|
|
|
- constraint.affectAnimation(ad.skeleton, boneAnimation);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- anims.put(boneAnimation.getName(), boneAnimation);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //getting meshes
|
|
|
|
|
- Mesh[] meshes = null;
|
|
|
|
|
- List<Mesh> meshesList = new ArrayList<Mesh>();
|
|
|
|
|
- List<Spatial> children = node.getChildren();
|
|
|
|
|
- for (Spatial child : children) {
|
|
|
|
|
- if (child instanceof Geometry) {
|
|
|
|
|
- meshesList.add(((Geometry) child).getMesh());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (meshesList.size() > 0) {
|
|
|
|
|
- meshes = meshesList.toArray(new Mesh[meshesList.size()]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //applying the control to the node
|
|
|
|
|
- SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
|
|
|
|
|
- AnimControl control = node.getControl(AnimControl.class);
|
|
|
|
|
-
|
|
|
|
|
- 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, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
|
|
|
|
|
- for (String animationName : control.getAnimationNames()) {
|
|
|
|
|
- animations.put(animationName, control.getAnim(animationName));
|
|
|
|
|
- }
|
|
|
|
|
- for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) {
|
|
|
|
|
- BoneAnimation ba = 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);
|
|
|
|
|
- node.addControl(control);
|
|
|
|
|
- node.addControl(skeletonControl);
|
|
|
|
|
- }
|
|
|
|
|
- return node;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method applies the array modifier to the node.
|
|
|
|
|
- * @param node
|
|
|
|
|
- * the object the modifier will be applied to
|
|
|
|
|
- * @param modifier
|
|
|
|
|
- * the modifier to be applied
|
|
|
|
|
- * @param dataRepository
|
|
|
|
|
- * the data repository
|
|
|
|
|
- * @return object node with array modifier applied
|
|
|
|
|
- */
|
|
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
|
|
- protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
- Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();
|
|
|
|
|
- int fittype = ((Number) modifierData.get("fittype")).intValue();
|
|
|
|
|
- float[] offset = (float[]) modifierData.get("offset");
|
|
|
|
|
- if (offset == null) {//the node will be repeated several times in the same place
|
|
|
|
|
- offset = new float[]{0.0f, 0.0f, 0.0f};
|
|
|
|
|
- }
|
|
|
|
|
- float[] scale = (float[]) modifierData.get("scale");
|
|
|
|
|
- if (scale == null) {//the node will be repeated several times in the same place
|
|
|
|
|
- scale = new float[]{0.0f, 0.0f, 0.0f};
|
|
|
|
|
- } else {
|
|
|
|
|
- //getting bounding box
|
|
|
|
|
- node.updateModelBound();
|
|
|
|
|
- BoundingVolume boundingVolume = node.getWorldBound();
|
|
|
|
|
- if (boundingVolume instanceof BoundingBox) {
|
|
|
|
|
- scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;
|
|
|
|
|
- scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;
|
|
|
|
|
- scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;
|
|
|
|
|
- } else if (boundingVolume instanceof BoundingSphere) {
|
|
|
|
|
- float radius = ((BoundingSphere) boundingVolume).getRadius();
|
|
|
|
|
- scale[0] *= radius * 2.0f;
|
|
|
|
|
- scale[1] *= radius * 2.0f;
|
|
|
|
|
- scale[2] *= radius * 2.0f;
|
|
|
|
|
- } else {
|
|
|
|
|
- throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //adding object's offset
|
|
|
|
|
- float[] objectOffset = new float[]{0.0f, 0.0f, 0.0f};
|
|
|
|
|
- Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");
|
|
|
|
|
- if (pOffsetObject != null) {
|
|
|
|
|
- FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
|
|
|
|
|
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
- try {//we take the structure in case the object was not yet loaded
|
|
|
|
|
- Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);
|
|
|
|
|
- Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();
|
|
|
|
|
- objectOffset[0] = translation.x;
|
|
|
|
|
- objectOffset[1] = translation.y;
|
|
|
|
|
- objectOffset[2] = translation.z;
|
|
|
|
|
- } catch (BlenderFileException e) {
|
|
|
|
|
- LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //getting start and end caps
|
|
|
|
|
- Node[] caps = new Node[]{null, null};
|
|
|
|
|
- Pointer[] pCaps = new Pointer[]{(Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap")};
|
|
|
|
|
- for (int i = 0; i < pCaps.length; ++i) {
|
|
|
|
|
- if (pCaps[i] != null) {
|
|
|
|
|
- caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
|
|
|
|
- if (caps[i] != null) {
|
|
|
|
|
- caps[i] = (Node) caps[i].clone();
|
|
|
|
|
- } else {
|
|
|
|
|
- FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
|
|
|
|
|
- try {//we take the structure in case the object was not yet loaded
|
|
|
|
|
- Structure capStructure = capBlock.getStructure(dataRepository);
|
|
|
|
|
- ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
- caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository);
|
|
|
|
|
- if (caps[i] == null) {
|
|
|
|
|
- LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!");
|
|
|
|
|
- }
|
|
|
|
|
- } catch (BlenderFileException e) {
|
|
|
|
|
- LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0],
|
|
|
|
|
- offset[1] + scale[1] + objectOffset[1],
|
|
|
|
|
- offset[2] + scale[2] + objectOffset[2]);
|
|
|
|
|
-
|
|
|
|
|
- //getting/calculating repeats amount
|
|
|
|
|
- int count = 0;
|
|
|
|
|
- if (fittype == 0) {//Fixed count
|
|
|
|
|
- count = ((Number) modifierData.get("count")).intValue() - 1;
|
|
|
|
|
- } else if (fittype == 1) {//Fixed length
|
|
|
|
|
- float length = ((Number) modifierData.get("length")).floatValue();
|
|
|
|
|
- if (translationVector.length() > 0.0f) {
|
|
|
|
|
- count = (int) (length / translationVector.length()) - 1;
|
|
|
|
|
- }
|
|
|
|
|
- } else if (fittype == 2) {//Fit curve
|
|
|
|
|
- LOGGER.warning("Fit curve mode in array modifier not yet implemented!");//TODO: implement fit curve
|
|
|
|
|
- } else {
|
|
|
|
|
- throw new IllegalStateException("Unknown fit type: " + fittype);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //adding translated nodes and caps
|
|
|
|
|
- if (count > 0) {
|
|
|
|
|
- Node[] arrayNodes = new Node[count];
|
|
|
|
|
- Vector3f newTranslation = node.getLocalTranslation().clone();
|
|
|
|
|
- for (int i = 0; i < count; ++i) {
|
|
|
|
|
- newTranslation.addLocal(translationVector);
|
|
|
|
|
- Node nodeClone = (Node) node.clone();
|
|
|
|
|
- nodeClone.setLocalTranslation(newTranslation);
|
|
|
|
|
- arrayNodes[i] = nodeClone;
|
|
|
|
|
- }
|
|
|
|
|
- for (Node nodeClone : arrayNodes) {
|
|
|
|
|
- node.attachChild(nodeClone);
|
|
|
|
|
- }
|
|
|
|
|
- if (caps[0] != null) {
|
|
|
|
|
- caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);
|
|
|
|
|
- node.attachChild(caps[0]);
|
|
|
|
|
- }
|
|
|
|
|
- if (caps[1] != null) {
|
|
|
|
|
- caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);
|
|
|
|
|
- node.attachChild(caps[1]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return node;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This method applies the mirror modifier to the node.
|
|
|
|
|
- * @param node
|
|
|
|
|
- * the object the modifier will be applied to
|
|
|
|
|
- * @param modifier
|
|
|
|
|
- * the modifier to be applied
|
|
|
|
|
- * @param dataRepository
|
|
|
|
|
- * the data repository
|
|
|
|
|
- * @return object node with mirror modifier applied
|
|
|
|
|
- */
|
|
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
|
|
|
|
+ private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName());
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This constructor parses the given blender version and stores the result. Some functionalities may differ in
|
|
|
|
|
+ * different blender versions.
|
|
|
|
|
+ * @param blenderVersion
|
|
|
|
|
+ * the version read from the blend file
|
|
|
|
|
+ */
|
|
|
|
|
+ public ModifierHelper(String blenderVersion) {
|
|
|
|
|
+ super(blenderVersion);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method applies modifier to the object.
|
|
|
|
|
+ * @param node
|
|
|
|
|
+ * the loaded object
|
|
|
|
|
+ * @param modifier
|
|
|
|
|
+ * the modifier to apply
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @return the node to whom the modifier was applied
|
|
|
|
|
+ */
|
|
|
|
|
+ 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.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
+ return this.applyArrayModifierData(node, modifier, dataRepository);
|
|
|
|
|
+ } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
+ return this.applyParticleSystemModifierData(node, modifier, dataRepository);
|
|
|
|
|
+ } else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) {
|
|
|
|
|
+ return this.applyMirrorModifierData(node, modifier, dataRepository);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ LOGGER.warning("Modifier: " + modifier.getType() + " not yet implemented!!!");
|
|
|
|
|
+ return node;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method reads the given object's modifiers.
|
|
|
|
|
+ * @param objectStructure
|
|
|
|
|
+ * the object structure
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @param converter
|
|
|
|
|
+ * the converter object (in some cases we need to read an object first before loading the modifier)
|
|
|
|
|
+ * @throws BlenderFileException
|
|
|
|
|
+ * this exception is thrown when the blender file is somehow corrupted
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ public void readModifiers(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
|
|
|
|
|
+ Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
|
|
|
|
|
+ List<Structure> modifiers = modifiersListBase.evaluateListBase(dataRepository);
|
|
|
|
|
+ for (Structure modifier : modifiers) {
|
|
|
|
|
+ Object loadedModifier = null;
|
|
|
|
|
+ Object modifierAdditionalData = null;
|
|
|
|
|
+ if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) {// ****************ARRAY MODIFIER
|
|
|
|
|
+ Map<String, Object> params = new HashMap<String, Object>();
|
|
|
|
|
+
|
|
|
|
|
+ Number fittype = (Number) modifier.getFieldValue("fit_type");
|
|
|
|
|
+ params.put("fittype", fittype);
|
|
|
|
|
+ switch (fittype.intValue()) {
|
|
|
|
|
+ case 0:// FIXED COUNT
|
|
|
|
|
+ params.put("count", modifier.getFieldValue("count"));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 1:// FIXED LENGTH
|
|
|
|
|
+ params.put("length", modifier.getFieldValue("length"));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 2:// FITCURVE
|
|
|
|
|
+ // TODO: implement after loading curves is added; warning will be generated during modifier applying
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ assert false : "Unknown array modifier fit type: " + fittype;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // offset parameters
|
|
|
|
|
+ int offsettype = ((Number) modifier.getFieldValue("offset_type")).intValue();
|
|
|
|
|
+ if ((offsettype & 0x01) != 0) {// Constant offset
|
|
|
|
|
+ DynamicArray<Number> offsetArray = (DynamicArray<Number>) modifier.getFieldValue("offset");
|
|
|
|
|
+ float[] offset = new float[] { offsetArray.get(0).floatValue(), offsetArray.get(1).floatValue(), offsetArray.get(2).floatValue() };
|
|
|
|
|
+ params.put("offset", offset);
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((offsettype & 0x02) != 0) {// Relative offset
|
|
|
|
|
+ DynamicArray<Number> scaleArray = (DynamicArray<Number>) modifier.getFieldValue("scale");
|
|
|
|
|
+ float[] scale = new float[] { scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue() };
|
|
|
|
|
+ params.put("scale", scale);
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((offsettype & 0x04) != 0) {// Object offset
|
|
|
|
|
+ Pointer pOffsetObject = (Pointer) modifier.getFieldValue("offset_ob");
|
|
|
|
|
+ if (!pOffsetObject.isNull()) {
|
|
|
|
|
+ params.put("offsetob", pOffsetObject);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // start cap and end cap
|
|
|
|
|
+ Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap");
|
|
|
|
|
+ if (!pStartCap.isNull()) {
|
|
|
|
|
+ params.put("startcap", pStartCap);
|
|
|
|
|
+ }
|
|
|
|
|
+ Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap");
|
|
|
|
|
+ if (!pEndCap.isNull()) {
|
|
|
|
|
+ params.put("endcap", pEndCap);
|
|
|
|
|
+ }
|
|
|
|
|
+ loadedModifier = params;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) {// ****************MIRROR MODIFIER
|
|
|
|
|
+ Map<String, Object> params = new HashMap<String, Object>();
|
|
|
|
|
+
|
|
|
|
|
+ params.put("flag", modifier.getFieldValue("flag"));
|
|
|
|
|
+ params.put("tolerance", modifier.getFieldValue("tolerance"));
|
|
|
|
|
+ Pointer pMirrorOb = (Pointer) modifier.getFieldValue("mirror_ob");
|
|
|
|
|
+ if (!pMirrorOb.isNull()) {
|
|
|
|
|
+ params.put("mirrorob", pMirrorOb);
|
|
|
|
|
+ }
|
|
|
|
|
+ loadedModifier = params;
|
|
|
|
|
+ } else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifier.getType())) {// ****************ARMATURE MODIFIER
|
|
|
|
|
+ Pointer pArmatureObject = (Pointer) modifier.getFieldValue("object");
|
|
|
|
|
+ if (!pArmatureObject.isNull()) {
|
|
|
|
|
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
+ 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)
|
|
|
|
|
+ Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
|
|
|
|
|
+ Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
|
|
|
|
|
+ Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
|
|
|
|
|
+ Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
|
|
|
|
|
+
|
|
|
|
|
+ String objectName = objectStructure.getName();
|
|
|
|
|
+ Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
|
|
|
|
|
+ if (animationNames != null && animationNames.size() > 0) {
|
|
|
|
|
+ ArrayList<BoneAnimation> animations = new ArrayList<BoneAnimation>();
|
|
|
|
|
+ List<FileBlockHeader> actionHeaders = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
|
|
|
|
+ for (FileBlockHeader header : actionHeaders) {
|
|
|
|
|
+ Structure actionStructure = header.getStructure(dataRepository);
|
|
|
|
|
+ String actionName = actionStructure.getName();
|
|
|
|
|
+ if (animationNames.contains(actionName)) {
|
|
|
|
|
+ int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, actionName);
|
|
|
|
|
+ int fps = dataRepository.getBlenderKey().getFps();
|
|
|
|
|
+ float start = (float) animationFrames[0] / (float) fps;
|
|
|
|
|
+ float stop = (float) animationFrames[1] / (float) fps;
|
|
|
|
|
+ BoneAnimation boneAnimation = new BoneAnimation(actionName, stop - start);
|
|
|
|
|
+ boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, dataRepository, objectName, actionName));
|
|
|
|
|
+ animations.add(boneAnimation);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ loadedModifier = new AnimData(new Skeleton(bones), animations);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ LOGGER.warning("Unsupported modifier type: " + modifier.getType());
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {// ****************PARTICLES MODIFIER
|
|
|
|
|
+ Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys");
|
|
|
|
|
+ if (!pParticleSystem.isNull()) {
|
|
|
|
|
+ ParticlesHelper particlesHelper = dataRepository.getHelper(ParticlesHelper.class);
|
|
|
|
|
+ Structure particleSystem = pParticleSystem.fetchData(dataRepository.getInputStream()).get(0);
|
|
|
|
|
+ loadedModifier = particlesHelper.toParticleEmitter(particleSystem, dataRepository);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // adding modifier to the modifier's lists
|
|
|
|
|
+ if (loadedModifier != null) {
|
|
|
|
|
+ dataRepository.addModifier(objectStructure.getOldMemoryAddress(), modifier.getType(), loadedModifier, modifierAdditionalData);
|
|
|
|
|
+ modifierAdditionalData = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method applies particles emitter to the given node.
|
|
|
|
|
+ * @param node
|
|
|
|
|
+ * the particles emitter node
|
|
|
|
|
+ * @param modifier
|
|
|
|
|
+ * the modifier containing the emitter data
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @return node with particles' emitter applied
|
|
|
|
|
+ */
|
|
|
|
|
+ protected Node applyParticleSystemModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
+ MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
|
|
|
|
|
+ ParticleEmitter emitter = (ParticleEmitter) modifier.getJmeModifierRepresentation();
|
|
|
|
|
+ emitter = emitter.clone();
|
|
|
|
|
+
|
|
|
|
|
+ // veryfying the alpha function for particles' texture
|
|
|
|
|
+ Integer alphaFunction = MaterialHelper.ALPHA_MASK_HYPERBOLE;
|
|
|
|
|
+ char nameSuffix = emitter.getName().charAt(emitter.getName().length() - 1);
|
|
|
|
|
+ if (nameSuffix == 'B' || nameSuffix == 'N') {
|
|
|
|
|
+ alphaFunction = MaterialHelper.ALPHA_MASK_NONE;
|
|
|
|
|
+ }
|
|
|
|
|
+ // removing the type suffix from the name
|
|
|
|
|
+ emitter.setName(emitter.getName().substring(0, emitter.getName().length() - 1));
|
|
|
|
|
+
|
|
|
|
|
+ // applying emitter shape
|
|
|
|
|
+ EmitterShape emitterShape = emitter.getShape();
|
|
|
|
|
+ List<Mesh> meshes = new ArrayList<Mesh>();
|
|
|
|
|
+ for (Spatial spatial : node.getChildren()) {
|
|
|
|
|
+ if (spatial instanceof Geometry) {
|
|
|
|
|
+ Mesh mesh = ((Geometry) spatial).getMesh();
|
|
|
|
|
+ if (mesh != null) {
|
|
|
|
|
+ meshes.add(mesh);
|
|
|
|
|
+ Material material = materialHelper.getParticlesMaterial(((Geometry) spatial).getMaterial(), alphaFunction, dataRepository);
|
|
|
|
|
+ emitter.setMaterial(material);// TODO: divide into several pieces
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (meshes.size() > 0 && emitterShape instanceof EmitterMeshVertexShape) {
|
|
|
|
|
+ ((EmitterMeshVertexShape) emitterShape).setMeshes(meshes);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ node.attachChild(emitter);
|
|
|
|
|
+ return node;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method applies ArmatureModifierData to the loaded object.
|
|
|
|
|
+ * @param node
|
|
|
|
|
+ * the loaded object
|
|
|
|
|
+ * @param modifier
|
|
|
|
|
+ * the modifier to apply
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @return the node to whom the modifier was applied
|
|
|
|
|
+ */
|
|
|
|
|
+ protected Node applyArmatureModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
+ AnimData ad = (AnimData) modifier.getJmeModifierRepresentation();
|
|
|
|
|
+ ArrayList<BoneAnimation> animList = ad.anims;
|
|
|
|
|
+ Long modifierArmatureObject = (Long) modifier.getAdditionalData();
|
|
|
|
|
+ if (animList != null && animList.size() > 0) {
|
|
|
|
|
+ ConstraintHelper constraintHelper = dataRepository.getHelper(ConstraintHelper.class);
|
|
|
|
|
+ Constraint[] constraints = constraintHelper.getConstraints(modifierArmatureObject);
|
|
|
|
|
+ HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
|
|
|
|
|
+ for (int i = 0; i < animList.size(); ++i) {
|
|
|
|
|
+ BoneAnimation boneAnimation = this.cloneBoneAnimation(animList.get(i));
|
|
|
|
|
+
|
|
|
|
|
+ // baking constraints into animations
|
|
|
|
|
+ if (constraints != null && constraints.length > 0) {
|
|
|
|
|
+ for (Constraint constraint : constraints) {
|
|
|
|
|
+ constraint.affectAnimation(ad.skeleton, boneAnimation);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ anims.put(boneAnimation.getName(), boneAnimation);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // getting meshes
|
|
|
|
|
+ Mesh[] meshes = null;
|
|
|
|
|
+ List<Mesh> meshesList = new ArrayList<Mesh>();
|
|
|
|
|
+ List<Spatial> children = node.getChildren();
|
|
|
|
|
+ for (Spatial child : children) {
|
|
|
|
|
+ if (child instanceof Geometry) {
|
|
|
|
|
+ meshesList.add(((Geometry) child).getMesh());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (meshesList.size() > 0) {
|
|
|
|
|
+ meshes = meshesList.toArray(new Mesh[meshesList.size()]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // applying the control to the node
|
|
|
|
|
+ SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
|
|
|
|
|
+ AnimControl control = node.getControl(AnimControl.class);
|
|
|
|
|
+
|
|
|
|
|
+ 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, BoneAnimation> animations = new HashMap<String, BoneAnimation>();
|
|
|
|
|
+ for (String animationName : control.getAnimationNames()) {
|
|
|
|
|
+ animations.put(animationName, control.getAnim(animationName));
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Entry<String, BoneAnimation> animEntry : anims.entrySet()) {
|
|
|
|
|
+ BoneAnimation ba = 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);
|
|
|
|
|
+ node.addControl(control);
|
|
|
|
|
+ node.addControl(skeletonControl);
|
|
|
|
|
+ }
|
|
|
|
|
+ return node;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method applies the array modifier to the node.
|
|
|
|
|
+ * @param node
|
|
|
|
|
+ * the object the modifier will be applied to
|
|
|
|
|
+ * @param modifier
|
|
|
|
|
+ * the modifier to be applied
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @return object node with array modifier applied
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ protected Node applyArrayModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
|
|
+ Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();
|
|
|
|
|
+ int fittype = ((Number) modifierData.get("fittype")).intValue();
|
|
|
|
|
+ float[] offset = (float[]) modifierData.get("offset");
|
|
|
|
|
+ if (offset == null) {// the node will be repeated several times in the same place
|
|
|
|
|
+ offset = new float[] { 0.0f, 0.0f, 0.0f };
|
|
|
|
|
+ }
|
|
|
|
|
+ float[] scale = (float[]) modifierData.get("scale");
|
|
|
|
|
+ if (scale == null) {// the node will be repeated several times in the same place
|
|
|
|
|
+ scale = new float[] { 0.0f, 0.0f, 0.0f };
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // getting bounding box
|
|
|
|
|
+ node.updateModelBound();
|
|
|
|
|
+ BoundingVolume boundingVolume = node.getWorldBound();
|
|
|
|
|
+ if (boundingVolume instanceof BoundingBox) {
|
|
|
|
|
+ scale[0] *= ((BoundingBox) boundingVolume).getXExtent() * 2.0f;
|
|
|
|
|
+ scale[1] *= ((BoundingBox) boundingVolume).getYExtent() * 2.0f;
|
|
|
|
|
+ scale[2] *= ((BoundingBox) boundingVolume).getZExtent() * 2.0f;
|
|
|
|
|
+ } else if (boundingVolume instanceof BoundingSphere) {
|
|
|
|
|
+ float radius = ((BoundingSphere) boundingVolume).getRadius();
|
|
|
|
|
+ scale[0] *= radius * 2.0f;
|
|
|
|
|
+ scale[1] *= radius * 2.0f;
|
|
|
|
|
+ scale[2] *= radius * 2.0f;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new IllegalStateException("Unknown bounding volume type: " + boundingVolume.getClass().getName());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // adding object's offset
|
|
|
|
|
+ float[] objectOffset = new float[] { 0.0f, 0.0f, 0.0f };
|
|
|
|
|
+ Pointer pOffsetObject = (Pointer) modifierData.get("offsetob");
|
|
|
|
|
+ if (pOffsetObject != null) {
|
|
|
|
|
+ FileBlockHeader offsetObjectBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
|
|
|
|
|
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
+ try {// we take the structure in case the object was not yet loaded
|
|
|
|
|
+ Structure offsetStructure = offsetObjectBlock.getStructure(dataRepository);
|
|
|
|
|
+ Vector3f translation = objectHelper.getTransformation(offsetStructure).getTranslation();
|
|
|
|
|
+ objectOffset[0] = translation.x;
|
|
|
|
|
+ objectOffset[1] = translation.y;
|
|
|
|
|
+ objectOffset[2] = translation.z;
|
|
|
|
|
+ } catch (BlenderFileException e) {
|
|
|
|
|
+ LOGGER.warning("Problems in blender file structure! Object offset cannot be applied! The problem: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // getting start and end caps
|
|
|
|
|
+ Node[] caps = new Node[] { null, null };
|
|
|
|
|
+ Pointer[] pCaps = new Pointer[] { (Pointer) modifierData.get("startcap"), (Pointer) modifierData.get("endcap") };
|
|
|
|
|
+ for (int i = 0; i < pCaps.length; ++i) {
|
|
|
|
|
+ if (pCaps[i] != null) {
|
|
|
|
|
+ caps[i] = (Node) dataRepository.getLoadedFeature(pCaps[i].getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
|
|
|
|
|
+ if (caps[i] != null) {
|
|
|
|
|
+ caps[i] = (Node) caps[i].clone();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ FileBlockHeader capBlock = dataRepository.getFileBlock(pOffsetObject.getOldMemoryAddress());
|
|
|
|
|
+ try {// we take the structure in case the object was not yet loaded
|
|
|
|
|
+ Structure capStructure = capBlock.getStructure(dataRepository);
|
|
|
|
|
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
+ caps[i] = (Node) objectHelper.toObject(capStructure, dataRepository);
|
|
|
|
|
+ if (caps[i] == null) {
|
|
|
|
|
+ LOGGER.warning("Cap object '" + capStructure.getName() + "' couldn't be loaded!");
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (BlenderFileException e) {
|
|
|
|
|
+ LOGGER.warning("Problems in blender file structure! Cap object cannot be applied! The problem: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Vector3f translationVector = new Vector3f(offset[0] + scale[0] + objectOffset[0], offset[1] + scale[1] + objectOffset[1], offset[2] + scale[2] + objectOffset[2]);
|
|
|
|
|
+
|
|
|
|
|
+ // getting/calculating repeats amount
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ if (fittype == 0) {// Fixed count
|
|
|
|
|
+ count = ((Number) modifierData.get("count")).intValue() - 1;
|
|
|
|
|
+ } else if (fittype == 1) {// Fixed length
|
|
|
|
|
+ float length = ((Number) modifierData.get("length")).floatValue();
|
|
|
|
|
+ if (translationVector.length() > 0.0f) {
|
|
|
|
|
+ count = (int) (length / translationVector.length()) - 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (fittype == 2) {// Fit curve
|
|
|
|
|
+ LOGGER.warning("Fit curve mode in array modifier not yet implemented!");// TODO: implement fit curve
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new IllegalStateException("Unknown fit type: " + fittype);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // adding translated nodes and caps
|
|
|
|
|
+ if (count > 0) {
|
|
|
|
|
+ Node[] arrayNodes = new Node[count];
|
|
|
|
|
+ Vector3f newTranslation = node.getLocalTranslation().clone();
|
|
|
|
|
+ for (int i = 0; i < count; ++i) {
|
|
|
|
|
+ newTranslation.addLocal(translationVector);
|
|
|
|
|
+ Node nodeClone = (Node) node.clone();
|
|
|
|
|
+ nodeClone.setLocalTranslation(newTranslation);
|
|
|
|
|
+ arrayNodes[i] = nodeClone;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Node nodeClone : arrayNodes) {
|
|
|
|
|
+ node.attachChild(nodeClone);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (caps[0] != null) {
|
|
|
|
|
+ caps[0].getLocalTranslation().set(node.getLocalTranslation()).subtractLocal(translationVector);
|
|
|
|
|
+ node.attachChild(caps[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (caps[1] != null) {
|
|
|
|
|
+ caps[1].getLocalTranslation().set(newTranslation).addLocal(translationVector);
|
|
|
|
|
+ node.attachChild(caps[1]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return node;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This method applies the mirror modifier to the node.
|
|
|
|
|
+ * @param node
|
|
|
|
|
+ * the object the modifier will be applied to
|
|
|
|
|
+ * @param modifier
|
|
|
|
|
+ * the modifier to be applied
|
|
|
|
|
+ * @param dataRepository
|
|
|
|
|
+ * the data repository
|
|
|
|
|
+ * @return object node with mirror modifier applied
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
protected Node applyMirrorModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
protected Node applyMirrorModifierData(Node node, Modifier modifier, DataRepository dataRepository) {
|
|
|
- Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();
|
|
|
|
|
- int flag = ((Number)modifierData.get("flag")).intValue();
|
|
|
|
|
- float[] mirrorFactor = new float[] {
|
|
|
|
|
- (flag & 0x08) != 0 ? -1.0f : 1.0f,
|
|
|
|
|
- (flag & 0x10) != 0 ? -1.0f : 1.0f,
|
|
|
|
|
- (flag & 0x20) != 0 ? -1.0f : 1.0f
|
|
|
|
|
- };
|
|
|
|
|
- float[] center = new float[] {0.0f, 0.0f, 0.0f};
|
|
|
|
|
- float tolerance = ((Number)modifierData.get("tolerance")).floatValue();
|
|
|
|
|
-
|
|
|
|
|
- List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
|
|
|
|
|
- for(int mirrorIndex = 0;mirrorIndex<3;++mirrorIndex) {
|
|
|
|
|
- if(mirrorFactor[mirrorIndex] == -1.0f) {
|
|
|
|
|
- for (Spatial spatial : node.getChildren()) {
|
|
|
|
|
- if (spatial instanceof Geometry) {
|
|
|
|
|
- Mesh mesh = ((Geometry) spatial).getMesh();
|
|
|
|
|
- Mesh clone = mesh.deepClone();
|
|
|
|
|
-
|
|
|
|
|
- VertexBuffer position = clone.getBuffer(Type.Position);
|
|
|
|
|
- VertexBuffer bindPosePosition = clone.getBuffer(Type.BindPosePosition);
|
|
|
|
|
- FloatBuffer positionBuffer = (FloatBuffer) position.getData();
|
|
|
|
|
- FloatBuffer bindPosePositionBuffer = (FloatBuffer) bindPosePosition.getData();
|
|
|
|
|
- positionBuffer.rewind();
|
|
|
|
|
- bindPosePositionBuffer.rewind();
|
|
|
|
|
- for(int i=mirrorIndex;i<positionBuffer.limit();i+=3) {
|
|
|
|
|
- float value = positionBuffer.get(i);
|
|
|
|
|
- positionBuffer.put(i, Math.abs(value) <= tolerance ? 0.0f : -value);
|
|
|
|
|
-
|
|
|
|
|
- value = bindPosePositionBuffer.get(i);
|
|
|
|
|
- bindPosePositionBuffer.put(i, Math.abs(value) <= tolerance ? 0.0f : -value);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Geometry geometry = new Geometry(null, clone);
|
|
|
|
|
- geometry.setMaterial(((Geometry) spatial).getMaterial());
|
|
|
|
|
- geometriesToAdd.add(geometry);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //adding meshes to node
|
|
|
|
|
- for(Geometry geometry : geometriesToAdd) {
|
|
|
|
|
- node.attachChild(geometry);
|
|
|
|
|
- }
|
|
|
|
|
- geometriesToAdd.clear();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return node;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * This class clones the bone animation data.
|
|
|
|
|
- * @param source
|
|
|
|
|
- * the source that is to be cloned
|
|
|
|
|
- * @return the copy of the given bone animation
|
|
|
|
|
- */
|
|
|
|
|
- protected BoneAnimation cloneBoneAnimation(BoneAnimation source) {
|
|
|
|
|
- BoneAnimation result = new BoneAnimation(source.getName(), source.getLength());
|
|
|
|
|
-
|
|
|
|
|
- //copying tracks and applying constraints
|
|
|
|
|
- BoneTrack[] sourceTracks = source.getTracks();
|
|
|
|
|
- BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length];
|
|
|
|
|
- for (int i = 0; i < sourceTracks.length; ++i) {
|
|
|
|
|
- int tablesLength = sourceTracks[i].getTimes().length;
|
|
|
|
|
-
|
|
|
|
|
- Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();
|
|
|
|
|
- Quaternion[] sourceRotations = sourceTracks[i].getRotations();
|
|
|
|
|
- Vector3f[] sourceScales = sourceTracks[i].getScales();
|
|
|
|
|
-
|
|
|
|
|
- Vector3f[] translations = new Vector3f[tablesLength];
|
|
|
|
|
- Quaternion[] rotations = new Quaternion[tablesLength];
|
|
|
|
|
- Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];
|
|
|
|
|
- for (int j = 0; j < tablesLength; ++j) {
|
|
|
|
|
- translations[j] = sourceTranslations[j].clone();
|
|
|
|
|
- rotations[j] = sourceRotations[j].clone();
|
|
|
|
|
- if (sourceScales != null) {//only scales may not be applied
|
|
|
|
|
- scales[j] = sourceScales[j].clone();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),//times do not change, no need to clone them,
|
|
|
|
|
- translations, rotations, scales);
|
|
|
|
|
- }
|
|
|
|
|
- result.setTracks(boneTracks);
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 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()]));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();
|
|
|
|
|
+ int flag = ((Number) modifierData.get("flag")).intValue();
|
|
|
|
|
+ float[] mirrorFactor = new float[] {
|
|
|
|
|
+ (flag & 0x08) != 0 ? -1.0f : 1.0f,
|
|
|
|
|
+ (flag & 0x10) != 0 ? -1.0f : 1.0f,
|
|
|
|
|
+ (flag & 0x20) != 0 ? -1.0f : 1.0f
|
|
|
|
|
+ };
|
|
|
|
|
+ float[] center = new float[] { 0.0f, 0.0f, 0.0f };
|
|
|
|
|
+ Pointer pObject = (Pointer) modifierData.get("mirrorob");
|
|
|
|
|
+ if (pObject != null) {
|
|
|
|
|
+ Structure objectStructure;
|
|
|
|
|
+ try {
|
|
|
|
|
+ objectStructure = pObject.fetchData(dataRepository.getInputStream()).get(0);
|
|
|
|
|
+ ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
|
|
|
|
|
+ Node object = (Node) objectHelper.toObject(objectStructure, dataRepository);
|
|
|
|
|
+ if (object != null) {
|
|
|
|
|
+ Vector3f translation = object.getWorldTranslation();
|
|
|
|
|
+ center[0] = translation.x;
|
|
|
|
|
+ center[1] = translation.y;
|
|
|
|
|
+ center[2] = translation.z;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (BlenderFileException e) {
|
|
|
|
|
+ LOGGER.severe("Cannot load mirror's reference object. Cause: " + e.getLocalizedMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
|
|
|
|
|
+ boolean mirrorU = (flag & 0x01) != 0;
|
|
|
|
|
+ boolean mirrorV = (flag & 0x02) != 0;
|
|
|
|
|
+// boolean mirrorVGroup = (flag & 0x20) != 0;
|
|
|
|
|
+
|
|
|
|
|
+ List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
|
|
|
|
|
+ for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
|
|
|
|
|
+ if (mirrorFactor[mirrorIndex] == -1.0f) {
|
|
|
|
|
+ for (Spatial spatial : node.getChildren()) {
|
|
|
|
|
+ if (spatial instanceof Geometry) {
|
|
|
|
|
+ Mesh mesh = ((Geometry) spatial).getMesh();
|
|
|
|
|
+ Mesh clone = mesh.deepClone();
|
|
|
|
|
+
|
|
|
|
|
+ // getting buffers
|
|
|
|
|
+ FloatBuffer position = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
|
|
|
+ FloatBuffer bindPosePosition = (FloatBuffer) mesh.getBuffer(Type.BindPosePosition).getData();
|
|
|
|
|
+
|
|
|
|
|
+ FloatBuffer clonePosition = (FloatBuffer) clone.getBuffer(Type.Position).getData();
|
|
|
|
|
+ FloatBuffer cloneBindPosePosition = (FloatBuffer) clone.getBuffer(Type.BindPosePosition).getData();
|
|
|
|
|
+ FloatBuffer cloneNormals = (FloatBuffer) clone.getBuffer(Type.Normal).getData();
|
|
|
|
|
+ FloatBuffer cloneBindPoseNormals = (FloatBuffer) clone.getBuffer(Type.BindPoseNormal).getData();
|
|
|
|
|
+
|
|
|
|
|
+ // modyfying data
|
|
|
|
|
+ for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) {
|
|
|
|
|
+ float value = clonePosition.get(i);
|
|
|
|
|
+ float d = center[mirrorIndex] - value;
|
|
|
|
|
+
|
|
|
|
|
+ if (Math.abs(d) <= tolerance) {
|
|
|
|
|
+ clonePosition.put(i, center[mirrorIndex]);
|
|
|
|
|
+ cloneBindPosePosition.put(i, center[mirrorIndex]);
|
|
|
|
|
+ position.put(i, center[mirrorIndex]);
|
|
|
|
|
+ bindPosePosition.put(i, center[mirrorIndex]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ clonePosition.put(i, value + 2.0f * d);
|
|
|
|
|
+ cloneBindPosePosition.put(i, value + 2.0f * d);
|
|
|
|
|
+ }
|
|
|
|
|
+ cloneNormals.put(i, -cloneNormals.get(i));
|
|
|
|
|
+ cloneBindPoseNormals.put(i, -cloneNormals.get(i));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(mirrorU) {
|
|
|
|
|
+ FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
|
|
|
+ for(int i=0;i<cloneUVs.limit();i+=2) {
|
|
|
|
|
+ cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if(mirrorV) {
|
|
|
|
|
+ FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
|
|
|
+ for(int i=1;i<cloneUVs.limit();i+=2) {
|
|
|
|
|
+ cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Geometry geometry = new Geometry(null, clone);
|
|
|
|
|
+ geometry.setMaterial(((Geometry) spatial).getMaterial());
|
|
|
|
|
+ geometriesToAdd.add(geometry);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // adding meshes to node
|
|
|
|
|
+ for (Geometry geometry : geometriesToAdd) {
|
|
|
|
|
+ node.attachChild(geometry);
|
|
|
|
|
+ }
|
|
|
|
|
+ geometriesToAdd.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return node;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This class clones the bone animation data.
|
|
|
|
|
+ * @param source
|
|
|
|
|
+ * the source that is to be cloned
|
|
|
|
|
+ * @return the copy of the given bone animation
|
|
|
|
|
+ */
|
|
|
|
|
+ protected BoneAnimation cloneBoneAnimation(BoneAnimation source) {
|
|
|
|
|
+ BoneAnimation result = new BoneAnimation(source.getName(), source.getLength());
|
|
|
|
|
+
|
|
|
|
|
+ // copying tracks and applying constraints
|
|
|
|
|
+ BoneTrack[] sourceTracks = source.getTracks();
|
|
|
|
|
+ BoneTrack[] boneTracks = new BoneTrack[sourceTracks.length];
|
|
|
|
|
+ for (int i = 0; i < sourceTracks.length; ++i) {
|
|
|
|
|
+ int tablesLength = sourceTracks[i].getTimes().length;
|
|
|
|
|
+
|
|
|
|
|
+ Vector3f[] sourceTranslations = sourceTracks[i].getTranslations();
|
|
|
|
|
+ Quaternion[] sourceRotations = sourceTracks[i].getRotations();
|
|
|
|
|
+ Vector3f[] sourceScales = sourceTracks[i].getScales();
|
|
|
|
|
+
|
|
|
|
|
+ Vector3f[] translations = new Vector3f[tablesLength];
|
|
|
|
|
+ Quaternion[] rotations = new Quaternion[tablesLength];
|
|
|
|
|
+ Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];
|
|
|
|
|
+ for (int j = 0; j < tablesLength; ++j) {
|
|
|
|
|
+ translations[j] = sourceTranslations[j].clone();
|
|
|
|
|
+ rotations[j] = sourceRotations[j].clone();
|
|
|
|
|
+ if (sourceScales != null) {// only scales may not be applied
|
|
|
|
|
+ scales[j] = sourceScales[j].clone();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),// times do not change, no need
|
|
|
|
|
+ // to clone them,
|
|
|
|
|
+ translations, rotations, scales);
|
|
|
|
|
+ }
|
|
|
|
|
+ result.setTracks(boneTracks);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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()]));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|