Bugfix: applying traditional face triangulation when the new algorithm
fails.
This commit is contained in:
parent
068ab69ed9
commit
676ea17465
@ -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!");
|
||||
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];
|
||||
|
||||
Arrays.sort(indexes, this);
|
||||
facesToTriangulate.addAll(face.detachTriangle(indexes));
|
||||
triangulatedFaces.add(new IndexesLoop(indexes));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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…
x
Reference in New Issue
Block a user