Преглед на файлове

Bugfix: applying traditional face triangulation when the new algorithm
fails.

jmekaelthas преди 11 години
родител
ревизия
676ea17465

+ 61 - 64
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<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);
-                }
-
-                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));
+        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));
                 }
-
-                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;
     }
 
     /**

+ 16 - 7
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/IndexesLoop.java

@@ -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

+ 22 - 21
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<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.");