Improved solid selection stability
[convexer.git] / src / convexer.c
index 185917a615b6bd069760e55d57b63073aaf2187c..5fac72984d2f489a9aefdc7632d39e8c0489871e 100644 (file)
@@ -59,6 +59,7 @@ typedef v3f                   m4x3f[4];
 typedef v3f                    boxf[2];
 
 #define CXR_EPSILON 0.001
+#define CXR_PLANE_SIMILARITY_MAX 0.998
 #define CXR_BIG_NUMBER 1e300
 #define CXR_INTERIOR_ANGLE_MAX 0.998
 #define CXR_API 
@@ -165,6 +166,7 @@ struct cxr_texinfo
 {
    v3f uaxis, vaxis;
    v2f offset, scale;
+   double winding;
 };
 
 // simple VDF writing interface
@@ -798,7 +800,7 @@ static struct cxr_mesh *cxr_pull_best_solid(
       {
          struct cxr_loop *loop = cxr_ab_ptr(&mesh->loops, polya->loop_start+j);
          if( plane_polarity( planeb, vertices[loop->index] ) > 0.001 ||
-             v3_dot(polya->normal,polyb->normal) > 0.98500 )
+             v3_dot(polya->normal,polyb->normal) > CXR_PLANE_SIMILARITY_MAX )
          {
             edge_tagged[i] = 1;
             break;
@@ -836,7 +838,7 @@ static struct cxr_mesh *cxr_pull_best_solid(
             struct cxr_polygon *polyj = cxr_ab_ptr(&mesh->polys,connected_planes[j]);
             struct cxr_polygon *polyk = cxr_ab_ptr(&mesh->polys,connected_planes[k]);
 
-            if( v3_dot(polyj->normal, polyk->normal) > 0.98500 )
+            if( v3_dot(polyj->normal, polyk->normal) > CXR_PLANE_SIMILARITY_MAX )
                goto IL_TAG_VERT;
          }
       }
@@ -910,8 +912,8 @@ IL_TAG_NEXT_VERT:;
          edge_tagged[i] = 1;
    }
 
-   /* Debug stuff -- 
-   for( int i=0; i<vertex_count; i++ )
+   
+   for( int i=0; i<vert_buffer->count; i++ )
       if( vertex_tagged[i] )
          cxr_debug_box( vertices[i], 0.03, (v4f){0.0,0.0,0.0,1.0});
 
@@ -924,7 +926,7 @@ IL_TAG_NEXT_VERT:;
       if( hot_edge[i] )
          cxr_debug_line( vertices[ edge->i0 ], vertices[ edge->i1 ], (v4f){0.0,1.0,1.0,1.0});
    }
-   */
+   
 
    // count regions
    int *faces_tagged = malloc(mesh->polys.count*sizeof(int));
@@ -940,6 +942,12 @@ IL_TAG_NEXT_VERT:;
    }
    *candidates = malloc( mesh->polys.count *sizeof(struct csolid) );
    int candidate_count = 0;
+   
+   struct tedge
+   {
+      int i0, i1;
+   } 
+   *edge_references = malloc( mesh->edges.count *sizeof(struct tedge) );
 
    for( int i=0; i<mesh->polys.count; i++ )
    {
@@ -973,11 +981,15 @@ IL_TAG_NEXT_VERT:;
             {
                if( !edge_tagged[loop->edge_index] )
                {
+                  // TODO(harry):
+                  // 
                   // Need to look ahead 1 step to make sure he does not want
                   // to add any more planes that are coplanar with some of 
                   // our existing group
                   //
-                  // TODO: is this unused due to hotedge improvements? leaving for safety...
+                  // This can sort out SOME invalid configs, but not all.
+                  // It would be nice to find a more robust clustering algorithm for this.
+                  //
 
                   struct cxr_polygon *poly_to_add = cxr_ab_ptr(&mesh->polys, loop->poly_right );
                   for( int l=0; l < poly_to_add->loop_total; l++ )
@@ -995,15 +1007,13 @@ IL_TAG_NEXT_VERT:;
                      for( int m=0; m<solid_len; m++ )
                      {
                         struct cxr_polygon *polym = cxr_ab_ptr(&mesh->polys,solid[m]);
-                        if( v3_dot( polym->normal, future_face->normal ) > 0.98500 )
+                        if( v3_dot( polym->normal,future_face->normal) > CXR_PLANE_SIMILARITY_MAX)
                            goto IL_SKIP_PLANE_ADD;
                      }
 
                      IL_SKIP_SIMILAR_PLANES:;
                   }
 
-                  // This plane passed all checks so we can add it to the current solid
-
                   solid[ solid_len ++ ] = loop->poly_right;
                   faces_tagged[ loop->poly_right ] = i;
                   changed = 1;
@@ -1017,6 +1027,25 @@ IL_TAG_NEXT_VERT:;
       if(changed) 
          goto IL_SEARCH_CONTINUE;
 
+      // The current method can create some invalid configurations
+      // filter those out that dont work and un-tag the faces
+      for( int j=0; j<solid_len-1; j++ )
+      {
+         for( int k=j+1; k<solid_len; k++ )
+         {
+            struct cxr_polygon *polyj = cxr_ab_ptr(&mesh->polys, solid[j]),
+                               *polyk = cxr_ab_ptr(&mesh->polys, solid[k]);
+            
+            if( v3_dot( polyj->normal, polyk->normal ) > CXR_PLANE_SIMILARITY_MAX )
+            {
+               for( int l=0; l<solid_len; l++ )
+                  faces_tagged[ solid[l] ] = -1;
+
+               goto IL_CANCEL_SOLID;
+            }
+         }
+      }
+
       // Add entry
       struct csolid *csolid = &candidates[candidate_count ++];
       csolid->start = solid_buffer_len;
@@ -1033,8 +1062,12 @@ IL_TAG_NEXT_VERT:;
       v3_divs( csolid->center, solid_len, csolid->center );
 
       solid_buffer_len += solid_len;
+
+      IL_CANCEL_SOLID:;
    }
 
+   free( edge_references );
+
    // Create all candidates who have one or less non-manifolds edges
    //   Loop each candidate, determine the manifold, and pick the best one
 
@@ -1267,13 +1300,6 @@ IL_TAG_NEXT_VERT:;
 
          exist_face->loop_total = -1;
       }
-
-      // Split manifold up by unique planes if it has more than 1
-      // otherwise, just use that face
-      //
-      // TODO: Need to build new manifold in sections, stably
-      //       currently there is an unsupported case where the manifold splits
-      //       are on located on an implicit face, causing 1-length manifolds.
       
       int new_polys = 0;
       int pullmesh_new_start = pullmesh->polys.count;
@@ -1299,7 +1325,7 @@ IL_TAG_NEXT_VERT:;
          // When it is even, it appears that internal implicit geometry is required, so we
          // need to fold the loops we create. Its really weird, but for some reason works on 
          // the geometry rules we've defined.
-         //   TODO: Find a well defined rule here.
+         //   TODO(harry): Find a well defined rule here.
 
          int collapse_used_segments = (u32)fewest_manifold_splits & 0x1? 0: 1;
 
@@ -1608,12 +1634,13 @@ static void cxr_calculate_axis(
       v2_zero( transform->offset );
 
       v2_div( (v2f){128.0, 128.0}, texture_res, transform->scale );
+      transform->winding = 1.0;
       return;
    }
    
    // Detect if UV is reversed
    double winding = v2_cross( tT, bT ) >= 0.0f? 1.0f: -1.0f;
-
+   
    // UV projection reference
    v2f vY, vX;
    v2_muls((v2f){1,0}, winding, vX);
@@ -1678,6 +1705,7 @@ static void cxr_calculate_axis(
    v3_copy( vaxis, transform->vaxis );
    v2_copy( tex_offset, transform->offset );
    v2_copy( uv_scale, transform->scale );
+   transform->winding = winding;
 }
 
 CXR_API struct cxr_input_mesh *cxr_decompose(struct cxr_input_mesh *src)
@@ -1751,9 +1779,6 @@ CXR_API struct cxr_input_mesh *cxr_decompose(struct cxr_input_mesh *src)
    struct cxr_auto_buffer solids;
    cxr_ab_init( &solids, sizeof(struct cxr_mesh *), 2 );
 
-   // TODO: Preprocessor stages
-   //  - Split mesh up into islands before doing anything here
-   //  - Snap vertices to grid (0.25u) ?
    while(1)
    {
       struct cxr_mesh *res = cxr_pull_best_solid( main_mesh, &abverts, 0, &error );
@@ -1884,7 +1909,7 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
    // human editable.
 
    v3f avg_normal, refv, refu, refn;
-   v3_zero(refv); v3_zero(refu); v4_zero(refn);
+   v3_zero(refv); v3_zero(refu); v3_zero(refn);
    
    for( int i=0; i<mesh->polys.count; i++ )
    {
@@ -1892,9 +1917,10 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
       v3_add( poly->normal, avg_normal, avg_normal );
    }
    v3_divs( avg_normal, mesh->polys.count, avg_normal );
-   v3_normalize( avg_normal );   // TODO: This can be zero length. Should add a safety check
+   v3_normalize( avg_normal );   // TODO(harry): This can be zero length. Should add a safety check
                                  // normalize function that checks for small length before
                                  // carrying out, otherwise we get inf/nan values...
+
    int n_cardinal = cxr_cardinal( avg_normal, -1 );
 
    // Approximately matching the area of the result brush faces to the actual area
@@ -1979,11 +2005,13 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
       }
    }
    
+   // TODO(harry): This currently only supports power 2 displacements
+   //              its quite straightforward to upgrade it.
+   //
    int dispedge[16];
    v2f corner_uvs[4];
    int dispedge_count;
    int disp_count = 0;
-   struct cxr_texinfo texinfo_shared;
 
    for( int i=0; i<mesh->polys.count; i++ )
    {
@@ -2014,8 +2042,8 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
             
             // Consume (remove) faces we use for corners
             basepoly->loop_total = -1;
-
-            cxr_debug_box( cxr_ab_ptr(abverts,l0->index),0.08,(v4f){0.0,0.0,1.0,1.0});
+            
+            //cxr_debug_box( cxr_ab_ptr(abverts,l0->index),0.08,(v4f){0.0,0.0,1.0,1.0});
 
             // Collect edges 
             // --------------------
@@ -2166,15 +2194,10 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
                }
             }
 
-            for( int j=0; j<5; j++ )
-            {
-               cxr_log( "%d %d %d %d %d\n", grid[j*5+0],grid[j*5+1],grid[j*5+2],grid[j*5+3],grid[j*5+4]);
-            }
-
             // Create brush vertices based on UV map
             
             // Create V reference based on first displacement.
-            // TODO: This is not the most stable selection method!
+            // TODO(harry): This is not the moststable selection method!
             //       faces can come in any order, so the first disp will of course
             //       always vary. Additionaly the triangle can be oriented differently.
             //
@@ -2191,11 +2214,17 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
                cxr_calculate_axis( &tx, tri_ref, corner_uvs, (v2f){512,512} );
 
                v3_muls( tx.vaxis, -1.0, refv );
-               int v_cardinal = cxr_cardinal( refv, n_cardinal );
-               v3_copy( avg_normal, refn );
+               int v_cardinal = cxr_cardinal( refv, -1 );
+
+               v3_cross( tx.vaxis, tx.uaxis, refn );
+               v3_muls( refn, -tx.winding, refn );
+
+               int n1_cardinal = cxr_cardinal( refn, v_cardinal );
+               
+               //v3_copy( avg_normal, refn );
                int u_cardinal = 0;
-               if( u_cardinal == n_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
-               if( u_cardinal == n_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
+               if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
+               if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
 
                v3_zero(refu);
                refu[u_cardinal] = tx.uaxis[u_cardinal] > 0.0? 1.0: -1.0;
@@ -2206,13 +2235,16 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
                v3_muladds( face_center, refn, 1.5, pn );
                v3_muladds( face_center, refv, 1.5, pv );
                v3_muladds( face_center, refu, 1.5, pu );
-
-               cxr_debug_line( p0, pn, (v4f){0.0,0.0,1.0,1.0});
-               cxr_debug_line( p0, pv, (v4f){0.0,1.0,0.0,1.0});
-               cxr_debug_line( p0, pu, (v4f){1.0,0.0,0.0,1.0});
-               cxr_debug_line( tri_ref[0], tri_ref[1], (v4f){1.0,1.0,1.0,1.0} );
-               cxr_debug_line( tri_ref[1], tri_ref[2], (v4f){1.0,1.0,1.0,1.0} );
-               cxr_debug_line( tri_ref[2], tri_ref[0], (v4f){1.0,1.0,1.0,1.0} );
+               
+               if( cxr_settings.debug )
+               {
+                  cxr_debug_line( p0, pn, (v4f){0.0,0.0,1.0,1.0});
+                  cxr_debug_line( p0, pv, (v4f){0.0,1.0,0.0,1.0});
+                  cxr_debug_line( p0, pu, (v4f){1.0,0.0,0.0,1.0});
+                  cxr_debug_line( tri_ref[0], tri_ref[1], (v4f){1.0,1.0,1.0,1.0} );
+                  cxr_debug_line( tri_ref[1], tri_ref[2], (v4f){1.0,1.0,1.0,1.0} );
+                  cxr_debug_line( tri_ref[2], tri_ref[0], (v4f){1.0,1.0,1.0,1.0} );
+               }
             }
 
             // Create world cordinates
@@ -2232,14 +2264,25 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
             }
 
             double *colour = colours_random[cxr_range(disp_count,8)];
-            cxr_debug_arrow( world_corners[0], world_corners[1], avg_normal, 0.1, colour );
-            cxr_debug_arrow( world_corners[1], world_corners[2], avg_normal, 0.1, colour );
-            cxr_debug_arrow( world_corners[2], world_corners[3], avg_normal, 0.1, colour );
-            cxr_debug_arrow( world_corners[3], world_corners[0], avg_normal, 0.1, colour );
 
             for( int j=0; j<4; j++ )
                v3_muladds( world_corners[j], refn, -1.0, world_corners[j+4] );
             
+            if( cxr_settings.debug )
+            {
+               cxr_debug_arrow( world_corners[0], world_corners[1], avg_normal, 0.1, colour );
+               cxr_debug_arrow( world_corners[1], world_corners[2], avg_normal, 0.1, colour );
+               cxr_debug_arrow( world_corners[2], world_corners[3], avg_normal, 0.1, colour );
+               cxr_debug_arrow( world_corners[3], world_corners[0], avg_normal, 0.1, colour );
+            }
+
+            /*
+            cxr_debug_arrow( world_corners[0+4], world_corners[1+4], avg_normal, 0.1, colour );
+            cxr_debug_arrow( world_corners[1+4], world_corners[2+4], avg_normal, 0.1, colour );
+            cxr_debug_arrow( world_corners[2+4], world_corners[3+4], avg_normal, 0.1, colour );
+            cxr_debug_arrow( world_corners[3+4], world_corners[0+4], avg_normal, 0.1, colour );
+            */
+
             // Apply world transform
             for( int j=0; j<8; j++ )
             {
@@ -2247,11 +2290,9 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
                world_corners[j][2] += cxr_context.offset_z;
             }
 
-            if( disp_count == 0 )
-            {
-               cxr_calculate_axis( &texinfo_shared, world_corners, world_uv, 
-                  (v2f){ matptr->res[0], matptr->res[1] } );
-            }
+            struct cxr_texinfo texinfo_shared;
+            cxr_calculate_axis( &texinfo_shared, world_corners, world_uv, 
+               (v2f){ matptr->res[0], matptr->res[1] } );
 
             // Write brush
             cxr_vdf_node( output, "solid" );
@@ -2382,8 +2423,6 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputme
       }
    }
 
-   cxr_log( "Disp count: %d\n", disp_count );
-
    // Main loop
 #if 0
    int pool[25];
@@ -2499,6 +2538,44 @@ IL_DISP_ERROR_COUNT:
    free( vertinfo );
 }
 
+static int cxr_solid_checkerr(struct cxr_mesh *mesh, struct cxr_auto_buffer *abverts )
+{
+   int err_count = 0;
+
+   for( int i=0; i<mesh->polys.count; i++ )
+   {
+      int plane_err = 0;
+
+      struct cxr_polygon *poly = cxr_ab_ptr(&mesh->polys,i);
+      v4f plane;
+
+      normal_to_plane( poly->normal, poly->center, plane );
+      
+      for( int j=0; j<poly->loop_total; j++ )
+      {
+         struct cxr_loop *loop = cxr_ab_ptr(&mesh->loops, poly->loop_start+j);
+         double *vert = cxr_ab_ptr(abverts,loop->index);
+
+         if( fabs(plane_polarity(plane,vert)) > 0.0025 )
+         {
+            err_count ++;
+            plane_err ++;
+
+            v3f ref;
+            plane_project_point( plane, vert, ref );
+
+            cxr_debug_line( ref, vert, colour_error );
+            cxr_debug_box( vert, 0.1, colour_error );
+         }
+      }
+
+      if( plane_err )
+         cxr_debug_poly( mesh, poly, cxr_ab_ptr(abverts,0), colour_error );
+   }
+   
+   return err_count;
+}
+
 CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *output)
 {
    // Split mesh into islands
@@ -2506,20 +2583,17 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *
    struct cxr_mesh *main_mesh = cxr_to_internal_format(src, &abverts);
 
    u32 error = 0x00;
+   int invalid_count = 0;
 
    struct solidinf
    {
       struct cxr_mesh *pmesh;
-      int is_displacement;
+      int is_displacement, invalid;
    };
 
    struct cxr_auto_buffer solids;
    cxr_ab_init( &solids, sizeof(struct solidinf), 2 );
 
-   // TODO: Preprocessor stages
-   //  - Split mesh up into islands before doing anything here    (DONE)
-   //  - Snap vertices to grid (0.25u)                            ?
-   
    // Preprocessor 1: Island seperation
    //  ---------------
 
@@ -2534,7 +2608,7 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *
    }
    cxr_ab_push( &solids, &(struct solidinf){main_mesh,0} );
    
-   // Preprocessor 2: Displacement break-out
+   // Preprocessor 2: Displacement break-out and error checking
    //  ---------------
    for( int i=0; i<solids.count; i++ )
    {
@@ -2554,6 +2628,12 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *
          }
       }
       
+      if( cxr_solid_checkerr( pinf->pmesh, &abverts ) )
+      {
+         pinf->invalid = 1;
+         invalid_count ++;
+      }
+
       continue;
       IL_SOLID_IS_DISPLACEMENT:;
       
@@ -2569,7 +2649,7 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *
    {
       struct solidinf pinf = *(struct solidinf *)cxr_ab_ptr(&solids, i);
 
-      if( pinf.is_displacement )
+      if( pinf.is_displacement || pinf.invalid )
          continue;
 
       while(1)
@@ -2598,6 +2678,8 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *
 
                   if( error ) break;
                }
+               else
+                  break;
             }
             else 
                break;