From b8826716b123ea96b177344892004c22f3144ccc Mon Sep 17 00:00:00 2001 From: "sha..rd" Date: Wed, 13 Jul 2011 00:30:57 +0000 Subject: [PATCH] * Fixed syntax error in ModifierHelper * Other minor fixes git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7855 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../blender/helpers/v249/ModifierHelper.java | 1331 +++++++++-------- .../plugins/blender/structures/Modifier.java | 16 +- engine/src/core/com/jme3/asset/AssetKey.java | 6 +- 3 files changed, 684 insertions(+), 669 deletions(-) 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 921e5cb5c..312909625 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 @@ -40,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.animation.AnimControl; @@ -84,668 +85,672 @@ import com.jme3.scene.shape.Curve; */ 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.OBJECT_ANIMATION_MODIFIER_DATA.equals(modifier.getType())) { - return this.applyObjectAnimationModifier(node, modifier, dataRepository); - } else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { - return this.applyArrayModifierData(node, modifier, dataRepository); - } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { - 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 - Pointer pCurveOb = (Pointer) modifier.getFieldValue("curve_ob"); - float length = 0; - if(pCurveOb.isNotNull()) { - Structure curveStructure = pCurveOb.fetchData(dataRepository.getInputStream()).get(0); - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - Node curveObject = (Node)objectHelper.toObject(curveStructure, dataRepository); - Set referencesToCurveLengths = new HashSet(curveObject.getChildren().size()); - for(Spatial spatial : curveObject.getChildren()) { - if(spatial instanceof Geometry) { - Mesh mesh = ((Geometry) spatial).getMesh(); - if(mesh instanceof Curve) { - length += ((Curve) mesh).getLength(); - } else { - //if bevel object has several parts then each mesh will have the same reference - //to length value (and we should use only one) - Number curveLength = spatial.getUserData("curveLength"); - if(curveLength!=null && !referencesToCurveLengths.contains(curveLength)) { - length += curveLength.floatValue(); - referencesToCurveLengths.add(curveLength); - } - } - } - } - } - params.put("length", Float.valueOf(length)); - params.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH - 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.isNotNull()) { - params.put("offsetob", pOffsetObject); - } - } - - // start cap and end cap - Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap"); - if (pStartCap.isNotNull()) { - params.put("startcap", pStartCap); - } - Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap"); - if (pEndCap.isNotNull()) { - params.put("endcap", pEndCap); - } - loadedModifier = params; - } else 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.isNotNull()) { - 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.isNotNull()) { - 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); - } - modifierAdditionalData = armatureObject.getOldMemoryAddress(); - ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); - - // changing bones matrices so that they fit the current object (that is why we need a copy of a skeleton) - Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); - Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); - Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); - Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); - - //setting the bones structure inside the skeleton (thus completing its loading) - Skeleton skeleton = new Skeleton(bones); - dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton); - - String objectName = objectStructure.getName(); - Set 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.isNotNull()) { - 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; - } - } - - //at the end read object's animation modifier - Modifier objectAnimationModifier = this.readObjectAnimation(objectStructure, dataRepository); - if(objectAnimationModifier != null) { - dataRepository.addModifier(objectStructure.getOldMemoryAddress(), - objectAnimationModifier.getType(), - objectAnimationModifier.getJmeModifierRepresentation(), - objectAnimationModifier.getAdditionalData()); - } - } - - /** - * This method reads animation of the object itself (without bones) and stores it as an ArmatureModifierData - * modifier. The animation is returned as a modifier. It should be later applied regardless other modifiers. The - * reason for this is that object may not have modifiers added but it's animation should be working. - * @param objectStructure - * the structure of the object - * @param dataRepository - * the data repository - * @return animation modifier is returned, it should be separately applied when the object is loaded - * @throws BlenderFileException - * this exception is thrown when the blender file is somehow corrupted - */ - protected Modifier readObjectAnimation(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { - Pointer pIpo = (Pointer)objectStructure.getFieldValue("ipo"); - if(pIpo.isNotNull()) { - //check if there is an action name connected with this ipo - String objectAnimationName = null; - List actionBlocks = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); - for(FileBlockHeader actionBlock : actionBlocks) { - Structure action = actionBlock.getStructure(dataRepository); - List actionChannels = ((Structure)action.getFieldValue("chanbase")).evaluateListBase(dataRepository); - if(actionChannels.size() == 1) {//object's animtion action has only one channel - Pointer pChannelIpo = (Pointer)actionChannels.get(0).getFieldValue("ipo"); - if(pChannelIpo.equals(pIpo)) { - objectAnimationName = action.getName(); - break; - } - } - } - - String objectName = objectStructure.getName(); - if(objectAnimationName == null) {//set the object's animation name to object's name - objectAnimationName = objectName; - } - - IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); - Structure ipoStructure = pIpo.fetchData(dataRepository.getInputStream()).get(0); - Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); - int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, objectAnimationName); - if(animationFrames == null) {//if the name was created here there are no frames set for the animation - animationFrames = new int[] {1, ipo.getLastFrame()}; - } - int fps = dataRepository.getBlenderKey().getFps(); - float start = (float)animationFrames[0] / (float)fps; - float stop = (float)animationFrames[1] / (float)fps; - - //calculating track for the only bone in this skeleton - BoneTrack[] tracks = new BoneTrack[1]; - tracks[0] = ipo.calculateTrack(0, animationFrames[0], animationFrames[1], fps); - - BoneAnimation boneAnimation = new BoneAnimation(objectAnimationName, stop - start); - boneAnimation.setTracks(tracks); - ArrayList animations = new ArrayList(1); - animations.add(boneAnimation); - - //preparing the object's bone - ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); - Transform t = objectHelper.getTransformation(objectStructure); - Bone bone = new Bone(null); - bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale()); - - return new Modifier(Modifier.OBJECT_ANIMATION_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[] {bone}), animations), objectStructure.getOldMemoryAddress()); - } - return null; - } - - /** - * 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 = animList.get(i).clone(); - - // 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; - } - - protected Node applyObjectAnimationModifier(Node node, Modifier modifier, DataRepository dataRepository) { - AnimData ad = (AnimData) modifier.getJmeModifierRepresentation(); - ad.skeleton.getBone(0).setAttachNode(node); - return this.applyArmatureModifierData(node, modifier, dataRepository); - } - - /** - * This method applies the array modifier to the node. - * @param node - * 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 - throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!"); - } else { - throw new IllegalStateException("Unknown fit type: " + fittype); - } - - // adding translated nodes and caps - if (count > 0) { - Node[] arrayNodes = new Node[count]; - Vector3f newTranslation = new Vector3f(); - 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 }; - 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; + 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.OBJECT_ANIMATION_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyObjectAnimationModifier(node, modifier, dataRepository); + } else if (Modifier.ARRAY_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyArrayModifierData(node, modifier, dataRepository); + } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyParticleSystemModifierData(node, modifier, dataRepository); + } else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifier.getType())) { + return this.applyMirrorModifierData(node, modifier, dataRepository); + } else { + LOGGER.log(Level.WARNING, "Modifier: {0} not yet implemented!!!", modifier.getType()); + 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 + Pointer pCurveOb = (Pointer) modifier.getFieldValue("curve_ob"); + float length = 0; + if (pCurveOb.isNotNull()) { + Structure curveStructure = pCurveOb.fetchData(dataRepository.getInputStream()).get(0); + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + Node curveObject = (Node) objectHelper.toObject(curveStructure, dataRepository); + Set referencesToCurveLengths = new HashSet(curveObject.getChildren().size()); + for (Spatial spatial : curveObject.getChildren()) { + if (spatial instanceof Geometry) { + Mesh mesh = ((Geometry) spatial).getMesh(); + if (mesh instanceof Curve) { + length += ((Curve) mesh).getLength(); + } else { + //if bevel object has several parts then each mesh will have the same reference + //to length value (and we should use only one) + Number curveLength = spatial.getUserData("curveLength"); + if (curveLength != null && !referencesToCurveLengths.contains(curveLength)) { + length += curveLength.floatValue(); + referencesToCurveLengths.add(curveLength); + } + } + } + } + } + params.put("length", Float.valueOf(length)); + params.put("fittype", Integer.valueOf(1));// treat it like FIXED LENGTH + 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.isNotNull()) { + params.put("offsetob", pOffsetObject); + } + } + + // start cap and end cap + Pointer pStartCap = (Pointer) modifier.getFieldValue("start_cap"); + if (pStartCap.isNotNull()) { + params.put("startcap", pStartCap); + } + Pointer pEndCap = (Pointer) modifier.getFieldValue("end_cap"); + if (pEndCap.isNotNull()) { + params.put("endcap", pEndCap); + } + loadedModifier = params; + } else 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.isNotNull()) { + 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.isNotNull()) { + 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); + } + modifierAdditionalData = armatureObject.getOldMemoryAddress(); + ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class); + + // changing bones matrices so that they fit the current object (that is why we need a copy of a skeleton) + Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject); + Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert(); + Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix); + Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation); + + //setting the bones structure inside the skeleton (thus completing its loading) + Skeleton skeleton = new Skeleton(bones); + dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton); + + String objectName = objectStructure.getName(); + Set 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.log(Level.WARNING, "Unsupported modifier type: {0}", modifier.getType()); + } + } else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifier.getType())) {// ****************PARTICLES MODIFIER + Pointer pParticleSystem = (Pointer) modifier.getFieldValue("psys"); + if (pParticleSystem.isNotNull()) { + 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; + } + } + + //at the end read object's animation modifier + Modifier objectAnimationModifier = this.readObjectAnimation(objectStructure, dataRepository); + if (objectAnimationModifier != null) { + dataRepository.addModifier(objectStructure.getOldMemoryAddress(), + objectAnimationModifier.getType(), + objectAnimationModifier.getJmeModifierRepresentation(), + objectAnimationModifier.getAdditionalData()); + } + } + + /** + * This method reads animation of the object itself (without bones) and stores it as an ArmatureModifierData + * modifier. The animation is returned as a modifier. It should be later applied regardless other modifiers. The + * reason for this is that object may not have modifiers added but it's animation should be working. + * @param objectStructure + * the structure of the object + * @param dataRepository + * the data repository + * @return animation modifier is returned, it should be separately applied when the object is loaded + * @throws BlenderFileException + * this exception is thrown when the blender file is somehow corrupted + */ + protected Modifier readObjectAnimation(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException { + Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); + if (pIpo.isNotNull()) { + //check if there is an action name connected with this ipo + String objectAnimationName = null; + List actionBlocks = dataRepository.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); + for (FileBlockHeader actionBlock : actionBlocks) { + Structure action = actionBlock.getStructure(dataRepository); + List actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(dataRepository); + if (actionChannels.size() == 1) {//object's animtion action has only one channel + Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo"); + if (pChannelIpo.equals(pIpo)) { + objectAnimationName = action.getName(); + break; + } + } + } + + String objectName = objectStructure.getName(); + if (objectAnimationName == null) {//set the object's animation name to object's name + objectAnimationName = objectName; + } + + IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class); + Structure ipoStructure = pIpo.fetchData(dataRepository.getInputStream()).get(0); + Ipo ipo = ipoHelper.createIpo(ipoStructure, dataRepository); + int[] animationFrames = dataRepository.getBlenderKey().getAnimationFrames(objectName, objectAnimationName); + if (animationFrames == null) {//if the name was created here there are no frames set for the animation + animationFrames = new int[]{1, ipo.getLastFrame()}; + } + int fps = dataRepository.getBlenderKey().getFps(); + float start = (float) animationFrames[0] / (float) fps; + float stop = (float) animationFrames[1] / (float) fps; + + //calculating track for the only bone in this skeleton + BoneTrack[] tracks = new BoneTrack[1]; + tracks[0] = ipo.calculateTrack(0, animationFrames[0], animationFrames[1], fps); + + BoneAnimation boneAnimation = new BoneAnimation(objectAnimationName, stop - start); + boneAnimation.setTracks(tracks); + ArrayList animations = new ArrayList(1); + animations.add(boneAnimation); + + //preparing the object's bone + ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class); + Transform t = objectHelper.getTransformation(objectStructure); + Bone bone = new Bone(null); + bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale()); + + return new Modifier(Modifier.OBJECT_ANIMATION_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[]{bone}), animations), objectStructure.getOldMemoryAddress()); + } + return null; + } + + /** + * 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 = animList.get(i).clone(); + + // 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; + } + + protected Node applyObjectAnimationModifier(Node node, Modifier modifier, DataRepository dataRepository) { + AnimData ad = (AnimData) modifier.getJmeModifierRepresentation(); + + // TODO: Why is this line here? Why is this needed? + // Remove if necessary. + //ad.skeleton.getBone(0).setAttachNode(node); + + return this.applyArmatureModifierData(node, modifier, dataRepository); + } + + /** + * 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.log(Level.WARNING, "Problems in blender file structure! Object offset cannot be applied! The problem: {0}", 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.log(Level.WARNING, "Cap object ''{0}'' couldn''t be loaded!", capStructure.getName()); + } + } catch (BlenderFileException e) { + LOGGER.log(Level.WARNING, "Problems in blender file structure! Cap object cannot be applied! The problem: {0}", 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 + throw new IllegalStateException("Fit curve should be transformed to Fixed Length array type!"); + } else { + throw new IllegalStateException("Unknown fit type: " + fittype); + } + + // adding translated nodes and caps + if (count > 0) { + Node[] arrayNodes = new Node[count]; + Vector3f newTranslation = new Vector3f(); + 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}; + 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.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", 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 = mesh.getFloatBuffer(Type.Position); - FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition); - - FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position); - FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition); - FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal); - FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal); - ShortBuffer cloneIndexes = (ShortBuffer) clone.getBuffer(Type.Index).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)); - - //modifying clone indexes - int vertexIndex = (i - mirrorIndex) / 3; - if(vertexIndex % 3 == 0) { - short index = cloneIndexes.get(vertexIndex + 2); - cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1)); - cloneIndexes.put(vertexIndex + 1, index); - } - } - - 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()])); - } + + 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 = mesh.getFloatBuffer(Type.Position); + FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition); + + FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position); + FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition); + FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal); + FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal); + ShortBuffer cloneIndexes = (ShortBuffer) clone.getBuffer(Type.Index).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)); + + //modifying clone indexes + int vertexIndex = (i - mirrorIndex) / 3; + if (vertexIndex % 3 == 0) { + short index = cloneIndexes.get(vertexIndex + 2); + cloneIndexes.put(vertexIndex + 2, cloneIndexes.get(vertexIndex + 1)); + cloneIndexes.put(vertexIndex + 1, index); + } + } + + 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 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 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()])); + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java index 6eb3cfb16..f66fe0e96 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/structures/Modifier.java @@ -7,16 +7,26 @@ package com.jme3.scene.plugins.blender.structures; * @author Marcin Roguski (Kaelthas) */ public class Modifier { + public static final String ARRAY_MODIFIER_DATA = "ArrayModifierData"; public static final String ARMATURE_MODIFIER_DATA = "ArmatureModifierData"; public static final String PARTICLE_MODIFIER_DATA = "ParticleSystemModifierData"; public static final String MIRROR_MODIFIER_DATA = "MirrorModifierData"; + public static final String OBJECT_ANIMATION_MODIFIER_DATA = "ObjectAnimationModifierData"; - /** Blender's type of modifier. */ + /** + * Blender's type of modifier. + */ private String type; - /** JME modifier representation object. */ + + /** + * JME modifier representation object. + */ private Object jmeModifierRepresentation; - /** Various additional data used by modifiers.*/ + + /** + * Various additional data used by modifiers. + */ private Object additionalData; /** diff --git a/engine/src/core/com/jme3/asset/AssetKey.java b/engine/src/core/com/jme3/asset/AssetKey.java index e906f139a..690d7a328 100644 --- a/engine/src/core/com/jme3/asset/AssetKey.java +++ b/engine/src/core/com/jme3/asset/AssetKey.java @@ -61,10 +61,10 @@ public class AssetKey implements Savable { protected static String getExtension(String name){ int idx = name.lastIndexOf('.'); //workaround for filenames ending with xml and another dot ending before that (my.mesh.xml) - if(name.toLowerCase().indexOf(".xml")==name.length()-4){ + if (name.toLowerCase().endsWith(".xml")) { idx = name.substring(0, idx).lastIndexOf('.'); - if(idx==-1){ - idx=name.lastIndexOf('.'); + if (idx == -1) { + idx = name.lastIndexOf('.'); } } if (idx <= 0 || idx == name.length() - 1)