@@ -4227,5 +4227,197 @@ const char* getError()
42274227 return Error::s_message;
42284228}
42294229
4230+ static bool isConvex (const GeometryData& geom, const GeometryPartition::Polygon& polygon) {
4231+ if (polygon.vertex_count < 3 ) return false ;
4232+ if (polygon.vertex_count == 3 ) return true ;
4233+
4234+ const Vec3Attributes positions = geom.getPositions ();
4235+ if (!positions.values ) return false ;
4236+
4237+ Vec3 normal = {0 , 0 , 0 };
4238+ // Compute polygon normal using Newell's method for robustness
4239+ // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
4240+ for (int i = 0 ; i < polygon.vertex_count ; ++i) {
4241+ int curr_idx = polygon.from_vertex + i;
4242+ int next_idx = polygon.from_vertex + ((i + 1 ) % polygon.vertex_count );
4243+
4244+ Vec3 v_curr = positions.get (curr_idx);
4245+ Vec3 v_next = positions.get (next_idx);
4246+
4247+ normal.x += (v_curr.y - v_next.y ) * (v_curr.z + v_next.z );
4248+ normal.y += (v_curr.z - v_next.z ) * (v_curr.x + v_next.x );
4249+ normal.z += (v_curr.x - v_next.x ) * (v_curr.y + v_next.y );
4250+ }
4251+
4252+ float normal_lensq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z ;
4253+ if (normal_lensq < 1e-6f ) return false ;
4254+
4255+ bool sign_positive = false ;
4256+ bool sign_negative = false ;
4257+
4258+ for (int i = 0 ; i < polygon.vertex_count ; ++i) {
4259+ int prev_idx = polygon.from_vertex + ((i - 1 + polygon.vertex_count ) % polygon.vertex_count );
4260+ int curr_idx = polygon.from_vertex + i;
4261+ int next_idx = polygon.from_vertex + ((i + 1 ) % polygon.vertex_count );
4262+
4263+ Vec3 v_prev = positions.get (prev_idx);
4264+ Vec3 v_curr = positions.get (curr_idx);
4265+ Vec3 v_next = positions.get (next_idx);
4266+
4267+ // Vectors from current vertex to adjacent vertices
4268+ Vec3 edge1 = {v_prev.x - v_curr.x , v_prev.y - v_curr.y , v_prev.z - v_curr.z };
4269+ Vec3 edge2 = {v_next.x - v_curr.x , v_next.y - v_curr.y , v_next.z - v_curr.z };
4270+
4271+ // Cross product to get turn direction
4272+ Vec3 cross = {
4273+ edge1.y * edge2.z - edge1.z * edge2.y ,
4274+ edge1.z * edge2.x - edge1.x * edge2.z ,
4275+ edge1.x * edge2.y - edge1.y * edge2.x
4276+ };
4277+
4278+ float dot = cross.x * normal.x + cross.y * normal.y + cross.z * normal.z ;
4279+
4280+ if (dot > 1e-6f ) sign_positive = true ;
4281+ else if (dot < -1e-6f ) sign_negative = true ;
4282+
4283+ if (sign_positive && sign_negative) return false ;
4284+ }
4285+
4286+ return true ;
4287+ }
4288+
4289+ u32 triangulate (const GeometryData& geom, const GeometryPartition::Polygon& polygon, int * tri_indices, int * tmp) {
4290+ if (polygon.vertex_count < 3 ) return 0 ;
4291+ if (polygon.vertex_count == 3 ) {
4292+ tri_indices[0 ] = polygon.from_vertex ;
4293+ tri_indices[1 ] = polygon.from_vertex + 1 ;
4294+ tri_indices[2 ] = polygon.from_vertex + 2 ;
4295+ return 3 ;
4296+ }
4297+
4298+ if (isConvex (geom, polygon)) {
4299+ for (int tri = 0 ; tri < polygon.vertex_count - 2 ; ++tri) {
4300+ tri_indices[tri * 3 + 0 ] = polygon.from_vertex ;
4301+ tri_indices[tri * 3 + 1 ] = polygon.from_vertex + 1 + tri;
4302+ tri_indices[tri * 3 + 2 ] = polygon.from_vertex + 2 + tri;
4303+ }
4304+ return 3 * (polygon.vertex_count - 2 );
4305+ }
4306+
4307+ // ear clipping - the simplest (and not the most efficient) implementation
4308+ int tmp_mem[128 ]; // temporary memory if use did not pass any in `tmp`
4309+ int * vertices = tmp ? tmp : tmp_mem;
4310+
4311+ if (!tmp && polygon.vertex_count > 128 ) return 0 ;
4312+
4313+ for (int i = 0 ; i < polygon.vertex_count ; ++i) {
4314+ vertices[i] = polygon.from_vertex + i;
4315+ }
4316+
4317+ int num_vertices = polygon.vertex_count ;
4318+ int tri_count = 0 ;
4319+
4320+ const Vec3Attributes positions = geom.getPositions ();
4321+ if (!positions.values ) return 0 ;
4322+
4323+ Vec3 v0 = positions.get (vertices[0 ]);
4324+ Vec3 v1 = positions.get (vertices[1 ]);
4325+ Vec3 v2 = positions.get (vertices[2 ]);
4326+
4327+ Vec3 edge1 = {v1.x - v0.x , v1.y - v0.y , v1.z - v0.z };
4328+ Vec3 edge2 = {v2.x - v0.x , v2.y - v0.y , v2.z - v0.z };
4329+
4330+ Vec3 normal = {
4331+ edge1.y * edge2.z - edge1.z * edge2.y ,
4332+ edge1.z * edge2.x - edge1.x * edge2.z ,
4333+ edge1.x * edge2.y - edge1.y * edge2.x
4334+ };
4335+
4336+ while (num_vertices > 3 ) {
4337+ bool ear_found = false ;
4338+
4339+ for (int i = 0 ; i < num_vertices; ++i) {
4340+ const int prev = (i - 1 + num_vertices) % num_vertices;
4341+ const int curr = i;
4342+ const int next = (i + 1 ) % num_vertices;
4343+
4344+ const Vec3 vp = positions.get (vertices[prev]);
4345+ const Vec3 vc = positions.get (vertices[curr]);
4346+ const Vec3 vn = positions.get (vertices[next]);
4347+
4348+ const Vec3 e1 = {vc.x - vp.x , vc.y - vp.y , vc.z - vp.z };
4349+ const Vec3 e2 = {vn.x - vc.x , vn.y - vc.y , vn.z - vc.z };
4350+
4351+ const Vec3 cross = {
4352+ e1 .y * e2 .z - e1 .z * e2 .y ,
4353+ e1 .z * e2 .x - e1 .x * e2 .z ,
4354+ e1 .x * e2 .y - e1 .y * e2 .x
4355+ };
4356+
4357+ const float dot = cross.x * normal.x + cross.y * normal.y + cross.z * normal.z ;
4358+
4359+ // 3 vertices with wrong winding order can not make an ear
4360+ if (dot <= 0 ) continue ;
4361+
4362+ // Check if any other vertex is inside this triangle
4363+ bool is_ear = true ;
4364+ for (int j = 0 ; j < num_vertices; ++j) {
4365+ if (j == prev || j == curr || j == next) continue ;
4366+
4367+ Vec3 vt = positions.get (vertices[j]);
4368+
4369+ // Barycentric coordinate test
4370+ Vec3 v0v1 = {vc.x - vp.x , vc.y - vp.y , vc.z - vp.z };
4371+ Vec3 v0v2 = {vn.x - vp.x , vn.y - vp.y , vn.z - vp.z };
4372+ Vec3 v0vt = {vt.x - vp.x , vt.y - vp.y , vt.z - vp.z };
4373+
4374+ float dot00 = v0v2.x * v0v2.x + v0v2.y * v0v2.y + v0v2.z * v0v2.z ;
4375+ float dot01 = v0v2.x * v0v1.x + v0v2.y * v0v1.y + v0v2.z * v0v1.z ;
4376+ float dot02 = v0v2.x * v0vt.x + v0v2.y * v0vt.y + v0v2.z * v0vt.z ;
4377+ float dot11 = v0v1.x * v0v1.x + v0v1.y * v0v1.y + v0v1.z * v0v1.z ;
4378+ float dot12 = v0v1.x * v0vt.x + v0v1.y * v0vt.y + v0v1.z * v0vt.z ;
4379+
4380+ float inv_denom = 1 .0f / (dot00 * dot11 - dot01 * dot01);
4381+ float u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
4382+ float v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
4383+
4384+ if (u > 0 && v > 0 && u + v < 1 ) {
4385+ is_ear = false ;
4386+ break ;
4387+ }
4388+ }
4389+
4390+ if (is_ear) {
4391+ tri_indices[tri_count * 3 + 0 ] = vertices[prev];
4392+ tri_indices[tri_count * 3 + 1 ] = vertices[curr];
4393+ tri_indices[tri_count * 3 + 2 ] = vertices[next];
4394+ tri_count++;
4395+
4396+ // Remove current vertex from polygon
4397+ for (int k = curr; k < num_vertices - 1 ; ++k) {
4398+ vertices[k] = vertices[k + 1 ];
4399+ }
4400+ num_vertices--;
4401+ ear_found = true ;
4402+ break ;
4403+ }
4404+ }
4405+
4406+ if (!ear_found) {
4407+ // Fallback: couldn't find ear, add remaining triangle
4408+ break ;
4409+ }
4410+ }
4411+
4412+ // Add final triangle
4413+ if (num_vertices == 3 ) {
4414+ tri_indices[tri_count * 3 + 0 ] = vertices[0 ];
4415+ tri_indices[tri_count * 3 + 1 ] = vertices[1 ];
4416+ tri_indices[tri_count * 3 + 2 ] = vertices[2 ];
4417+ tri_count++;
4418+ }
4419+
4420+ return tri_count * 3 ;
4421+ }
42304422
42314423} // namespace ofbx
0 commit comments