Bugfix: fixed a bug that caused ba UV coordinates to be applied on the

face after triangulation
Refactoring: made temporal mesh more exposed for external modifications;
this allows modifiers to properly modify the mesh and to remove some
modifier-specific code from the temporal mesh implementation
experimental
jmekaelthas 10 years ago
parent 1992da3471
commit 6e05304d26
  1. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java
  2. 40
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
  3. 24
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  4. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Point.java
  5. 214
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java
  6. 19
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArrayModifier.java
  7. 72
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MaskModifier.java
  8. 26
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/MirrorModifier.java

@ -18,7 +18,7 @@ import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class Edge extends Line {
public class Edge extends Line {
private static final long serialVersionUID = 7172714692126675311L;
private static final Logger LOGGER = Logger.getLogger(Edge.class.getName());

@ -25,7 +25,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class Face implements Comparator<Integer> {
public class Face implements Comparator<Integer> {
private static final Logger LOGGER = Logger.getLogger(Face.class.getName());
/** The indexes loop of the face. */
@ -117,6 +117,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
return indexes.get(indexPosition);
}
/**
* @return the mesh this face belongs to
*/
public TemporalMesh getTemporalMesh() {
return temporalMesh;
}
/**
* @return the original indexes of the face
*/
@ -129,11 +136,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
*/
@SuppressWarnings("unchecked")
public List<List<Integer>> getCurrentIndexes() {
if(triangulatedFaces == null) {
if (triangulatedFaces == null) {
return Arrays.asList(indexes.getAll());
}
List<List<Integer>> result = new ArrayList<List<Integer>>(triangulatedFaces.size());
for(IndexesLoop loop : triangulatedFaces) {
for (IndexesLoop loop : triangulatedFaces) {
result.add(loop.getAll());
}
return result;
@ -155,6 +162,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
if (triangleIndexes.length != 3) {
throw new IllegalArgumentException("Cannot detach triangle with that does not have 3 indexes!");
}
MeshHelper meshHelper = temporalMesh.getBlenderContext().getHelper(MeshHelper.class);
List<Face> detachedFaces = new ArrayList<Face>();
List<Integer> path = new ArrayList<Integer>(indexes.size());
@ -171,7 +179,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
throw new IllegalStateException("Triangulation failed. Cannot find path between two indexes. Please apply triangulation in Blender as a workaround.");
}
if (detachedFaces.size() == 0 && path.size() < indexes.size()) {
detachedFaces.add(new Face(path.toArray(new Integer[path.size()]), smooth, materialNumber, faceUVCoords, vertexColors, temporalMesh));
Integer[] indexesSublist = path.toArray(new Integer[path.size()]);
detachedFaces.add(new Face(indexesSublist, smooth, materialNumber, meshHelper.selectUVSubset(this, indexesSublist), vertexColors, temporalMesh));
for (int j = 0; j < path.size() - 1; ++j) {
indexes.removeEdge(path.get(j), path.get(j + 1));
}
@ -244,12 +253,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
/**
* The method triangulates the face.
* @param vertices
* the vertices of the mesh (all verts and not only those belonging to the face)
* @param normals
* the normals of the mesh (all normals and not only those belonging to the face)
*/
public void triangulate(List<Vector3f> vertices, List<Vector3f> normals) {
public void triangulate() {
LOGGER.fine("Triangulating face.");
assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!";
triangulatedFaces = new ArrayList<IndexesLoop>(indexes.size() - 2);
@ -266,13 +271,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
indexes[2] = face.findClosestVertex(indexes[0], indexes[1]);
LOGGER.finer("Veryfying improper triangulation of the temporal mesh.");
if(indexes[0] < 0 || indexes[1] < 0 || indexes[2] < 0) {
throw new BlenderFileException("Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh +
"Please apply triangulation modifier in blender as a workaround and load again!");
if (indexes[0] < 0 || indexes[1] < 0 || indexes[2] < 0) {
throw new BlenderFileException("Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!");
}
if(previousIndex1 == indexes[0] && previousIndex2 == indexes[1] && previousIndex3 == indexes[2]) {
throw new BlenderFileException("Infinite loop detected during triangulation of mesh: " + temporalMesh +
"Please apply triangulation modifier in blender as a workaround and load again!");
if (previousIndex1 == indexes[0] && previousIndex2 == indexes[1] && previousIndex3 == indexes[2]) {
throw new BlenderFileException("Infinite loop detected during triangulation of mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!");
}
previousIndex1 = indexes[0];
previousIndex2 = indexes[1];
@ -283,11 +286,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
triangulatedFaces.add(new IndexesLoop(indexes));
}
}
} catch(BlenderFileException e) {
LOGGER.log(Level.WARNING, "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, " +
"but the results might not be identical to blender.", e.getLocalizedMessage());
} catch (BlenderFileException e) {
LOGGER.log(Level.WARNING, "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, " + "but the results might not be identical to blender.", e.getLocalizedMessage());
indexes[0] = this.getIndex(0);
for(int i=1;i<this.vertexCount() - 1;++i) {
for (int i = 1; i < this.vertexCount() - 1; ++i) {
indexes[1] = this.getIndex(i);
indexes[2] = this.getIndex(i + 1);
triangulatedFaces.add(new IndexesLoop(indexes));

@ -36,6 +36,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -317,6 +318,29 @@ public class MeshHelper extends AbstractBlenderHelper {
return result;
}
/**
* Selects the proper subsets of UV coordinates for the given sublist of indexes.
* @param face
* the face with the original UV sets
* @param indexesSublist
* the sub list of indexes
* @return a map of UV coordinates subsets
*/
public Map<String, List<Vector2f>> selectUVSubset(Face face, Integer... indexesSublist) {
Map<String, List<Vector2f>> result = null;
if (face.getUvSets() != null) {
result = new HashMap<String, List<Vector2f>>();
for (Entry<String, List<Vector2f>> entry : face.getUvSets().entrySet()) {
List<Vector2f> uvs = new ArrayList<Vector2f>(indexesSublist.length);
for (Integer index : indexesSublist) {
uvs.add(entry.getValue().get(face.getIndexes().indexOf(index)));
}
result.put(entry.getKey(), uvs);
}
}
return result;
}
/**
* Returns the black unshaded material. It is used for lines and points because that is how blender
* renders it.

@ -17,7 +17,7 @@ import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class Point {
public class Point {
private static final Logger LOGGER = Logger.getLogger(Point.class.getName());
/** The point's index. */

@ -4,15 +4,11 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -32,7 +28,6 @@ import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
import com.jme3.scene.plugins.blender.meshes.MeshBuffers.BoneBuffersData;
import com.jme3.scene.plugins.blender.modifiers.Modifier;
import com.jme3.scene.plugins.blender.objects.Properties;
@ -123,6 +118,62 @@ public class TemporalMesh extends Geometry {
}
}
/**
* @return the blender context
*/
public BlenderContext getBlenderContext() {
return blenderContext;
}
/**
* @return the vertices of the mesh
*/
public List<Vector3f> getVertices() {
return vertices;
}
/**
* @return the normals of the mesh
*/
public List<Vector3f> getNormals() {
return normals;
}
/**
* @return all faces
*/
public List<Face> getFaces() {
return faces;
}
/**
* @return all edges
*/
public List<Edge> getEdges() {
return edges;
}
/**
* @return all points (do not mistake it with vertices)
*/
public List<Point> getPoints() {
return points;
}
/**
* @return all vertices colors
*/
public List<byte[]> getVerticesColors() {
return verticesColors;
}
/**
* @return all vertex groups for the vertices (each map has groups for the proper vertex)
*/
public List<Map<String, Float>> getVertexGroups() {
return vertexGroups;
}
@Override
public TemporalMesh clone() {
try {
@ -159,20 +210,6 @@ public class TemporalMesh extends Geometry {
return null;
}
/**
* @return the vertices of the mesh
*/
protected List<Vector3f> getVertices() {
return vertices;
}
/**
* @return the normals of the mesh
*/
protected List<Vector3f> getNormals() {
return normals;
}
@Override
public void updateModelBound() {
if (boundingBox == null) {
@ -212,7 +249,7 @@ public class TemporalMesh extends Geometry {
public void triangulate() {
LOGGER.fine("Triangulating temporal mesh.");
for (Face face : faces) {
face.triangulate(vertices, normals);
face.triangulate();
}
}
@ -248,19 +285,6 @@ public class TemporalMesh extends Geometry {
boneIndexes.putAll(mesh.boneIndexes);
}
/**
* Translate all vertices by the given vector.
* @param translation
* the translation vector
* @return this mesh after translation (NO new instance is created)
*/
public TemporalMesh translate(Vector3f translation) {
for (Vector3f v : vertices) {
v.addLocal(translation);
}
return this;
}
/**
* Sets the properties of the mesh.
* @param properties
@ -304,47 +328,6 @@ public class TemporalMesh extends Geometry {
return vertices.size();
}
/**
* Returns the vertex at the given position.
* @param i
* the vertex position
* @return the vertex at the given position
*/
public Vector3f getVertex(int i) {
return vertices.get(i);
}
/**
* Returns the normal at the given position.
* @param i
* the normal position
* @return the normal at the given position
*/
public Vector3f getNormal(int i) {
return normals.get(i);
}
/**
* Returns the vertex groups at the given vertex index.
* @param i
* the vertex groups for vertex with a given index
* @return the vertex groups at the given vertex index
*/
public Map<String, Float> getVertexGroups(int i) {
return vertexGroups.size() > i ? vertexGroups.get(i) : null;
}
/**
* @return a collection of vertex group names for this mesh
*/
public Collection<String> getVertexGroupNames() {
Set<String> result = new HashSet<String>();
for (Map<String, Float> groups : vertexGroups) {
result.addAll(groups.keySet());
}
return result;
}
/**
* Removes all vertices from the mesh.
*/
@ -358,86 +341,6 @@ public class TemporalMesh extends Geometry {
points.clear();
}
/**
* Every face, edge and point that contains
* the vertex will be removed.
* @param index
* the index of a vertex to be removed
* @throws IndexOutOfBoundsException
* thrown when given index is negative or beyond the count of vertices
*/
public void removeVertexAt(final int index) {
if (index < 0 || index >= vertices.size()) {
throw new IndexOutOfBoundsException("The given index is out of bounds: " + index);
}
vertices.remove(index);
normals.remove(index);
if(vertexGroups.size() > 0) {
vertexGroups.remove(index);
}
if(verticesColors.size() > 0) {
verticesColors.remove(index);
}
IndexPredicate shiftPredicate = new IndexPredicate() {
@Override
public boolean execute(Integer i) {
return i > index;
}
};
for (int i = faces.size() - 1; i >= 0; --i) {
Face face = faces.get(i);
if (face.getIndexes().indexOf(index) >= 0) {
faces.remove(i);
} else {
face.getIndexes().shiftIndexes(-1, shiftPredicate);
}
}
for (int i = edges.size() - 1; i >= 0; --i) {
Edge edge = edges.get(i);
if (edge.getFirstIndex() == index || edge.getSecondIndex() == index) {
edges.remove(i);
} else {
edge.shiftIndexes(-1, shiftPredicate);
}
}
for (int i = points.size() - 1; i >= 0; --i) {
Point point = points.get(i);
if (point.getIndex() == index) {
points.remove(i);
} else {
point.shiftIndexes(-1, shiftPredicate);
}
}
}
/**
* Flips the order of the mesh's indexes.
*/
public void flipIndexes() {
for (Face face : faces) {
face.flipIndexes();
}
for (Edge edge : edges) {
edge.flipIndexes();
}
Collections.reverse(points);
}
/**
* Flips UV coordinates.
* @param u
* indicates if U coords should be flipped
* @param v
* indicates if V coords should be flipped
*/
public void flipUV(boolean u, boolean v) {
for (Face face : faces) {
face.flipUV(u, v);
}
}
/**
* The mesh builds geometries from the mesh. The result is stored in the blender context
* under the mesh's OMA.
@ -513,7 +416,8 @@ public class TemporalMesh extends Geometry {
}
}
meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, face.getUvSets(), tempVertColors, boneBuffers);
Map<String, List<Vector2f>> uvs = meshHelper.selectUVSubset(face, indexes.toArray(new Integer[indexes.size()]));
meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, uvs, tempVertColors, boneBuffers);
}
}

@ -210,15 +210,28 @@ import com.jme3.scene.shape.Curve;
if (count > 0) {
TemporalMesh originalMesh = temporalMesh.clone();
for (int i = 0; i < count; ++i) {
temporalMesh.append(originalMesh.clone().translate(totalTranslation));
TemporalMesh clone = originalMesh.clone();
for (Vector3f v : clone.getVertices()) {
v.addLocal(totalTranslation);
}
temporalMesh.append(clone);
totalTranslation.addLocal(translationVector);
}
}
if (caps[0] != null) {
temporalMesh.append(caps[0].clone().translate(translationVector.multLocal(-1)));
translationVector.multLocal(-1);
TemporalMesh capsClone = caps[0].clone();
for (Vector3f v : capsClone.getVertices()) {
v.addLocal(translationVector);
}
temporalMesh.append(capsClone);
}
if (caps[1] != null) {
temporalMesh.append(caps[1].clone().translate(totalTranslation));
TemporalMesh capsClone = caps[1].clone();
for (Vector3f v : capsClone.getVertices()) {
v.addLocal(totalTranslation);
}
temporalMesh.append(capsClone);
}
} else {
LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node);

@ -1,10 +1,11 @@
package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -14,6 +15,10 @@ import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.meshes.Edge;
import com.jme3.scene.plugins.blender.meshes.Face;
import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
import com.jme3.scene.plugins.blender.meshes.Point;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
/**
@ -78,7 +83,10 @@ import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
// if the mesh has no vertex groups then remove all verts
// if the mesh has at least one vertex group - then do nothing
// I have no idea why we should do that, but blender works this way
Collection<String> vertexGroupNames = temporalMesh.getVertexGroupNames();
Set<String> vertexGroupNames = new HashSet<String>();
for (Map<String, Float> groups : temporalMesh.getVertexGroups()) {
vertexGroupNames.addAll(groups.keySet());
}
if (vertexGroupNames.size() == 0 && !invertMask || vertexGroupNames.size() > 0 && invertMask) {
temporalMesh.clear();
}
@ -87,9 +95,9 @@ import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
if (vertexGroupsToRemove.size() > 0) {
List<Integer> vertsToBeRemoved = new ArrayList<Integer>();
for (int i = 0; i < temporalMesh.getVertexCount(); ++i) {
Map<String, Float> vertexGroups = temporalMesh.getVertexGroups(i);
Map<String, Float> vertexGroups = temporalMesh.getVertexGroups().get(i);
boolean hasVertexGroup = false;
if(vertexGroups != null) {
if (vertexGroups != null) {
for (String groupName : vertexGroupsToRemove) {
Float weight = vertexGroups.get(groupName);
if (weight != null && weight > 0) {
@ -106,7 +114,7 @@ import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
Collections.reverse(vertsToBeRemoved);
for (Integer vertexIndex : vertsToBeRemoved) {
temporalMesh.removeVertexAt(vertexIndex);
this.removeVertexAt(vertexIndex, temporalMesh);
}
}
} else {
@ -115,6 +123,60 @@ import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
}
}
/**
* Every face, edge and point that contains
* the vertex will be removed.
* @param index
* the index of a vertex to be removed
* @throws IndexOutOfBoundsException
* thrown when given index is negative or beyond the count of vertices
*/
private void removeVertexAt(final int index, TemporalMesh temporalMesh) {
if (index < 0 || index >= temporalMesh.getVertexCount()) {
throw new IndexOutOfBoundsException("The given index is out of bounds: " + index);
}
temporalMesh.getVertices().remove(index);
temporalMesh.getNormals().remove(index);
if (temporalMesh.getVertexGroups().size() > 0) {
temporalMesh.getVertexGroups().remove(index);
}
if (temporalMesh.getVerticesColors().size() > 0) {
temporalMesh.getVerticesColors().remove(index);
}
IndexPredicate shiftPredicate = new IndexPredicate() {
@Override
public boolean execute(Integer i) {
return i > index;
}
};
for (int i = temporalMesh.getFaces().size() - 1; i >= 0; --i) {
Face face = temporalMesh.getFaces().get(i);
if (face.getIndexes().indexOf(index) >= 0) {
temporalMesh.getFaces().remove(i);
} else {
face.getIndexes().shiftIndexes(-1, shiftPredicate);
}
}
for (int i = temporalMesh.getEdges().size() - 1; i >= 0; --i) {
Edge edge = temporalMesh.getEdges().get(i);
if (edge.getFirstIndex() == index || edge.getSecondIndex() == index) {
temporalMesh.getEdges().remove(i);
} else {
edge.shiftIndexes(-1, shiftPredicate);
}
}
for (int i = temporalMesh.getPoints().size() - 1; i >= 0; --i) {
Point point = temporalMesh.getPoints().get(i);
if (point.getIndex() == index) {
temporalMesh.getPoints().remove(i);
} else {
point.shiftIndexes(-1, shiftPredicate);
}
}
}
/**
* Reads the names of the bones from the given bone base.
* @param boneBase

@ -1,5 +1,6 @@
package com.jme3.scene.plugins.blender.modifiers;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -10,6 +11,8 @@ import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.meshes.Edge;
import com.jme3.scene.plugins.blender.meshes.Face;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
@ -70,7 +73,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
tolerance = ((Number) modifierStructure.getFieldValue("tolerance")).floatValue();
pMirrorObject = (Pointer) modifierStructure.getFieldValue("mirror_ob");
if(mirrorVGroup) {
if (mirrorVGroup) {
LOGGER.warning("Mirroring vertex groups is currently not supported.");
}
}
@ -117,8 +120,8 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
TemporalMesh mirror = temporalMesh.clone();
for (int i = 0; i < mirror.getVertexCount(); ++i) {
Vector3f vertex = mirror.getVertex(i);
Vector3f normal = mirror.getNormal(i);
Vector3f vertex = mirror.getVertices().get(i);
Vector3f normal = mirror.getNormals().get(i);
if (mirrorAtPoint0) {
d = Math.abs(vertex.get(mirrorIndex));
@ -131,18 +134,27 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
if (merge && d <= tolerance) {
vertex.addLocal(shiftVector);
normal.set(mirrorIndex, 0);
temporalMesh.getVertex(i).addLocal(shiftVector);
temporalMesh.getNormal(i).set(mirrorIndex, 0);
temporalMesh.getVertices().get(i).addLocal(shiftVector);
temporalMesh.getNormals().get(i).set(mirrorIndex, 0);
} else {
vertex.addLocal(shiftVector.multLocal(2));
normal.set(mirrorIndex, -normal.get(mirrorIndex));
}
}
mirror.flipIndexes();
// flipping the indexes
for (Face face : mirror.getFaces()) {
face.flipIndexes();
}
for (Edge edge : mirror.getEdges()) {
edge.flipIndexes();
}
Collections.reverse(mirror.getPoints());
if (mirrorU || mirrorV) {
mirror.flipUV(mirrorU, mirrorV);
for (Face face : mirror.getFaces()) {
face.flipUV(mirrorU, mirrorV);
}
}
temporalMesh.append(mirror);

Loading…
Cancel
Save