Bugfix: applying traditional face triangulation when the new algorithm

fails.
experimental
jmekaelthas 11 years ago
parent 068ab69ed9
commit 676ea17465
  1. 121
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
  2. 23
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/IndexesLoop.java
  3. 43
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java

@ -30,6 +30,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
/** The indexes loop of the face. */ /** The indexes loop of the face. */
private IndexesLoop indexes; private IndexesLoop indexes;
private List<IndexesLoop> triangulatedFaces;
/** Indicates if the face is smooth or solid. */ /** Indicates if the face is smooth or solid. */
private boolean smooth; private boolean smooth;
/** The material index of the face. */ /** The material index of the face. */
@ -118,8 +120,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
/** /**
* @return all indexes * @return all indexes
*/ */
public List<Integer> getIndexes() { @SuppressWarnings("unchecked")
return indexes.getAll(); public List<List<Integer>> getIndexes() {
if(triangulatedFaces == null) {
return Arrays.asList(indexes.getAll());
}
List<List<Integer>> result = new ArrayList<List<Integer>>(triangulatedFaces.size());
for(IndexesLoop loop : triangulatedFaces) {
result.add(loop.getAll());
}
return result;
} }
/** /**
@ -130,24 +140,27 @@ import com.jme3.scene.plugins.blender.file.Structure;
* @param triangleIndexes * @param triangleIndexes
* the indexes of a triangle to be detached * the indexes of a triangle to be detached
* @return a list of faces that need to be detached as well in order to keep them normalized * @return a list of faces that need to be detached as well in order to keep them normalized
* @throws BlenderFileException
* an exception is thrown when vertices of a face create more than one loop; this is found during path finding
*/ */
private List<Face> detachTriangle(Integer[] triangleIndexes) { private List<Face> detachTriangle(Integer[] triangleIndexes) throws BlenderFileException {
LOGGER.fine("Detaching triangle."); LOGGER.fine("Detaching triangle.");
if (triangleIndexes.length != 3) { if (triangleIndexes.length != 3) {
throw new IllegalArgumentException("Cannot detach triangle with that does not have 3 indexes!"); throw new IllegalArgumentException("Cannot detach triangle with that does not have 3 indexes!");
} }
List<Face> detachedFaces = new ArrayList<Face>(); List<Face> detachedFaces = new ArrayList<Face>();
List<Integer> path = new ArrayList<Integer>(indexes.size());
boolean[] edgeRemoved = new boolean[] { indexes.removeEdge(triangleIndexes[0], triangleIndexes[1]), indexes.removeEdge(triangleIndexes[0], triangleIndexes[2]), indexes.removeEdge(triangleIndexes[1], triangleIndexes[2]) }; boolean[] edgeRemoved = new boolean[] { indexes.removeEdge(triangleIndexes[0], triangleIndexes[1]), indexes.removeEdge(triangleIndexes[0], triangleIndexes[2]), indexes.removeEdge(triangleIndexes[1], triangleIndexes[2]) };
Integer[][] indexesPairs = new Integer[][] { new Integer[] { triangleIndexes[0], triangleIndexes[1] }, new Integer[] { triangleIndexes[0], triangleIndexes[2] }, new Integer[] { triangleIndexes[1], triangleIndexes[2] } }; Integer[][] indexesPairs = new Integer[][] { new Integer[] { triangleIndexes[0], triangleIndexes[1] }, new Integer[] { triangleIndexes[0], triangleIndexes[2] }, new Integer[] { triangleIndexes[1], triangleIndexes[2] } };
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
if (!edgeRemoved[i]) { if (!edgeRemoved[i]) {
List<Integer> path = indexes.findPath(indexesPairs[i][0], indexesPairs[i][1]); indexes.findPath(indexesPairs[i][0], indexesPairs[i][1], path);
if (path == null) { if (path.size() == 0) {
path = indexes.findPath(indexesPairs[i][1], indexesPairs[i][0]); indexes.findPath(indexesPairs[i][1], indexesPairs[i][0], path);
} }
if (path == null) { if (path.size() == 0) {
throw new IllegalStateException("Triangulation failed. Cannot find path between two indexes. Please apply triangulation in Blender as a workaround."); 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()) { if (detachedFaces.size() == 0 && path.size() < indexes.size()) {
@ -171,7 +184,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
* the index whose position will be queried * the index whose position will be queried
* @return position of the given index or -1 if such index is not in the index loop * @return position of the given index or -1 if such index is not in the index loop
*/ */
private int indexOf(Integer index) { public int indexOf(Integer index) {
return indexes.indexOf(index); return indexes.indexOf(index);
} }
@ -247,67 +260,51 @@ import com.jme3.scene.plugins.blender.file.Structure;
* the vertices of the mesh (all verts and not only those belonging to the face) * the vertices of the mesh (all verts and not only those belonging to the face)
* @param normals * @param normals
* the normals of the mesh (all normals and not only those belonging to the face) * the normals of the mesh (all normals and not only those belonging to the face)
* @return a list of faces that are triangles
*/ */
public List<Face> triangulate(List<Vector3f> vertices, List<Vector3f> normals) { public void triangulate(List<Vector3f> vertices, List<Vector3f> normals) {
LOGGER.fine("Triangulating face."); LOGGER.fine("Triangulating face.");
assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!"; assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!";
List<Face> result = new ArrayList<Face>(); triangulatedFaces = new ArrayList<IndexesLoop>(indexes.size() - 2);
Integer[] indexes = new Integer[3];
List<Face> facesToTriangulate = new ArrayList<Face>(Arrays.asList(this.clone()));
while (facesToTriangulate.size() > 0) { try {
Face face = facesToTriangulate.remove(0); List<Face> facesToTriangulate = new ArrayList<Face>(Arrays.asList(this.clone()));
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1; while (facesToTriangulate.size() > 0) {
while (face.vertexCount() > 0) { Face face = facesToTriangulate.remove(0);
int index1 = face.getIndex(0); int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
int index2 = face.findClosestVertex(index1, -1); while (face.vertexCount() > 0) {
int index3 = face.findClosestVertex(index1, index2); indexes[0] = face.getIndex(0);
indexes[1] = face.findClosestVertex(indexes[0], -1);
LOGGER.finer("Veryfying improper triangulation of the temporal mesh."); indexes[2] = face.findClosestVertex(indexes[0], indexes[1]);
if(index1 < 0 || index2 < 0 || index3 < 0) {
throw new IllegalStateException("Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + LOGGER.finer("Veryfying improper triangulation of the temporal mesh.");
"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 +
if(previousIndex1 == index1 && previousIndex2 == index2 && previousIndex3 == index3) { "Please apply triangulation modifier in blender as a workaround and load again!");
throw new IllegalStateException("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 +
previousIndex1 = index1; "Please apply triangulation modifier in blender as a workaround and load again!");
previousIndex2 = index2; }
previousIndex3 = index3; previousIndex1 = indexes[0];
previousIndex2 = indexes[1];
Integer[] indexes = new Integer[] { index1, index2, index3 }; previousIndex3 = indexes[2];
Arrays.sort(indexes, this);
List<Face> detachedFaces = face.detachTriangle(indexes);
facesToTriangulate.addAll(detachedFaces);
int indexOf0 = this.indexOf(indexes[0]);
int indexOf1 = this.indexOf(indexes[1]);
int indexOf2 = this.indexOf(indexes[2]);
Map<String, List<Vector2f>> faceUVS = new HashMap<String, List<Vector2f>>();
for (Entry<String, List<Vector2f>> entry : faceUVCoords.entrySet()) {
List<Vector2f> uvs = new ArrayList<Vector2f>(3);
uvs.add(entry.getValue().get(indexOf0));
uvs.add(entry.getValue().get(indexOf1));
uvs.add(entry.getValue().get(indexOf2));
faceUVS.put(entry.getKey(), uvs);
}
List<byte[]> vertexColors = null; Arrays.sort(indexes, this);
if (this.vertexColors != null) { facesToTriangulate.addAll(face.detachTriangle(indexes));
vertexColors = new ArrayList<byte[]>(3); triangulatedFaces.add(new IndexesLoop(indexes));
vertexColors.add(this.vertexColors.get(indexOf0));
vertexColors.add(this.vertexColors.get(indexOf1));
vertexColors.add(this.vertexColors.get(indexOf2));
} }
}
result.add(new Face(indexes, smooth, materialNumber, faceUVS, vertexColors, temporalMesh)); } 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) {
indexes[1] = this.getIndex(i);
indexes[2] = this.getIndex(i + 1);
triangulatedFaces.add(new IndexesLoop(indexes));
} }
} }
LOGGER.log(Level.FINE, "Face triangulated on {0} faces.", result.size());
return result;
} }
/** /**

@ -10,6 +10,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
/** /**
* This class represents the Face's indexes loop. It is a simplified implementation of directed graph. * This class represents the Face's indexes loop. It is a simplified implementation of directed graph.
* *
@ -218,27 +220,34 @@ public class IndexesLoop implements Comparator<Integer>, Iterable<Integer> {
* the start index * the start index
* @param end * @param end
* the end index * the end index
* @return a list containing indexes on the path from start to end (inclusive) * @param result
* a list containing indexes on the path from start to end (inclusive)
* @throws IllegalStateException * @throws IllegalStateException
* an exception is thrown when the loop is not normalized (at least one * an exception is thrown when the loop is not normalized (at least one
* index has more than 2 neighbours) * index has more than 2 neighbours)
* @throws BlenderFileException
* an exception is thrown if the vertices of a face create more than one loop; this is thrown
* to prevent lack of memory errors during triangulation
*/ */
public List<Integer> findPath(Integer start, Integer end) { public void findPath(Integer start, Integer end, List<Integer> result) throws BlenderFileException {
List<Integer> result = new ArrayList<Integer>(); result.clear();
Integer node = start; Integer node = start;
while (!node.equals(end)) { while (!node.equals(end)) {
if (result.contains(node)) {
throw new BlenderFileException("Indexes of face have infinite loops!");
}
result.add(node); result.add(node);
List<Integer> nextSteps = edges.get(node); List<Integer> nextSteps = edges.get(node);
if (nextSteps.size() == 0) { if (nextSteps == null || nextSteps.size() == 0) {
return null; result.clear();// no directed path from start to end
return;
} else if (nextSteps.size() == 1) { } else if (nextSteps.size() == 1) {
node = nextSteps.get(0); node = nextSteps.get(0);
} else { } else {
throw new IllegalStateException("Triangulation failed. Face has ambiguous indexes loop. Please triangulate your model in Blender as a workaround."); throw new BlenderFileException("Triangulation failed. Face has ambiguous indexes loop. Please triangulate your model in Blender as a workaround.");
} }
} }
result.add(end); result.add(end);
return result;
} }
@Override @Override

@ -207,11 +207,9 @@ public class TemporalMesh extends Geometry {
*/ */
public void triangulate() { public void triangulate() {
LOGGER.fine("Triangulating temporal mesh."); LOGGER.fine("Triangulating temporal mesh.");
List<Face> triangulatedFaces = new ArrayList<Face>();
for (Face face : faces) { for (Face face : faces) {
triangulatedFaces.addAll(face.triangulate(vertices, normals)); face.triangulate(vertices, normals);
} }
faces = triangulatedFaces;
} }
/** /**
@ -399,29 +397,32 @@ public class TemporalMesh extends Geometry {
faceMeshes.put(face.getMaterialNumber(), meshBuffers); faceMeshes.put(face.getMaterialNumber(), meshBuffers);
} }
List<Integer> indexes = face.getIndexes(); List<List<Integer>> triangulatedIndexes = face.getIndexes();
List<byte[]> vertexColors = face.getVertexColors(); List<byte[]> vertexColors = face.getVertexColors();
boneBuffers.clear();
assert indexes.size() == 3 : "The mesh has not been properly triangulated!"; for(List<Integer> indexes : triangulatedIndexes) {
for (int i = 0; i < 3; ++i) { assert indexes.size() == 3 : "The mesh has not been properly triangulated!";
int vertIndex = indexes.get(i); boneBuffers.clear();
tempVerts[i] = vertices.get(vertIndex); for (int i = 0; i < 3; ++i) {
tempNormals[i] = normals.get(vertIndex); int vertIndex = indexes.get(i);
tempVertColors[i] = vertexColors != null ? vertexColors.get(i) : null; tempVerts[i] = vertices.get(vertIndex);
tempNormals[i] = normals.get(vertIndex);
if (boneIndexes.size() > 0) { tempVertColors[i] = vertexColors != null ? vertexColors.get(i) : null;
Map<Float, Integer> boneBuffersForVertex = new HashMap<Float, Integer>();
Map<String, Float> vertexGroupsForVertex = vertexGroups.get(vertIndex); if (boneIndexes.size() > 0) {
for (Entry<String, Integer> entry : boneIndexes.entrySet()) { Map<Float, Integer> boneBuffersForVertex = new HashMap<Float, Integer>();
if (vertexGroupsForVertex.containsKey(entry.getKey())) { Map<String, Float> vertexGroupsForVertex = vertexGroups.get(vertIndex);
boneBuffersForVertex.put(vertexGroupsForVertex.get(entry.getKey()), entry.getValue()); for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
if (vertexGroupsForVertex.containsKey(entry.getKey())) {
boneBuffersForVertex.put(vertexGroupsForVertex.get(entry.getKey()), entry.getValue());
}
} }
boneBuffers.add(boneBuffersForVertex);
} }
boneBuffers.add(boneBuffersForVertex);
} }
}
meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, face.getUvSets(), tempVertColors, boneBuffers); meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, face.getUvSets(), tempVertColors, boneBuffers);
}
} }
LOGGER.fine("Converting mesh buffers to geometries."); LOGGER.fine("Converting mesh buffers to geometries.");

Loading…
Cancel
Save