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. */
|
/** 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 +
|
||||||
|
"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 +
|
} catch(BlenderFileException e) {
|
||||||
"Please apply triangulation modifier in blender as a workaround and load again!");
|
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());
|
||||||
previousIndex1 = index1;
|
indexes[0] = this.getIndex(0);
|
||||||
previousIndex2 = index2;
|
for(int i=1;i<this.vertexCount() - 1;++i) {
|
||||||
previousIndex3 = index3;
|
indexes[1] = this.getIndex(i);
|
||||||
|
indexes[2] = this.getIndex(i + 1);
|
||||||
Integer[] indexes = new Integer[] { index1, index2, index3 };
|
triangulatedFaces.add(new IndexesLoop(indexes));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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…
x
Reference in New Issue
Block a user