Correct for face culling after triangulation
This commit is contained in:
parent
8472849d6d
commit
17bc98b54f
@ -340,6 +340,47 @@ public class ObjUtils {
|
||||
return normalizeVector(new DefaultFloatTuple(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the normal vector of a triangle defined by three vertices.
|
||||
*
|
||||
* @param v1 The first vertex of the triangle.
|
||||
* @param v2 The second vertex of the triangle.
|
||||
* @param v3 The third vertex of the triangle.
|
||||
* @return The normal vector of the triangle.
|
||||
*/
|
||||
public static FloatTuple calculateNormalVector(FloatTuple v1, FloatTuple v2, FloatTuple v3) {
|
||||
FloatTuple u = subtractVectors(v2, v1);
|
||||
FloatTuple v = subtractVectors(v3, v1);
|
||||
|
||||
return normalizeVector(crossProduct(u, v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts two vectors.
|
||||
*
|
||||
* @param v1 the first vector
|
||||
* @param v2 the second vector
|
||||
* @return a new FloatTuple representing the subtraction of v2 from v1
|
||||
*/
|
||||
public static FloatTuple subtractVectors(FloatTuple v1, FloatTuple v2) {
|
||||
return new DefaultFloatTuple(v1.getX() - v2.getX(), v1.getY() - v2.getY(), v1.getZ() - v2.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cross product of two vectors.
|
||||
*
|
||||
* @param v1 the first vector
|
||||
* @param v2 the second vector
|
||||
* @return the cross product of the given vectors
|
||||
*/
|
||||
public static FloatTuple crossProduct(FloatTuple v1, FloatTuple v2) {
|
||||
return new DefaultFloatTuple(
|
||||
v1.getY() * v2.getZ() - v1.getZ() * v2.getY(),
|
||||
v1.getZ() * v2.getX() - v1.getX() * v2.getZ(),
|
||||
v1.getX() * v2.getY() - v1.getY() * v2.getX()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the average of a list of vertices.
|
||||
* @param vertices The list of vertices
|
||||
|
@ -9,6 +9,8 @@ import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator;
|
||||
import org.locationtech.jts.triangulate.tri.Tri;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -17,6 +19,8 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class TriangulationHelper {
|
||||
private static final Logger log = LoggerFactory.getLogger(TriangulationHelper.class);
|
||||
|
||||
public static DefaultObj simpleTriangulate(DefaultObj obj) {
|
||||
return de.javagl.obj.ObjUtils.triangulate(obj, new DefaultObj());
|
||||
}
|
||||
@ -55,6 +59,9 @@ public abstract class TriangulationHelper {
|
||||
if (face.getNumVertices() == 3) {
|
||||
output.addFace(face);
|
||||
continue;
|
||||
} else if (face.getNumVertices() < 3) {
|
||||
log.debug("Face has less than 3 vertices, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate the new triangulated faces
|
||||
@ -69,23 +76,56 @@ public abstract class TriangulationHelper {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates constrained Delaunay triangulation faces based on a given object and face.
|
||||
*
|
||||
* @param obj The input object containing 3D polygon data.
|
||||
* @param face The specific face of the object to be triangulated.
|
||||
* @return A list of generated object faces representing the triangulated faces.
|
||||
*/
|
||||
public static List<ObjFace> generateCDTFaces(DefaultObj obj, DefaultObjFace face) {
|
||||
PolygonWithOriginalIndices polygonWithIndices = createProjectedPolygon(obj, face);
|
||||
// Calculate the face normal
|
||||
Coordinate normal = vertexToCoordinate(ObjUtils.calculateNormalVector(
|
||||
obj.getVertex(face.getVertexIndices()[0]),
|
||||
obj.getVertex(face.getVertexIndices()[1]),
|
||||
obj.getVertex(face.getVertexIndices()[2])));
|
||||
|
||||
// Project the 3D face to a 2D polygon with only X and Y coordinates.
|
||||
// This is necessary because the JTS library only works with 2D polygons for triangulation.
|
||||
PolygonWithOriginalIndices polygonWithIndices = createProjectedPolygon(obj, face, normal);
|
||||
Polygon polygon = polygonWithIndices.polygon();
|
||||
Map<Coordinate3D, Integer> vertexIndexMap = polygonWithIndices.vertexIndexMap();
|
||||
|
||||
// Triangulate the polygon
|
||||
ConstrainedDelaunayTriangulator triangulator = new ConstrainedDelaunayTriangulator(polygon);
|
||||
List<Tri> triangles = triangulator.getTriangles();
|
||||
|
||||
// Create the new faces to add to the OBJ
|
||||
List<ObjFace> newFaces = new ArrayList<>();
|
||||
for (Tri tri : triangles) {
|
||||
// Map the 2D triangle vertices back to the original vertex indices
|
||||
int[] vertexIndices = new int[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Coordinate coord = tri.getCoordinate(i);
|
||||
vertexIndices[i] = getNearbyValue(vertexIndexMap, coord);
|
||||
}
|
||||
|
||||
// Calculate the normal of the triangle, and verify that it has the same orientation as the original face
|
||||
// If it does not, invert the vertex order to ensure the normal points in the same direction
|
||||
// This is necessary for correct face culling.
|
||||
Coordinate triangleNormal = calculateNormal(
|
||||
vertexToCoordinate(obj.getVertex(vertexIndices[0])),
|
||||
vertexToCoordinate(obj.getVertex(vertexIndices[1])),
|
||||
vertexToCoordinate(obj.getVertex(vertexIndices[2])));
|
||||
if (normalsHaveDifferentDirection(triangleNormal, normal)) {
|
||||
int temp = vertexIndices[0];
|
||||
vertexIndices[0] = vertexIndices[2];
|
||||
vertexIndices[2] = temp;
|
||||
}
|
||||
|
||||
// Add the new face to the list
|
||||
if (vertexIndices[0] != -1 && vertexIndices[1] != -1 && vertexIndices[2] != -1) {
|
||||
//DefaultObjFace newFace = ObjUtils.createFaceWithNewIndices(face, vertexIndices);
|
||||
// TODO: Add support for texture coordinates and normals (create a new map to map vertex index and normal/texcoord index)
|
||||
DefaultObjFace newFace = new DefaultObjFace(vertexIndices, null, null);
|
||||
newFaces.add(newFace);
|
||||
}
|
||||
@ -100,15 +140,10 @@ public abstract class TriangulationHelper {
|
||||
*
|
||||
* @param obj The input OBJ containing 3D polygon data.
|
||||
* @param face The specific face of the OBJ to be projected and triangulated.
|
||||
* @param normal the normal of the face to determine its orientation in 3D space
|
||||
* @return A polygon in 2D space suitable for use with JTS.
|
||||
*/
|
||||
private static PolygonWithOriginalIndices createProjectedPolygon(DefaultObj obj, DefaultObjFace face) {
|
||||
// Calculate the normal of the polygon to determine its orientation in 3D space
|
||||
Coordinate normal = calculateNormal(
|
||||
vertexToCoordinate(obj.getVertex(face.getVertexIndices()[0])),
|
||||
vertexToCoordinate(obj.getVertex(face.getVertexIndices()[1])),
|
||||
vertexToCoordinate(obj.getVertex(face.getVertexIndices()[2])));
|
||||
|
||||
private static PolygonWithOriginalIndices createProjectedPolygon(DefaultObj obj, DefaultObjFace face, Coordinate normal) {
|
||||
// Create a list for storing the projected 2D coordinates
|
||||
List<Coordinate> projectedCoords = new ArrayList<>();
|
||||
Map<Coordinate3D, Integer> vertexIndexMap = new HashMap<>();
|
||||
@ -129,6 +164,7 @@ public abstract class TriangulationHelper {
|
||||
projectedCoords.add(projectedCoords.get(0));
|
||||
}
|
||||
|
||||
// Create the polygon
|
||||
GeometryFactory factory = new GeometryFactory();
|
||||
Polygon polygon = factory.createPolygon(projectedCoords.toArray(new Coordinate[0]));
|
||||
|
||||
@ -188,7 +224,11 @@ public abstract class TriangulationHelper {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return -1; // Or any default value.
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean normalsHaveDifferentDirection(Coordinate normal1, Coordinate normal2) {
|
||||
return dotProduct(normal1, normal2) < 0;
|
||||
}
|
||||
|
||||
|
||||
@ -204,7 +244,6 @@ public abstract class TriangulationHelper {
|
||||
private static Coordinate normalize(Coordinate vector) {
|
||||
double magnitude = magnitude(vector);
|
||||
if (magnitude == 0) {
|
||||
// Handle potential divide by zero if the vector is a zero vector
|
||||
return new Coordinate(0, 0, 0);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user