diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java index 3a11bc438..0bf2ffb55 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java @@ -60,7 +60,6 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.plugins.blender.data.FileBlockHeader; import com.jme3.scene.plugins.blender.data.Structure; @@ -81,537 +80,586 @@ import com.jme3.scene.plugins.ogre.AnimData; */ 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 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 params = new HashMap(); - - 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 offsetArray = (DynamicArray) 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 scaleArray = (DynamicArray) 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 params = new HashMap(); - - 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 animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); - if (animationNames != null && animationNames.size() > 0) { - ArrayList animations = new ArrayList(); - List 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 meshes = new ArrayList(); - 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 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 anims = new HashMap(); - 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 meshesList = new ArrayList(); - List 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 animations = new HashMap(); - for (String animationName : control.getAnimationNames()) { - animations.put(animationName, control.getAnim(animationName)); - } - for (Entry 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 modifierData = (Map) 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 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 params = new HashMap(); + + 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 offsetArray = (DynamicArray) 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 scaleArray = (DynamicArray) 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 params = new HashMap(); + + 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 animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName); + if (animationNames != null && animationNames.size() > 0) { + ArrayList animations = new ArrayList(); + List 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 meshes = new ArrayList(); + 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 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 anims = new HashMap(); + 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 meshesList = new ArrayList(); + List 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 animations = new HashMap(); + for (String animationName : control.getAnimationNames()) { + animations.put(animationName, control.getAnim(animationName)); + } + for (Entry 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 modifierData = (Map) 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) { - Map modifierData = (Map) 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 geometriesToAdd = new ArrayList(); - 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 bones = new ArrayList(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 modifierData = (Map) 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 geometriesToAdd = new ArrayList(); + 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 bones = new ArrayList(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()])); + } }