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