|
@@ -5,14 +5,13 @@ import java.nio.FloatBuffer;
|
|
import java.nio.IntBuffer;
|
|
import java.nio.IntBuffer;
|
|
import java.nio.ShortBuffer;
|
|
import java.nio.ShortBuffer;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
-import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
-import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
+import com.jme3.math.Matrix4f;
|
|
import com.jme3.math.Vector3f;
|
|
import com.jme3.math.Vector3f;
|
|
import com.jme3.scene.Geometry;
|
|
import com.jme3.scene.Geometry;
|
|
import com.jme3.scene.Mesh;
|
|
import com.jme3.scene.Mesh;
|
|
@@ -31,10 +30,21 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
* @author Marcin Roguski (Kaelthas)
|
|
* @author Marcin Roguski (Kaelthas)
|
|
*/
|
|
*/
|
|
/* package */class MirrorModifier extends Modifier {
|
|
/* package */class MirrorModifier extends Modifier {
|
|
- private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
|
|
|
|
|
|
+ private static final Logger LOGGER = Logger.getLogger(MirrorModifier.class.getName());
|
|
|
|
|
|
- /** Parameters of the modifier. */
|
|
|
|
- private Map<String, Object> modifierData = new HashMap<String, Object>();
|
|
|
|
|
|
+ private static final int FLAG_MIRROR_X = 0x08;
|
|
|
|
+ private static final int FLAG_MIRROR_Y = 0x10;
|
|
|
|
+ private static final int FLAG_MIRROR_Z = 0x20;
|
|
|
|
+ private static final int FLAG_MIRROR_U = 0x02;
|
|
|
|
+ private static final int FLAG_MIRROR_V = 0x04;
|
|
|
|
+ // private static final int FLAG_MIRROR_VERTEX_GROUP = 0x40;
|
|
|
|
+ private static final int FLAG_MIRROR_MERGE = 0x80;
|
|
|
|
+
|
|
|
|
+ private boolean[] isMirrored;
|
|
|
|
+ private boolean mirrorU, mirrorV;
|
|
|
|
+ private boolean merge;
|
|
|
|
+ private float tolerance;
|
|
|
|
+ private Pointer pMirrorObject;
|
|
|
|
|
|
/**
|
|
/**
|
|
* This constructor reads mirror data from the modifier structure. The
|
|
* This constructor reads mirror data from the modifier structure. The
|
|
@@ -54,12 +64,21 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
*/
|
|
*/
|
|
public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
|
|
public MirrorModifier(Structure modifierStructure, BlenderContext blenderContext) {
|
|
if (this.validate(modifierStructure, blenderContext)) {
|
|
if (this.validate(modifierStructure, blenderContext)) {
|
|
- modifierData.put("flag", modifierStructure.getFieldValue("flag"));
|
|
|
|
- modifierData.put("tolerance", modifierStructure.getFieldValue("tolerance"));
|
|
|
|
- Pointer pMirrorOb = (Pointer) modifierStructure.getFieldValue("mirror_ob");
|
|
|
|
- if (pMirrorOb.isNotNull()) {
|
|
|
|
- modifierData.put("mirrorob", pMirrorOb);
|
|
|
|
|
|
+ int flag = ((Number) modifierStructure.getFieldValue("flag")).intValue();
|
|
|
|
+
|
|
|
|
+ isMirrored = new boolean[] { (flag & FLAG_MIRROR_X) != 0, (flag & FLAG_MIRROR_Y) != 0, (flag & FLAG_MIRROR_Z) != 0 };
|
|
|
|
+ if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
|
|
|
+ boolean temp = isMirrored[1];
|
|
|
|
+ isMirrored[1] = isMirrored[2];
|
|
|
|
+ isMirrored[2] = temp;
|
|
}
|
|
}
|
|
|
|
+ mirrorU = (flag & FLAG_MIRROR_U) != 0;
|
|
|
|
+ mirrorV = (flag & FLAG_MIRROR_V) != 0;
|
|
|
|
+ // boolean mirrorVGroup = (flag & FLAG_MIRROR_VERTEX_GROUP) != 0;
|
|
|
|
+ merge = (flag & FLAG_MIRROR_MERGE) == 0;// in this case we use == instead of != (this is not a mistake)
|
|
|
|
+
|
|
|
|
+ tolerance = ((Number) modifierStructure.getFieldValue("tolerance")).floatValue();
|
|
|
|
+ pMirrorObject = (Pointer) modifierStructure.getFieldValue("mirror_ob");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -68,46 +87,48 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
if (invalid) {
|
|
if (invalid) {
|
|
LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
|
|
LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
|
|
} else {
|
|
} else {
|
|
- int flag = ((Number) modifierData.get("flag")).intValue();
|
|
|
|
- boolean[] isMirrored = new boolean[] { (flag & 0x08) != 0, (flag & 0x10) != 0, (flag & 0x20) != 0 };
|
|
|
|
- if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
|
|
|
- boolean temp = isMirrored[1];
|
|
|
|
- isMirrored[1] = isMirrored[2];
|
|
|
|
- isMirrored[2] = temp;
|
|
|
|
- }
|
|
|
|
- float[] center = new float[] { 0.0f, 0.0f, 0.0f };
|
|
|
|
- Pointer pObject = (Pointer) modifierData.get("mirrorob");
|
|
|
|
- if (pObject != null) {
|
|
|
|
|
|
+ Vector3f mirrorPlaneCenter = new Vector3f();
|
|
|
|
+ if (pMirrorObject.isNotNull()) {
|
|
Structure objectStructure;
|
|
Structure objectStructure;
|
|
try {
|
|
try {
|
|
- objectStructure = pObject.fetchData(blenderContext.getInputStream()).get(0);
|
|
|
|
|
|
+ objectStructure = pMirrorObject.fetchData(blenderContext.getInputStream()).get(0);
|
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
|
Node object = (Node) objectHelper.toObject(objectStructure, blenderContext);
|
|
Node object = (Node) objectHelper.toObject(objectStructure, blenderContext);
|
|
if (object != null) {
|
|
if (object != null) {
|
|
- Vector3f translation = object.getWorldTranslation();
|
|
|
|
- center[0] = translation.x;
|
|
|
|
- center[1] = translation.y;
|
|
|
|
- center[2] = translation.z;
|
|
|
|
|
|
+ // compute the mirror object coordinates in node's local space
|
|
|
|
+ mirrorPlaneCenter = this.getWorldMatrix(node).invertLocal().mult(object.getWorldTranslation());
|
|
}
|
|
}
|
|
} catch (BlenderFileException e) {
|
|
} catch (BlenderFileException e) {
|
|
LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage());
|
|
LOGGER.log(Level.SEVERE, "Cannot load mirror''s reference object. Cause: {0}", e.getLocalizedMessage());
|
|
|
|
+ LOGGER.log(Level.SEVERE, "Mirror modifier will not be applied to node named: {0}", node.getName());
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- float tolerance = ((Number) modifierData.get("tolerance")).floatValue();
|
|
|
|
- boolean mirrorU = (flag & 0x01) != 0;
|
|
|
|
- boolean mirrorV = (flag & 0x02) != 0;
|
|
|
|
- // boolean mirrorVGroup = (flag & 0x20) != 0;
|
|
|
|
|
|
|
|
|
|
+ LOGGER.finest("Allocating temporal variables.");
|
|
|
|
+ float d;
|
|
|
|
+ Vector3f mirrorPlaneNormal = new Vector3f();
|
|
|
|
+ Vector3f shiftVector = new Vector3f();
|
|
|
|
+ Vector3f point = new Vector3f();
|
|
|
|
+ Vector3f normal = new Vector3f();
|
|
Set<Integer> modifiedIndexes = new HashSet<Integer>();
|
|
Set<Integer> modifiedIndexes = new HashSet<Integer>();
|
|
List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
|
|
List<Geometry> geometriesToAdd = new ArrayList<Geometry>();
|
|
|
|
+ final char[] mirrorNames = new char[] { 'X', 'Y', 'Z' };
|
|
|
|
+
|
|
|
|
+ LOGGER.fine("Mirroring mesh.");
|
|
for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
|
|
for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {
|
|
if (isMirrored[mirrorIndex]) {
|
|
if (isMirrored[mirrorIndex]) {
|
|
|
|
+ boolean mirrorAtPoint0 = mirrorPlaneCenter.get(mirrorIndex) == 0;
|
|
|
|
+ if (!mirrorAtPoint0) {// compute mirror's plane normal vector in node's space
|
|
|
|
+ mirrorPlaneNormal.set(0, 0, 0).set(mirrorIndex, Math.signum(mirrorPlaneCenter.get(mirrorIndex)));
|
|
|
|
+ }
|
|
|
|
+
|
|
for (Spatial spatial : node.getChildren()) {
|
|
for (Spatial spatial : node.getChildren()) {
|
|
if (spatial instanceof Geometry) {
|
|
if (spatial instanceof Geometry) {
|
|
Mesh mesh = ((Geometry) spatial).getMesh();
|
|
Mesh mesh = ((Geometry) spatial).getMesh();
|
|
Mesh clone = mesh.deepClone();
|
|
Mesh clone = mesh.deepClone();
|
|
|
|
|
|
- // getting buffers
|
|
|
|
|
|
+ LOGGER.log(Level.FINEST, "Fetching buffers of cloned spatial: {0}", spatial.getName());
|
|
FloatBuffer position = mesh.getFloatBuffer(Type.Position);
|
|
FloatBuffer position = mesh.getFloatBuffer(Type.Position);
|
|
FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);
|
|
FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);
|
|
|
|
|
|
@@ -121,34 +142,33 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
int index = cloneIndexes instanceof ShortBuffer ? ((ShortBuffer) cloneIndexes).get(i) : ((IntBuffer) cloneIndexes).get(i);
|
|
int index = cloneIndexes instanceof ShortBuffer ? ((ShortBuffer) cloneIndexes).get(i) : ((IntBuffer) cloneIndexes).get(i);
|
|
if (!modifiedIndexes.contains(index)) {
|
|
if (!modifiedIndexes.contains(index)) {
|
|
modifiedIndexes.add(index);
|
|
modifiedIndexes.add(index);
|
|
- int valueIndex = index * 3 + mirrorIndex;
|
|
|
|
|
|
|
|
- float value = clonePosition.get(valueIndex);
|
|
|
|
- float d = center[mirrorIndex] - value;
|
|
|
|
|
|
+ this.get(clonePosition, index, point);
|
|
|
|
+ if (mirrorAtPoint0) {
|
|
|
|
+ d = Math.abs(point.get(mirrorIndex));
|
|
|
|
+ shiftVector.set(0, 0, 0).set(mirrorIndex, -point.get(mirrorIndex));
|
|
|
|
+ } else {
|
|
|
|
+ d = this.computeDistanceFromPlane(point, mirrorPlaneCenter, mirrorPlaneNormal);
|
|
|
|
+ mirrorPlaneNormal.mult(d, shiftVector);
|
|
|
|
+ }
|
|
|
|
|
|
- if (Math.abs(d) <= tolerance) {
|
|
|
|
- clonePosition.put(valueIndex, center[mirrorIndex]);
|
|
|
|
- if (cloneBindPosePosition != null) {
|
|
|
|
- cloneBindPosePosition.put(valueIndex, center[mirrorIndex]);
|
|
|
|
- }
|
|
|
|
- position.put(valueIndex, center[mirrorIndex]);
|
|
|
|
- if (bindPosePosition != null) {
|
|
|
|
- bindPosePosition.put(valueIndex, center[mirrorIndex]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cloneNormals.put(valueIndex, 0);
|
|
|
|
- if (cloneBindPoseNormals != null) {
|
|
|
|
- cloneBindPoseNormals.put(valueIndex, 0);
|
|
|
|
|
|
+ if (merge && d <= tolerance) {
|
|
|
|
+ point.addLocal(shiftVector);
|
|
|
|
+
|
|
|
|
+ this.set(index, point, clonePosition, cloneBindPosePosition, position, bindPosePosition);
|
|
|
|
+ if (cloneNormals != null) {
|
|
|
|
+ this.get(cloneNormals, index, normal);
|
|
|
|
+ normal.set(mirrorIndex, 0);
|
|
|
|
+ this.set(index, normal, cloneNormals, cloneBindPoseNormals);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- clonePosition.put(valueIndex, value + 2.0f * d);
|
|
|
|
- if (cloneBindPosePosition != null) {
|
|
|
|
- cloneBindPosePosition.put(valueIndex, value + 2.0f * d);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- cloneNormals.put(valueIndex, -cloneNormals.get(valueIndex));
|
|
|
|
- if (cloneBindPoseNormals != null) {
|
|
|
|
- cloneBindPoseNormals.put(valueIndex, -cloneNormals.get(valueIndex));
|
|
|
|
|
|
+ point.addLocal(shiftVector.multLocal(2));
|
|
|
|
+
|
|
|
|
+ this.set(index, point, clonePosition, cloneBindPosePosition);
|
|
|
|
+ if (cloneNormals != null) {
|
|
|
|
+ this.get(cloneNormals, index, normal);
|
|
|
|
+ normal.set(mirrorIndex, -normal.get(mirrorIndex));
|
|
|
|
+ this.set(index, normal, cloneNormals, cloneBindPoseNormals);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -156,7 +176,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
modifiedIndexes.clear();
|
|
modifiedIndexes.clear();
|
|
|
|
|
|
LOGGER.finer("Flipping index order.");
|
|
LOGGER.finer("Flipping index order.");
|
|
- switch(mesh.getMode()) {
|
|
|
|
|
|
+ switch (mesh.getMode()) {
|
|
case Points:
|
|
case Points:
|
|
cloneIndexes.flip();
|
|
cloneIndexes.flip();
|
|
break;
|
|
break;
|
|
@@ -191,25 +211,27 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
}
|
|
}
|
|
|
|
|
|
if (mirrorU && clone.getBuffer(Type.TexCoord) != null) {
|
|
if (mirrorU && clone.getBuffer(Type.TexCoord) != null) {
|
|
|
|
+ LOGGER.finer("Mirroring U coordinates.");
|
|
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
for (int i = 0; i < cloneUVs.limit(); i += 2) {
|
|
for (int i = 0; i < cloneUVs.limit(); i += 2) {
|
|
cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mirrorV && clone.getBuffer(Type.TexCoord) != null) {
|
|
if (mirrorV && clone.getBuffer(Type.TexCoord) != null) {
|
|
|
|
+ LOGGER.finer("Mirroring V coordinates.");
|
|
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();
|
|
for (int i = 1; i < cloneUVs.limit(); i += 2) {
|
|
for (int i = 1; i < cloneUVs.limit(); i += 2) {
|
|
cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
cloneUVs.put(i, 1.0f - cloneUVs.get(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- Geometry geometry = new Geometry(null, clone);
|
|
|
|
|
|
+ Geometry geometry = new Geometry(spatial.getName() + " - mirror " + mirrorNames[mirrorIndex], clone);
|
|
geometry.setMaterial(((Geometry) spatial).getMaterial());
|
|
geometry.setMaterial(((Geometry) spatial).getMaterial());
|
|
geometriesToAdd.add(geometry);
|
|
geometriesToAdd.add(geometry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // adding meshes to node
|
|
|
|
|
|
+ LOGGER.log(Level.FINE, "Adding {0} geometries to current node.", geometriesToAdd.size());
|
|
for (Geometry geometry : geometriesToAdd) {
|
|
for (Geometry geometry : geometriesToAdd) {
|
|
node.attachChild(geometry);
|
|
node.attachChild(geometry);
|
|
}
|
|
}
|
|
@@ -218,4 +240,69 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Fetches the world matrix transformation of the given node.
|
|
|
|
+ * @param node
|
|
|
|
+ * the node
|
|
|
|
+ * @return the node's world transformation matrix
|
|
|
|
+ */
|
|
|
|
+ private Matrix4f getWorldMatrix(Node node) {
|
|
|
|
+ Matrix4f result = new Matrix4f();
|
|
|
|
+ result.setTranslation(node.getWorldTranslation());
|
|
|
|
+ result.setRotationQuaternion(node.getWorldRotation());
|
|
|
|
+ result.setScale(node.getWorldScale());
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The method computes the distance between a point and a plane (described by point in space and normal vector).
|
|
|
|
+ * @param p
|
|
|
|
+ * the point in the space
|
|
|
|
+ * @param c
|
|
|
|
+ * mirror's plane center
|
|
|
|
+ * @param n
|
|
|
|
+ * mirror's plane normal (should be normalized)
|
|
|
|
+ * @return the minimum distance from point to plane
|
|
|
|
+ */
|
|
|
|
+ private float computeDistanceFromPlane(Vector3f p, Vector3f c, Vector3f n) {
|
|
|
|
+ return Math.abs(n.dot(p) - c.dot(n));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Sets the given value (v) into every of the buffers at the given index.
|
|
|
|
+ * The index is cosidered to be an index of a vertex of the mesh.
|
|
|
|
+ * @param index
|
|
|
|
+ * the index of vertex of the mesh
|
|
|
|
+ * @param value
|
|
|
|
+ * the value to be set
|
|
|
|
+ * @param buffers
|
|
|
|
+ * the buffers where the value will be set
|
|
|
|
+ */
|
|
|
|
+ private void set(int index, Vector3f value, FloatBuffer... buffers) {
|
|
|
|
+ index *= 3;
|
|
|
|
+ for (FloatBuffer buffer : buffers) {
|
|
|
|
+ if (buffer != null) {
|
|
|
|
+ buffer.put(index, value.x);
|
|
|
|
+ buffer.put(index + 1, value.y);
|
|
|
|
+ buffer.put(index + 2, value.z);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Fetches the vector's value from the given buffer at specified index.
|
|
|
|
+ * @param buffer
|
|
|
|
+ * the buffer we get the data from
|
|
|
|
+ * @param index
|
|
|
|
+ * the index of vertex of the mesh
|
|
|
|
+ * @param store
|
|
|
|
+ * the vector where the result will be set
|
|
|
|
+ */
|
|
|
|
+ private void get(FloatBuffer buffer, int index, Vector3f store) {
|
|
|
|
+ index *= 3;
|
|
|
|
+ store.x = buffer.get(index);
|
|
|
|
+ store.y = buffer.get(index + 1);
|
|
|
|
+ store.z = buffer.get(index + 2);
|
|
|
|
+ }
|
|
}
|
|
}
|