Skip to content

Commit 159b647

Browse files
committed
Subdiv: add sharp edge feature (based on threshold)
1 parent d2c7071 commit 159b647

1 file changed

Lines changed: 175 additions & 1 deletion

File tree

src/slg/shapes/subdiv.cpp

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,11 +581,164 @@ struct InterpolatedValues {
581581
const int stride;
582582
};
583583

584+
585+
// Structure to represent an edge with sharpness information
586+
struct EdgeInfo {
587+
int v0, v1; // Vertex indices (always stored with v0 < v1)
588+
float sharpness; // Sharpness value (0.0 = smooth, 1.0 = sharp)
589+
float creaseWeight; // Crease weight for subdivision
590+
591+
EdgeInfo(int vertex0, int vertex1)
592+
: v0(std::min(vertex0, vertex1)), v1(std::max(vertex0, vertex1)),
593+
sharpness(0.0f), creaseWeight(0.0f) {}
594+
};
595+
596+
// Calculate face normal for a triangle
597+
Vector calculateTriangleNormal(const ExtTriangleMesh &mesh, const Triangle &tri) {
598+
auto vertices = std::span<Point>(mesh.GetVertices(), mesh.GetTotalVertexCount());
599+
const Point &p0 = vertices[tri.v[0]];
600+
const Point &p1 = vertices[tri.v[1]];
601+
const Point &p2 = vertices[tri.v[2]];
602+
603+
const Vector edge1 = Vector(p1 - p0);
604+
const Vector edge2 = Vector(p2 - p0);
605+
606+
return Normalize(Cross(edge1, edge2));
607+
}
608+
609+
// Calculate dihedral angle between two adjacent triangles
610+
float calculateDihedralAngle(const Vector &normal1, const Vector &normal2,
611+
const Vector &edgeVector) {
612+
// Calculate the angle between the two face normals
613+
const float cosAngle = Dot(normal1, normal2);
614+
const float angle = acosf(Clamp(cosAngle, -1.0f, 1.0f));
615+
616+
// Calculate the edge direction (normalized)
617+
const Vector edgeDir = Normalize(edgeVector);
618+
619+
// Determine if the angle is convex or concave using cross product
620+
const Vector crossProd = Cross(normal1, normal2);
621+
const float sign = Dot(crossProd, edgeDir);
622+
623+
// Return the angle in the range [0, π]
624+
return (sign >= 0) ? angle : M_PI - angle;
625+
}
626+
627+
// Process edges to determine sharpness based on dihedral angles
628+
void processEdgesForSharpness(const ExtTriangleMesh &mesh,
629+
std::map<std::pair<int, int>, EdgeInfo> &edgeMap,
630+
float sharpnessThresholdRadians) {
631+
632+
auto triangles = std::span<Triangle>(mesh.GetTriangles(), mesh.GetTotalTriangleCount());
633+
auto vertices = std::span<Point>(mesh.GetVertices(), mesh.GetTotalVertexCount());
634+
635+
// Clear any existing edge information
636+
edgeMap.clear();
637+
638+
// First pass: build edge map
639+
for (const auto& tri : triangles) {
640+
641+
// Process each edge of the triangle
642+
for (int i = 0; i < 3; ++i) {
643+
int v0 = tri.v[i];
644+
int v1 = tri.v[(i + 1) % 3];
645+
646+
// Create a canonical edge key (always store with v0 < v1)
647+
auto edgeKey = std::make_pair(std::min(v0, v1), std::max(v0, v1));
648+
649+
// If edge doesn't exist in map, create it
650+
if (edgeMap.find(edgeKey) == edgeMap.end()) {
651+
edgeMap.try_emplace(edgeKey, v0, v1);
652+
}
653+
}
654+
}
655+
656+
// Second pass: calculate dihedral angles for edges shared by two triangles
657+
for (auto &edgePair : edgeMap) {
658+
EdgeInfo &edgeInfo = edgePair.second;
659+
int v0 = edgeInfo.v0;
660+
int v1 = edgeInfo.v1;
661+
662+
// Find all triangles that share this edge
663+
std::vector<size_t> adjacentTris;
664+
for (size_t triIndex = 0; triIndex < triangles.size(); ++triIndex) {
665+
const Triangle &tri = triangles[triIndex];
666+
// Check if triangle contains this edge (in either order)
667+
if ((tri.v[0] == v0 && tri.v[1] == v1) ||
668+
(tri.v[1] == v0 && tri.v[2] == v1) ||
669+
(tri.v[2] == v0 && tri.v[0] == v1) ||
670+
(tri.v[0] == v1 && tri.v[1] == v0) ||
671+
(tri.v[1] == v1 && tri.v[2] == v0) ||
672+
(tri.v[2] == v1 && tri.v[0] == v0)) {
673+
adjacentTris.push_back(triIndex);
674+
}
675+
}
676+
677+
// If edge is on boundary (only one adjacent triangle), mark as sharp
678+
if (adjacentTris.size() == 1) {
679+
edgeInfo.sharpness = 1.0f;
680+
edgeInfo.creaseWeight = 1.0f;
681+
continue;
682+
}
683+
684+
// For internal edges, calculate dihedral angle
685+
if (adjacentTris.size() >= 2) {
686+
const Triangle &tri1 = triangles[adjacentTris[0]];
687+
const Triangle &tri2 = triangles[adjacentTris[1]];
688+
689+
const Vector normal1 = calculateTriangleNormal(mesh, tri1);
690+
const Vector normal2 = calculateTriangleNormal(mesh, tri2);
691+
692+
// Calculate edge vector
693+
const Vector edgeVector = Vector(vertices[v1] - vertices[v0]);
694+
const float dihedralAngle = calculateDihedralAngle(normal1, normal2, edgeVector);
695+
696+
// Determine sharpness based on threshold
697+
if (dihedralAngle > sharpnessThresholdRadians) {
698+
edgeInfo.sharpness = 1.0f;
699+
edgeInfo.creaseWeight = 1.0f;
700+
} else {
701+
edgeInfo.sharpness = 0.0f;
702+
edgeInfo.creaseWeight = 0.0f;
703+
}
704+
}
705+
}
706+
}
707+
708+
// Debug function to output edge sharpness information
709+
void debugEdgeSharpness(const ExtTriangleMesh &mesh, float sharpnessThresholdRadians) {
710+
std::map<std::pair<int, int>, EdgeInfo> edgeMap;
711+
processEdgesForSharpness(mesh, edgeMap, sharpnessThresholdRadians);
712+
713+
int sharpCount = 0;
714+
int smoothCount = 0;
715+
int boundaryCount = 0;
716+
717+
for (const auto &edgePair : edgeMap) {
718+
const EdgeInfo &edge = edgePair.second;
719+
if (edge.sharpness > 0.5f) {
720+
sharpCount++;
721+
} else {
722+
smoothCount++;
723+
}
724+
}
725+
726+
SLG_LOG("Edge sharpness analysis:");
727+
SLG_LOG(" Total edges: " << edgeMap.size());
728+
SLG_LOG(" Sharp edges: " << sharpCount << " (" <<
729+
(100.0f * sharpCount / edgeMap.size()) << "%)");
730+
SLG_LOG(" Smooth edges: " << smoothCount << " (" <<
731+
(100.0f * smoothCount / edgeMap.size()) << "%)");
732+
}
733+
734+
584735
// Adaptive topology refiner factory function
585736
TopologyRefinerPtr createTopologyAdaptiveRefiner(
586737
const int * vertIndicesPerFace,
587738
int numVertices,
588739
int numFaces,
740+
const std::map<std::pair<int, int>, EdgeInfo>& edgeMap,
741+
const float sharpnessThresholdRadians,
589742
const Far::PatchTableFactory::Options& patchOptions
590743
) {
591744

@@ -607,6 +760,21 @@ TopologyRefinerPtr createTopologyAdaptiveRefiner(
607760
desc.numVertsPerFace = vertsPerFace.data();
608761
desc.vertIndicesPerFace = vertIndicesPerFace;
609762

763+
// Set creases (sharp edges)
764+
std::vector<int> creases;
765+
std::vector<float> creaseWeights;
766+
for (const auto &edgePair : edgeMap) {
767+
const EdgeInfo &edge = edgePair.second;
768+
if (edge.sharpness > 0.5f) { // TODO parameter
769+
creases.push_back(edge.v0);
770+
creases.push_back(edge.v1);
771+
creaseWeights.push_back(10.0f);
772+
}
773+
}
774+
desc.numCreases = creaseWeights.size();
775+
desc.creaseVertexIndexPairs = creases.data();
776+
desc.creaseWeights = creaseWeights.data();
777+
610778
// Create refiner
611779
using RefinerFactory = Far::TopologyRefinerFactory<Far::TopologyDescriptor>;
612780
TopologyRefinerPtr refiner(
@@ -617,6 +785,7 @@ TopologyRefinerPtr createTopologyAdaptiveRefiner(
617785
);
618786
assert(refiner);
619787

788+
620789
return refiner;
621790

622791
}
@@ -652,12 +821,17 @@ struct Surface {
652821
patchTableOptions.endCapType =
653822
Far::PatchTableFactory::Options::ENDCAP_BSPLINE_BASIS;
654823

824+
// Mark edges for sharpness
825+
std::map<std::pair<int, int>, EdgeInfo> edgeMap;
826+
processEdgesForSharpness(srcMesh, edgeMap, 0.5);
827+
655828
// Construct refiner
656829
refiner = std::move(
657830
createTopologyAdaptiveRefiner(
658831
reinterpret_cast<const int *>(srcMesh.GetTriangles()),
659832
srcMesh.GetTotalVertexCount(),
660833
srcMesh.GetTotalTriangleCount(),
834+
edgeMap,
661835
patchTableOptions
662836
)
663837
);
@@ -699,7 +873,7 @@ struct Surface {
699873
/// Input:
700874
/// V2
701875
/// / \
702-
/// / \
876+
/// / \
703877
/// / \
704878
/// V0----V1
705879
///

0 commit comments

Comments
 (0)