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. */
private IndexesLoop indexes;
private List<IndexesLoop> triangulatedFaces;
/** Indicates if the face is smooth or solid. */
private boolean smooth;
/** The material index of the face. */
@ -118,8 +120,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
/**
* @return all indexes
*/
public List<Integer> getIndexes() {
return indexes.getAll();
@SuppressWarnings("unchecked")
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
* 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
* @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.");
if (triangleIndexes.length != 3) {
throw new IllegalArgumentException("Cannot detach triangle with that does not have 3 indexes!");
}
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]) };
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) {
if (!edgeRemoved[i]) {
List<Integer> path = indexes.findPath(indexesPairs[i][0], indexesPairs[i][1]);
if (path == null) {
path = indexes.findPath(indexesPairs[i][1], indexesPairs[i][0]);
indexes.findPath(indexesPairs[i][0], indexesPairs[i][1], path);
if (path.size() == 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.");
}
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
* @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);
}
@ -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)
* @param normals
* 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.");
assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!";
List<Face> result = new ArrayList<Face>();
List<Face> facesToTriangulate = new ArrayList<Face>(Arrays.asList(this.clone()));
while (facesToTriangulate.size() > 0) {
Face face = facesToTriangulate.remove(0);
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
while (face.vertexCount() > 0) {
int index1 = face.getIndex(0);
int index2 = face.findClosestVertex(index1, -1);
int index3 = face.findClosestVertex(index1, index2);
LOGGER.finer("Veryfying improper triangulation of the temporal mesh.");
if(index1 < 0 || index2 < 0 || index3 < 0) {
throw new IllegalStateException("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 == index1 && previousIndex2 == index2 && previousIndex3 == index3) {
throw new IllegalStateException("Infinite loop detected during triangulation of mesh: " + temporalMesh +
"Please apply triangulation modifier in blender as a workaround and load again!");
}
previousIndex1 = index1;
previousIndex2 = index2;
previousIndex3 = index3;
Integer[] indexes = new Integer[] { index1, index2, index3 };
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);
}
triangulatedFaces = new ArrayList<IndexesLoop>(indexes.size() - 2);
Integer[] indexes = new Integer[3];
try {
List<Face> facesToTriangulate = new ArrayList<Face>(Arrays.asList(this.clone()));
while (facesToTriangulate.size() > 0) {
Face face = facesToTriangulate.remove(0);
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
while (face.vertexCount() > 0) {
indexes[0] = face.getIndex(0);
indexes[1] = face.findClosestVertex(indexes[0], -1);
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(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];
previousIndex3 = indexes[2];
List<byte[]> vertexColors = null;
if (this.vertexColors != null) {
vertexColors = new ArrayList<byte[]>(3);
vertexColors.add(this.vertexColors.get(indexOf0));
vertexColors.add(this.vertexColors.get(indexOf1));
vertexColors.add(this.vertexColors.get(indexOf2));
Arrays.sort(indexes, this);
facesToTriangulate.addAll(face.detachTriangle(indexes));
triangulatedFaces.add(new IndexesLoop(indexes));
}
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.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.
*
@ -218,27 +220,34 @@ public class IndexesLoop implements Comparator<Integer>, Iterable<Integer> {
* the start index
* @param end
* 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
* an exception is thrown when the loop is not normalized (at least one
* 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) {
List<Integer> result = new ArrayList<Integer>();
public void findPath(Integer start, Integer end, List<Integer> result) throws BlenderFileException {
result.clear();
Integer node = start;
while (!node.equals(end)) {
if (result.contains(node)) {
throw new BlenderFileException("Indexes of face have infinite loops!");
}
result.add(node);
List<Integer> nextSteps = edges.get(node);
if (nextSteps.size() == 0) {
return null;
if (nextSteps == null || nextSteps.size() == 0) {
result.clear();// no directed path from start to end
return;
} else if (nextSteps.size() == 1) {
node = nextSteps.get(0);
} 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);
return result;
}
@Override

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

Loading…
Cancel
Save