diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java index 0aa9ae1d1..6d29617b2 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java @@ -30,6 +30,8 @@ import com.jme3.scene.plugins.blender.file.Structure; /** The indexes loop of the face. */ private IndexesLoop indexes; + + private List 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 getIndexes() { - return indexes.getAll(); + @SuppressWarnings("unchecked") + public List> getIndexes() { + if(triangulatedFaces == null) { + return Arrays.asList(indexes.getAll()); + } + List> result = new ArrayList>(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 detachTriangle(Integer[] triangleIndexes) { + private List 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 detachedFaces = new ArrayList(); - + List path = new ArrayList(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 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 triangulate(List vertices, List normals) { + public void triangulate(List vertices, List normals) { LOGGER.fine("Triangulating face."); assert indexes.size() >= 3 : "Invalid indexes amount for face. 3 is the required minimum!"; - List result = new ArrayList(); - - List facesToTriangulate = new ArrayList(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 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> faceUVS = new HashMap>(); - for (Entry> entry : faceUVCoords.entrySet()) { - List uvs = new ArrayList(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 vertexColors = null; - if (this.vertexColors != null) { - vertexColors = new ArrayList(3); - vertexColors.add(this.vertexColors.get(indexOf0)); - vertexColors.add(this.vertexColors.get(indexOf1)); - vertexColors.add(this.vertexColors.get(indexOf2)); + triangulatedFaces = new ArrayList(indexes.size() - 2); + Integer[] indexes = new Integer[3]; + + try { + List facesToTriangulate = new ArrayList(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)); } - - 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, Iterable { * 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 findPath(Integer start, Integer end) { - List result = new ArrayList(); + public void findPath(Integer start, Integer end, List 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 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 diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java index 049217742..db8aaf989 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/TemporalMesh.java @@ -207,11 +207,9 @@ public class TemporalMesh extends Geometry { */ public void triangulate() { LOGGER.fine("Triangulating temporal mesh."); - List triangulatedFaces = new ArrayList(); 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 indexes = face.getIndexes(); + List> triangulatedIndexes = face.getIndexes(); List 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 boneBuffersForVertex = new HashMap(); - Map vertexGroupsForVertex = vertexGroups.get(vertIndex); - for (Entry entry : boneIndexes.entrySet()) { - if (vertexGroupsForVertex.containsKey(entry.getKey())) { - boneBuffersForVertex.put(vertexGroupsForVertex.get(entry.getKey()), entry.getValue()); + + for(List 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 boneBuffersForVertex = new HashMap(); + Map vertexGroupsForVertex = vertexGroups.get(vertIndex); + for (Entry 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.");