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
experimental
Kae..pl 11 years ago
parent 68debc95eb
commit b77f5a422c
  1. 200
      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 {

Loading…
Cancel
Save