From b77f5a422c093940b40d84a83f878750c4003d86 Mon Sep 17 00:00:00 2001 From: "Kae..pl" Date: Sat, 14 Dec 2013 20:01:52 +0000 Subject: [PATCH] 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 --- .../plugins/blender/objects/ObjectHelper.java | 200 ++++++++++++++---- 1 file changed, 163 insertions(+), 37 deletions(-) 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 07e453542..2443a125e 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 @@ -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 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 size = (DynamicArray) 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 obmat = (DynamicArray) 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 {