|
@@ -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 {
|