From 32b79324a96d33bf796f311352d482fa55adf706 Mon Sep 17 00:00:00 2001 From: "Kae..pl" Date: Sun, 20 Oct 2013 14:45:33 +0000 Subject: [PATCH] Bugfix: fixed an issue with proper bone orientation in 3D space (as in blender bones have different local coordinates system than other features); this fix caused more models to be loaded properly and made the code more simple git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10840 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../blender/animations/ArmatureHelper.java | 9 +- .../blender/animations/BoneContext.java | 100 ++++++++++++------ .../scene/plugins/blender/animations/Ipo.java | 38 +++---- .../blender/modifiers/ArmatureModifier.java | 13 +-- .../plugins/blender/objects/ObjectHelper.java | 82 ++++++++------ 5 files changed, 139 insertions(+), 103 deletions(-) diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java index f5fc203de..8b26f30d5 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java @@ -41,7 +41,6 @@ import java.util.logging.Logger; import com.jme3.animation.Bone; import com.jme3.animation.BoneTrack; import com.jme3.animation.Skeleton; -import com.jme3.math.Matrix4f; import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.curves.BezierCurve; @@ -76,21 +75,25 @@ public class ArmatureHelper extends AbstractBlenderHelper { /** * This method builds the object's bones structure. * + * @param armatureObjectOMA + * the OMa of the armature node * @param boneStructure * the structure containing the bones' data * @param parent * the parent bone * @param result * the list where the newly created bone will be added + * @param spatialOMA + * the OMA of the spatial that will own the skeleton * @param blenderContext * the blender context * @throws BlenderFileException * an exception is thrown when there is problem with the blender * file */ - public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List result, Matrix4f objectToArmatureTransformation, BlenderContext blenderContext) throws BlenderFileException { + public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List result, Long spatialOMA, BlenderContext blenderContext) throws BlenderFileException { BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, blenderContext); - bc.buildBone(result, objectToArmatureTransformation, blenderContext); + bc.buildBone(result, spatialOMA, blenderContext); } /** diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java index 744f3a236..3613fc85d 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java @@ -8,7 +8,10 @@ import com.jme3.animation.Skeleton; import com.jme3.math.Matrix4f; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; import com.jme3.scene.plugins.blender.BlenderContext; +import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType; +import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.objects.ObjectHelper; @@ -19,27 +22,36 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper; * @author Marcin Roguski (Kaelthas) */ public class BoneContext { - private BlenderContext blenderContext; + // the flags of the bone + private static final int CONNECTED_TO_PARENT = 0x10; + + /** + * The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us). + * So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results. + */ + private static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1); + + private BlenderContext blenderContext; /** The OMA of the bone's armature object. */ - private Long armatureObjectOMA; + private Long armatureObjectOMA; /** The structure of the bone. */ - private Structure boneStructure; + private Structure boneStructure; /** Bone's name. */ - private String boneName; - /** The bone's armature matrix. */ - private Matrix4f armatureMatrix; + private String boneName; + /** The bone's flag. */ + private int flag; + /** The bone's matrix in world space. */ + private Matrix4f globalBoneMatrix; + /** The bone's matrix in the model space. */ + private Matrix4f boneMatrixInModelSpace; /** The parent context. */ - private BoneContext parent; + private BoneContext parent; /** The children of this context. */ - private List children = new ArrayList(); + private List children = new ArrayList(); /** Created bone (available after calling 'buildBone' method). */ - private Bone bone; - /** The bone's rest matrix. */ - private Matrix4f restMatrix; - /** Bone's total inverse transformation. */ - private Matrix4f inverseTotalTransformation; + private Bone bone; /** The length of the bone. */ - private float length; + private float length; /** * Constructor. Creates the basic set of bone's data. @@ -79,21 +91,26 @@ public class BoneContext { this.boneStructure = boneStructure; this.armatureObjectOMA = armatureObjectOMA; boneName = boneStructure.getFieldValue("name").toString(); + flag = ((Number) boneStructure.getFieldValue("flag")).intValue(); length = ((Number) boneStructure.getFieldValue("length")).floatValue(); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis()); - // compute the bone's rest matrix - restMatrix = armatureMatrix.clone(); - inverseTotalTransformation = restMatrix.invert(); - if (parent != null) { - restMatrix = parent.inverseTotalTransformation.mult(restMatrix); - } + // first get the bone matrix in its armature space + globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis()); + // then make sure it is rotated in a proper way to fit the jme bone transformation conventions + globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX); + + Spatial armature = (Spatial) blenderContext.getLoadedFeature(armatureObjectOMA, LoadedFeatureDataType.LOADED_FEATURE); + ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); + Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform()); + + // and now compute the final bone matrix in world space + globalBoneMatrix = armatureWorldMatrix.mult(globalBoneMatrix); // create the children List childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext); for (Structure child : childbase) { - this.children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext)); + children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext)); } blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this); @@ -104,31 +121,36 @@ public class BoneContext { * * @param bones * a list of bones where the newly created bone will be added - * @param objectToArmatureMatrix - * object to armature transformation matrix + * @param skeletonOwnerOma + * the spatial of the object that will own the skeleton * @param blenderContext * the blender context * @return newly created bone */ - public Bone buildBone(List bones, Matrix4f objectToArmatureMatrix, BlenderContext blenderContext) { + public Bone buildBone(List bones, Long skeletonOwnerOma, BlenderContext blenderContext) { Long boneOMA = boneStructure.getOldMemoryAddress(); bone = new Bone(boneName); bones.add(bone); blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone); + ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Vector3f poseLocation = restMatrix.toTranslationVector(); - Quaternion rotation = restMatrix.toRotationQuat().normalizeLocal(); - Vector3f scale = restMatrix.toScaleVector(); - if (parent == null) { - Quaternion rotationQuaternion = objectToArmatureMatrix.toRotationQuat().normalizeLocal(); - scale.multLocal(objectToArmatureMatrix.toScaleVector()); - rotationQuaternion.multLocal(poseLocation.addLocal(objectToArmatureMatrix.toTranslationVector())); - rotation.multLocal(rotationQuaternion); + Structure skeletonOwnerObjectStructure = (Structure) blenderContext.getLoadedFeature(skeletonOwnerOma, LoadedFeatureDataType.LOADED_STRUCTURE); + Matrix4f invertedObjectOwnerGlobalMatrix = objectHelper.getMatrix(skeletonOwnerObjectStructure, "imat", blenderContext.getBlenderKey().isFixUpAxis()); + if (objectHelper.isParent(skeletonOwnerOma, armatureObjectOMA)) { + boneMatrixInModelSpace = globalBoneMatrix.mult(invertedObjectOwnerGlobalMatrix); + } else { + boneMatrixInModelSpace = invertedObjectOwnerGlobalMatrix.mult(globalBoneMatrix); } + Matrix4f boneLocalMatrix = parent == null ? boneMatrixInModelSpace : parent.boneMatrixInModelSpace.invert().multLocal(boneMatrixInModelSpace); + + Vector3f poseLocation = parent == null || !this.is(CONNECTED_TO_PARENT) ? boneLocalMatrix.toTranslationVector() : new Vector3f(0, parent.length, 0); + Quaternion rotation = boneLocalMatrix.toRotationQuat().normalizeLocal(); + Vector3f scale = boneLocalMatrix.toScaleVector(); + bone.setBindTransforms(poseLocation, rotation, scale); for (BoneContext child : children) { - bone.addChild(child.buildBone(bones, objectToArmatureMatrix, blenderContext)); + bone.addChild(child.buildBone(bones, skeletonOwnerOma, blenderContext)); } return bone; @@ -168,4 +190,14 @@ public class BoneContext { public Skeleton getSkeleton() { return blenderContext.getSkeleton(armatureObjectOMA); } + + /** + * Tells if the bone is of specified property defined by its flag. + * @param flagMask + * the mask of the flag (constants defined in this class) + * @return true if the bone IS of specified proeprty and false otherwise + */ + private boolean is(int flagMask) { + return (flag & flagMask) != 0; + } } diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java index 4f6445794..9922a145a 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java @@ -1,5 +1,6 @@ package com.jme3.scene.plugins.blender.animations; +import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.animation.BoneTrack; @@ -148,6 +149,11 @@ public class Ipo { if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here } + int yIndex = 1, zIndex = 2; + if(spatialTrack && fixUpAxis) { + yIndex = 2; + zIndex = 1; + } // calculating track data for (int frame = startFrame; frame <= stopFrame; ++frame) { @@ -157,36 +163,30 @@ public class Ipo { for (int j = 0; j < bezierCurves.length; ++j) { double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); switch (bezierCurves[j].getType()) { - // LOCATION + // LOCATION case AC_LOC_X: translation[0] = (float) value; break; case AC_LOC_Y: - if (fixUpAxis) { - translation[2] = value == 0.0f ? 0 : (float) -value; - } else { - translation[1] = (float) value; - } + translation[yIndex] = (float) value; break; case AC_LOC_Z: - translation[fixUpAxis ? 1 : 2] = (float) value; + translation[zIndex] = (float) value; break; // ROTATION (used with object animation) - // the value here is in degrees divided by 10 (so in - // example: 9 = PI/2) case OB_ROT_X: objectRotation[0] = (float) value * degreeToRadiansFactor; break; case OB_ROT_Y: if (fixUpAxis) { - objectRotation[2] = value == 0.0f ? 0 : (float) -value * degreeToRadiansFactor; + objectRotation[yIndex] = value == 0.0f ? 0 : (float) -value * degreeToRadiansFactor; } else { - objectRotation[1] = (float) value * degreeToRadiansFactor; + objectRotation[yIndex] = (float) value * degreeToRadiansFactor; } break; case OB_ROT_Z: - objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor; + objectRotation[zIndex] = (float) value * degreeToRadiansFactor; break; // SIZE @@ -200,9 +200,7 @@ public class Ipo { scale[fixUpAxis ? 1 : 2] = (float) value; break; - // QUATERNION ROTATION (used with bone animation), dunno - // why but here we shouldn't check the - // spatialTrack flag value + // QUATERNION ROTATION (used with bone animation) case AC_QUAT_W: quaternionRotation[3] = (float) value; break; @@ -210,17 +208,13 @@ public class Ipo { quaternionRotation[0] = (float) value; break; case AC_QUAT_Y: - if (fixUpAxis) { - quaternionRotation[2] = value == 0.0f ? 0 : -(float) value; - } else { - quaternionRotation[1] = (float) value; - } + quaternionRotation[fixUpAxis ? 1 : 2] = (float) value; break; case AC_QUAT_Z: - quaternionRotation[fixUpAxis ? 1 : 2] = (float) value; + quaternionRotation[fixUpAxis ? 2 : 1] = (float) value; break; default: - LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType()); + LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType()); } } translations[index] = localQuaternionRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java index b4590bf87..130f9e07d 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java @@ -17,7 +17,6 @@ import com.jme3.animation.Bone; import com.jme3.animation.BoneTrack; import com.jme3.animation.Skeleton; import com.jme3.animation.SkeletonControl; -import com.jme3.math.Matrix4f; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; @@ -35,7 +34,6 @@ import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.meshes.MeshContext; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.util.BufferUtils; /** @@ -85,17 +83,10 @@ import com.jme3.util.BufferUtils; // load skeleton Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0); - - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - boolean fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); - Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", fixUpAxis); - Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "imat", fixUpAxis); - Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix); - List bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext); List bonesList = new ArrayList(); for (int i = 0; i < bonebase.size(); ++i) { - armatureHelper.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectToArmatureTransformation, blenderContext); + armatureHelper.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectStructure.getOldMemoryAddress(), blenderContext); } bonesList.add(0, new Bone("")); Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]); @@ -105,7 +96,7 @@ import com.jme3.util.BufferUtils; this.meshStructure = meshStructure; // read mesh indexes - this.meshOMA = meshStructure.getOldMemoryAddress(); + meshOMA = meshStructure.getOldMemoryAddress(); // read animations ArrayList animations = new ArrayList(); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java index 1156031cf..bd53e761b 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java @@ -44,6 +44,7 @@ import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; +import com.jme3.scene.Spatial; import com.jme3.scene.Spatial.CullHint; import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.BlenderContext; @@ -67,9 +68,9 @@ import com.jme3.scene.plugins.blender.modifiers.ModifierHelper; * @author Marcin Roguski (Kaelthas) */ public class ObjectHelper extends AbstractBlenderHelper { - private static final Logger LOGGER = Logger.getLogger(ObjectHelper.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ObjectHelper.class.getName()); - public static final String OMA_MARKER = "oma"; + public static final String OMA_MARKER = "oma"; /** * This constructor parses the given blender version and stores the result. @@ -98,28 +99,28 @@ public class ObjectHelper extends AbstractBlenderHelper { */ public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { LOGGER.fine("Loading blender object."); - + int type = ((Number) objectStructure.getFieldValue("type")).intValue(); ObjectType objectType = ObjectType.valueOf(type); LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType); - if(objectType == ObjectType.LAMP && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.LIGHTS)) { + if (objectType == ObjectType.LAMP && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.LIGHTS)) { LOGGER.fine("Lamps are not included in loading."); return null; } - if(objectType == ObjectType.CAMERA && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.CAMERAS)) { + if (objectType == ObjectType.CAMERA && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.CAMERAS)) { LOGGER.fine("Cameras are not included in loading."); return null; } - if(!blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.OBJECTS)) { + if (!blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.OBJECTS)) { LOGGER.fine("Objects are not included in loading."); return null; - } + } int lay = ((Number) objectStructure.getFieldValue("lay")).intValue(); - if((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { + if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { LOGGER.fine("The layer this object is located in is not included in loading."); return null; } - + LOGGER.fine("Checking if the object has not been already loaded."); Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE); if (loadedResult != null) { @@ -201,29 +202,29 @@ public class ObjectHelper extends AbstractBlenderHelper { if (result != null) { blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result); - + result.setLocalTransform(t); result.setCullHint(visible ? CullHint.Always : CullHint.Inherit); if (parent instanceof Node) { ((Node) parent).attachChild(result); } - + LOGGER.fine("Reading and applying object's modifiers."); ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class); Collection modifiers = modifierHelper.readModifiers(objectStructure, blenderContext); for (Modifier modifier : modifiers) { modifier.apply(result, blenderContext); } - + // I prefer do compute bounding box here than read it from the file result.updateModelBound(); - + LOGGER.fine("Applying markers (those will be removed before the final result is released)."); blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress()); - if(objectType == ObjectType.ARMATURE) { + if (objectType == ObjectType.ARMATURE) { blenderContext.addMarker(ArmatureHelper.ARMATURE_NODE_MARKER, result, Boolean.TRUE); } - + LOGGER.fine("Loading constraints connected with this object."); ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); constraintHelper.loadConstraints(objectStructure, blenderContext); @@ -241,6 +242,31 @@ public class ObjectHelper extends AbstractBlenderHelper { return result; } + /** + * Checks if the first given OMA points to a parent of the second one. + * The parent need not to be the direct one. This method should be called when we are sure + * that both of the features are alred loaded because it does not check it. + * The OMA's should point to a spatials, otherwise the function will throw ClassCastException. + * @param supposedParentOMA + * the OMA of the node that we suppose might be a parent of the second one + * @param spatialOMA + * the OMA of the scene's node + * @return true if the first given OMA points to a parent of the second one and false otherwise + */ + public boolean isParent(Long supposedParentOMA, Long spatialOMA) { + Spatial supposedParent = (Spatial) blenderContext.getLoadedFeature(supposedParentOMA, LoadedFeatureDataType.LOADED_FEATURE); + Spatial spatial = (Spatial) blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_FEATURE); + + Spatial parent = spatial.getParent(); + while (parent != null) { + if (parent.equals(supposedParent)) { + return true; + } + parent = parent.getParent(); + } + return false; + } + /** * This method calculates local transformation for the object. Parentage is * taken under consideration. @@ -320,7 +346,7 @@ public class ObjectHelper extends AbstractBlenderHelper { public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) { Matrix4f result = new Matrix4f(); DynamicArray obmat = (DynamicArray) structure.getFieldValue(matrixName); - //the matrix must be square + // the matrix must be square int rowAndColumnSize = Math.abs((int) Math.sqrt(obmat.getTotalSize())); for (int i = 0; i < rowAndColumnSize; ++i) { for (int j = 0; j < rowAndColumnSize; ++j) { @@ -361,29 +387,19 @@ public class ObjectHelper extends AbstractBlenderHelper { return result; } - + private static enum ObjectType { - EMPTY(0), - MESH(1), - CURVE(2), - SURF(3), - TEXT(4), - METABALL(5), - LAMP(10), - CAMERA(11), - WAVE(21), - LATTICE(22), - ARMATURE(25); - + EMPTY(0), MESH(1), CURVE(2), SURF(3), TEXT(4), METABALL(5), LAMP(10), CAMERA(11), WAVE(21), LATTICE(22), ARMATURE(25); + private int blenderTypeValue; - + private ObjectType(int blenderTypeValue) { this.blenderTypeValue = blenderTypeValue; } - + public static ObjectType valueOf(int blenderTypeValue) throws BlenderFileException { - for(ObjectType type : ObjectType.values()) { - if(type.blenderTypeValue == blenderTypeValue) { + for (ObjectType type : ObjectType.values()) { + if (type.blenderTypeValue == blenderTypeValue) { return type; } }