Prechádzať zdrojové kódy

Bugfix: fixed a bug that caused objects with negative scale to have improper transformations.
Bugfix: improved the matrix loading method (now the matrix is switched to Y-up axis withou loosing the information about negative scales).
Refactoring: decreased the amount of allocated matrices during transformation loading.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10942 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

Kae..pl 11 rokov pred
rodič
commit
b77f5a422c

+ 163 - 37
engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

@@ -31,6 +31,10 @@
  */
 package com.jme3.scene.plugins.blender.objects;
 
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
 import java.util.Collection;
 import java.util.List;
 import java.util.logging.Level;
@@ -39,13 +43,14 @@ import java.util.logging.Logger;
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
 import com.jme3.math.FastMath;
 import com.jme3.math.Matrix4f;
-import com.jme3.math.Quaternion;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh.Mode;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial.CullHint;
+import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
@@ -61,6 +66,7 @@ import com.jme3.scene.plugins.blender.lights.LightHelper;
 import com.jme3.scene.plugins.blender.meshes.MeshHelper;
 import com.jme3.scene.plugins.blender.modifiers.Modifier;
 import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
+import com.jme3.util.TempVars;
 
 /**
  * A class that is used in object calculations.
@@ -209,6 +215,14 @@ public class ObjectHelper extends AbstractBlenderHelper {
                 ((Node) parent).attachChild(result);
             }
 
+            if (result.getChildren() != null) {
+                for (Spatial child : result.getChildren()) {
+                    if (child instanceof Geometry) {
+                        this.flipMeshIfRequired((Geometry) child, child.getWorldScale());
+                    }
+                }
+            }
+
             LOGGER.fine("Reading and applying object's modifiers.");
             ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
             Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
@@ -242,6 +256,53 @@ public class ObjectHelper extends AbstractBlenderHelper {
         return result;
     }
 
+    /**
+     * The method flips the mesh if the scale is mirroring it. Mirroring scale has either 1 or all 3 factors negative.
+     * If two factors are negative then there is no mirroring because a rotation and translation can be found that will
+     * lead to the same transform when all scales are positive.
+     * 
+     * @param geometry
+     *            the geometry that is being flipped if necessary
+     * @param scale
+     *            the scale vector of the given geometry
+     */
+    private void flipMeshIfRequired(Geometry geometry, Vector3f scale) {
+        float s = scale.x * scale.y * scale.z;
+
+        if (s < 0 && geometry.getMesh() != null) {// negative s means that the scale is mirroring the object
+            FloatBuffer normals = geometry.getMesh().getFloatBuffer(Type.Normal);
+            if (normals != null) {
+                for (int i = 0; i < normals.limit(); i += 3) {
+                    if (scale.x < 0) {
+                        normals.put(i, -normals.get(i));
+                    }
+                    if (scale.y < 0) {
+                        normals.put(i + 1, -normals.get(i + 1));
+                    }
+                    if (scale.z < 0) {
+                        normals.put(i + 2, -normals.get(i + 2));
+                    }
+                }
+            }
+
+            if (geometry.getMesh().getMode() == Mode.Triangles) {// there is no need to flip the indexes for lines and points
+                LOGGER.finer("Flipping index order in triangle mesh.");
+                Buffer indexBuffer = geometry.getMesh().getBuffer(Type.Index).getData();
+                for (int i = 0; i < indexBuffer.limit(); i += 3) {
+                    if (indexBuffer instanceof ShortBuffer) {
+                        short index = ((ShortBuffer) indexBuffer).get(i + 1);
+                        ((ShortBuffer) indexBuffer).put(i + 1, ((ShortBuffer) indexBuffer).get(i + 2));
+                        ((ShortBuffer) indexBuffer).put(i + 2, index);
+                    } else {
+                        int index = ((IntBuffer) indexBuffer).get(i + 1);
+                        ((IntBuffer) indexBuffer).put(i + 1, ((IntBuffer) indexBuffer).get(i + 2));
+                        ((IntBuffer) indexBuffer).put(i + 2, index);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * 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
@@ -276,74 +337,139 @@ public class ObjectHelper extends AbstractBlenderHelper {
      * @return objects transformation relative to its parent
      */
     public Transform getTransformation(Structure objectStructure, BlenderContext blenderContext) {
-        Matrix4f parentInv = Matrix4f.IDENTITY.clone();
+        TempVars tempVars = TempVars.get();
+
+        Matrix4f parentInv = tempVars.tempMat4;
         Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
-        if(pParent.isNotNull()) {
+        if (pParent.isNotNull()) {
             Structure parentObjectStructure = (Structure) blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_STRUCTURE);
-            parentInv = this.getMatrix(parentObjectStructure, "obmat", fixUpAxis).invertLocal();
+            this.getMatrix(parentObjectStructure, "obmat", fixUpAxis, parentInv).invertLocal();
+        } else {
+            parentInv.loadIdentity();
         }
 
-        Matrix4f globalMatrix = this.getMatrix(objectStructure, "obmat", fixUpAxis);
+        Matrix4f globalMatrix = this.getMatrix(objectStructure, "obmat", fixUpAxis, tempVars.tempMat42);
         Matrix4f localMatrix = parentInv.multLocal(globalMatrix);
-        return new Transform(localMatrix.toTranslationVector(), localMatrix.toRotationQuat(), localMatrix.toScaleVector());
+
+        this.getSizeSignums(objectStructure, tempVars.vect1);
+
+        localMatrix.toTranslationVector(tempVars.vect2);
+        localMatrix.toRotationQuat(tempVars.quat1);
+        localMatrix.toScaleVector(tempVars.vect3);
+
+        Transform t = new Transform(tempVars.vect2, tempVars.quat1.normalizeLocal(), tempVars.vect3.multLocal(tempVars.vect1));
+        tempVars.release();
+        return t;
+    }
+
+    /**
+     * The method gets the signs of the scale factors and stores them properly in the given vector.
+     * @param objectStructure
+     *            the object's structure
+     * @param store
+     *            the vector where the result will be stored
+     */
+    @SuppressWarnings("unchecked")
+    private void getSizeSignums(Structure objectStructure, Vector3f store) {
+        DynamicArray<Number> size = (DynamicArray<Number>) objectStructure.getFieldValue("size");
+        if (fixUpAxis) {
+            store.x = Math.signum(size.get(0).floatValue());
+            store.y = Math.signum(size.get(2).floatValue());
+            store.z = Math.signum(size.get(1).floatValue());
+        } else {
+            store.x = Math.signum(size.get(0).floatValue());
+            store.y = Math.signum(size.get(1).floatValue());
+            store.z = Math.signum(size.get(2).floatValue());
+        }
     }
 
     /**
      * This method returns the matrix of a given name for the given structure.
      * It takes up axis into consideration.
      * 
+     * The method that moves the matrix from Z-up axis to Y-up axis space is as follows:
+     * - load the matrix directly from blender (it has the Z-up axis orientation)
+     * - switch the second and third rows in the matrix
+     * - switch the second and third column in the matrix
+     * - multiply the values in the third row by -1
+     * - multiply the values in the third column by -1
+     * 
+     * The result matrix is now in Y-up axis orientation.
+     * The procedure was discovered by experimenting but it looks like it's working :)
+     * The previous procedure transformet the loaded matrix into component (loc, rot, scale),
+     * switched several values and pu the back into the matrix.
+     * It worked fine until models with negative scale are used.
+     * The current method is not touched by that flaw.
+     * 
      * @param structure
      *            the structure with matrix data
      * @param matrixName
      *            the name of the matrix
      * @param fixUpAxis
      *            tells if the Y axis is a UP axis
+     * @param store
+     *            the matrix where the result will pe placed
      * @return the required matrix
      */
     @SuppressWarnings("unchecked")
-    public Matrix4f getMatrix(Structure structure, String matrixName, boolean fixUpAxis) {
-        Matrix4f result = new Matrix4f();
+    private Matrix4f getMatrix(Structure structure, String matrixName, boolean fixUpAxis, Matrix4f store) {
         DynamicArray<Number> obmat = (DynamicArray<Number>) structure.getFieldValue(matrixName);
         // 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) {
-                result.set(i, j, obmat.get(j, i).floatValue());
+                float value = obmat.get(j, i).floatValue();
+                if (Math.abs(value) <= FastMath.FLT_EPSILON) {
+                    value = 0;
+                }
+                store.set(i, j, value);
             }
         }
         if (fixUpAxis) {
-            Vector3f translation = result.toTranslationVector();
-            Quaternion rotation = result.toRotationQuat();
-            Vector3f scale = result.toScaleVector();
-
-            float y = translation.y;
-            translation.y = translation.z;
-            translation.z = y == 0 ? 0 : -y;
-
-            y = rotation.getY();
-            float z = rotation.getZ();
-            rotation.set(rotation.getX(), z, y == 0 ? 0 : -y, rotation.getW());
-
-            y = scale.y;
-            scale.y = scale.z;
-            scale.z = y;
-
-            result.loadIdentity();
-            result.setTranslation(translation);
-            result.setRotationQuaternion(rotation);
-            result.setScale(scale);
-        }
+            // first switch the second and third row
+            for (int i = 0; i < 4; ++i) {
+                float temp = store.get(1, i);
+                store.set(1, i, store.get(2, i));
+                store.set(2, i, temp);
+            }
 
-        for (int i = 0; i < 4; ++i) {
-            for (int j = 0; j < 4; ++j) {
-                float value = result.get(i, j);
-                if (Math.abs(value) <= FastMath.FLT_EPSILON) {
-                    result.set(i, j, 0);
-                }
+            // then switch the second and third column
+            for (int i = 0; i < 4; ++i) {
+                float temp = store.get(i, 1);
+                store.set(i, 1, store.get(i, 2));
+                store.set(i, 2, temp);
             }
+
+            // multiply the values in the third row by -1
+            store.m20 *= -1;
+            store.m21 *= -1;
+            store.m22 *= -1;
+            store.m23 *= -1;
+
+            // multiply the values in the third column by -1
+            store.m02 *= -1;
+            store.m12 *= -1;
+            store.m22 *= -1;
+            store.m32 *= -1;
         }
 
-        return result;
+        return store;
+    }
+
+    /**
+     * This method returns the matrix of a given name for the given structure.
+     * It takes up axis into consideration.
+     * 
+     * @param structure
+     *            the structure with matrix data
+     * @param matrixName
+     *            the name of the matrix
+     * @param fixUpAxis
+     *            tells if the Y axis is a UP axis
+     * @return the required matrix
+     */
+    public Matrix4f getMatrix(Structure structure, String matrixName, boolean fixUpAxis) {
+        return this.getMatrix(structure, matrixName, fixUpAxis, new Matrix4f());
     }
 
     private static enum ObjectType {