fixes some invalid solids
[convexer.git] / cxr / cxr.h
index bb904c4d00ba8ce0522935f03787d0145e11c4e8..9f11096925ab8b105784ec411a5ced306951b39b 100644 (file)
--- a/cxr/cxr.h
+++ b/cxr/cxr.h
@@ -45,7 +45,7 @@
    IMPLEMENTATION
 */
 
-#define CXR_API 
+#define CXR_API
 #define CXR_EPSILON 0.001
 #define CXR_PLANE_SIMILARITY_MAX 0.998
 #define CXR_BIG_NUMBER 1e300
@@ -146,7 +146,7 @@ struct cxr_static_mesh
    struct cxr_edge
    {
       i32 i0, i1;
-      i32 freestyle;
+      i32 freestyle, sharp;
    }
    *edges;
 
@@ -170,7 +170,7 @@ struct cxr_static_mesh
    struct cxr_material
    {
       i32 res[2];
-      const char *name;
+      char *name;
    }
    *materials;
 
@@ -278,7 +278,8 @@ enum cxr_soliderr
    k_soliderr_no_solids,
    k_soliderr_degenerate_implicit,
    k_soliderr_non_coplanar_vertices,
-   k_soliderr_non_convex_poly
+   k_soliderr_non_convex_poly,
+   k_soliderr_bad_result
 };
 
 /*
@@ -489,7 +490,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src )
    }
    fprintf( fp, "};\n" );
 
-   fprintf( fp, "struct cxr_static_loop test_loops[] = {\n" );
+   fprintf( fp, "cxr_static_loop test_loops[] = {\n" );
    for( int i=0; i<src->loop_count; i ++ )
    {
       fprintf( fp, "   {%d, %d},\n",
@@ -498,7 +499,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src )
    }
    fprintf( fp, "};\n" );
 
-   fprintf( fp, "struct cxr_polygon test_polys[] = {\n" );
+   fprintf( fp, "cxr_polygon test_polys[] = {\n" );
    for( int i=0; i <src->poly_count; i++ )
    {
       fprintf( fp, "   {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n",
@@ -513,18 +514,19 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src )
    }
    fprintf( fp, "};\n" );
 
-   fprintf( fp, "struct cxr_edge test_edges[] = {\n" );
+   fprintf( fp, "cxr_edge test_edges[] = {\n" );
    for( int i=0; i<src->edge_count; i++ )
    {
-      fprintf( fp, "   {%d, %d, %d},\n",
+      fprintf( fp, "   {%d, %d, %d, %d},\n",
          src->edges[i].i0,
          src->edges[i].i1,
-         src->edges[i].freestyle
+         src->edges[i].freestyle,
+         src->edges[i].sharp
       );
    }
    fprintf( fp, "};\n" );
 
-   fprintf( fp, "struct cxr_static_mesh test_mesh = {\n" );
+   fprintf( fp, "cxr_static_mesh test_mesh = {\n" );
    fprintf( fp, "   .vertices = test_verts,\n" );
    fprintf( fp, "   .loops = test_loops,\n" );
    fprintf( fp, "   .edges = test_edges,\n" );
@@ -621,10 +623,12 @@ static void cxr_mesh_clean_edges( cxr_mesh *mesh )
          {
             cxr_edge *orig_edge = &mesh->edges[ orig_edge_id ];
             edge.freestyle = orig_edge->freestyle;
+            edge.sharp = orig_edge->sharp;
          }
          else
          {
             edge.freestyle = 0;
+            edge.sharp = 0;
          }
 
          cxr_ab_push( &new_edges, &edge );
@@ -711,9 +715,15 @@ static int cxr_mesh_link_loops( cxr_mesh *mesh )
             if( *edge == -1 )
             {
                *edge = i;
-               break;
+               goto next;
             }
          }
+         
+         /* Overflowed edge mapping... Duplicated faces. */
+         free( polygon_edge_map );
+         return 0;
+
+         next:;
       }
    }
    for( int i = 0; i < mesh->abpolys.count; i ++ )
@@ -1001,7 +1011,7 @@ static int *cxr_mesh_reflex_edges( cxr_mesh *mesh )
       edge_tagged[lp->edge_index] = 0;
 
       cxr_polygon *polya = &mesh->polys[ lp->poly_left ],
-                         *polyb = &mesh->polys[ lp->poly_right ];
+                  *polyb = &mesh->polys[ lp->poly_right ];
 
       v4f planeb;
       normal_to_plane(polyb->normal, polyb->center, planeb);
@@ -1320,16 +1330,70 @@ static void cxr_link_manifold(
    struct temp_manifold *manifold
 ){
    cxr_loop **edge_list = malloc( sizeof(*edge_list) * solid->edge_count );
+   int *temp_solid = malloc( solid->count *sizeof(int) );
+   int  temp_solid_len = 0;
 
    int init_reverse = 0;
    int unique_edge_count = 0;
+   int discard_splits = 1;
+   
+   /* Try remove splitting faces first */
+   {
+      for( int j=0; j<solid->count; j++ )
+      {
+         cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ];
+         int interior_count = 0;
+
+         for( int k=0; k<poly->loop_total; k++ )
+         {
+            cxr_loop *loop = &mesh->loops[ poly->loop_start+k ];
 
-   /* Gather list of unique edges */
+            for( int l=0; l<solid->count; l++ )
+               if( loop->poly_right == solid_buffer[solid->start+l] )
+               {
+                  interior_count ++;
+                  goto next;
+               }
+
+            next:;
+         }
+         
+         if( interior_count < poly->loop_total-1 )
+            continue;
+         
+         temp_solid[ temp_solid_len ++ ] = solid_buffer[solid->start+j];
+      }
+
+      if( temp_solid_len < 3 )
+      {
+         /* Revert back to normal */
+         free( temp_solid );
+
+         temp_solid = &solid_buffer[ solid->start ];
+         temp_solid_len = solid->count;
+         discard_splits = 0;
+      }
+      else
+      {
+         /* Overwrite original solid */
+         for( int j=0; j<temp_solid_len; j++ )
+            solid_buffer[ solid->start+j ] = temp_solid[ j ];
+
+         solid->count = temp_solid_len;
+      }
+
+      if( discard_splits )
+         free( temp_solid );
+   }
 
    for( int j=0; j<solid->count; j++ )
    {
       cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ];
 
+      /* when discarding, if a face has only one loop that points outwards,
+       * we keep it */
+
+
       for( int k=0; k<poly->loop_total; k++ )
       {
          cxr_loop *loop = &mesh->loops[ poly->loop_start+k ];
@@ -1537,22 +1601,30 @@ static int cxr_build_implicit_geo( cxr_mesh *mesh, int new_polys, int start )
    return 1;
 }
 
-/* 
- * Convexer's main algorithm
- *
- * Return the best availible convex solid from mesh, and patch the existing mesh
- * to fill the gap where the new mesh left it.
- *
- * Returns NULL if shape is already convex or empty.
- * This function will not preserve edge data such as freestyle, sharp etc.
- */
-static cxr_mesh *cxr_pull_best_solid(
-      cxr_mesh *mesh, 
-      int preserve_more_edges,
-      enum cxr_soliderr *err )
+static int cxr_reflex_err( cxr_mesh *mesh )
 {
-   *err = k_soliderr_none;
+   int error = 0;
+   int *reflex_check = cxr_mesh_reflex_edges( mesh );
+
+   v3f *temp = cxr_ab_ptr(mesh->p_abverts, 0);
 
+   for( int i=0; i<mesh->abedges.count; i++ )
+   {
+      if( reflex_check[i] )
+      {
+         cxr_debug_line( temp[mesh->edges[i].i0],
+                         temp[mesh->edges[i].i1],
+                         colour_error );
+         error ++;
+      }
+   }
+
+   free( reflex_check );
+   return error;
+}
+
+static int cxr_non_manifold_err( cxr_mesh *mesh )
+{
    if( !cxr_mesh_link_loops(mesh) )
    {
 #ifdef CXR_DEBUG
@@ -1564,18 +1636,43 @@ static cxr_mesh *cxr_pull_best_solid(
       for( int i=0; i<mesh->abloops.count; i++ )
       {
          cxr_loop *lp = &mesh->loops[i];
+         cxr_edge *edge = &mesh->edges[lp->edge_index];
+         cxr_debug_line( verts[edge->i0], verts[edge->i1], colours_random[1] );
 
          if( lp->poly_left == -1 || lp->poly_right == -1 )
          {
-            cxr_edge *edge = &mesh->edges[lp->edge_index];
             cxr_debug_line( verts[edge->i0], verts[edge->i1], colour_error );
          }
       }
 #endif
+      return 1;
+   }
+
+   return 0;
+}
+
+/* 
+ * Convexer's main algorithm
+ *
+ * Return the best availible convex solid from mesh, and patch the existing mesh
+ * to fill the gap where the new mesh left it.
+ *
+ * Returns NULL if shape is already convex or empty.
+ * This function will not preserve edge data such as freestyle, sharp etc.
+ */
+static cxr_mesh *cxr_pull_best_solid(
+      cxr_mesh *mesh, 
+      int preserve_more_edges,
+      enum cxr_soliderr *err )
+{
+   *err = k_soliderr_none;
+
+   if( cxr_non_manifold_err( mesh ) )
+   {
       *err = k_soliderr_non_manifold;
       return NULL;
    }
-   
+
    int *edge_tagged = cxr_mesh_reflex_edges( mesh );
    int *vertex_tagged = cxr_mesh_reflex_vertices( mesh );
 
@@ -1689,7 +1786,7 @@ static cxr_mesh *cxr_pull_best_solid(
       
       struct temp_manifold manifold;
       cxr_link_manifold( mesh, solid, solid_buffer, &manifold);
-      
+
       if( manifold.status == k_manifold_err )
       {
          *err = k_soliderr_bad_manifold;
@@ -1917,7 +2014,17 @@ static cxr_mesh *cxr_pull_best_solid(
       free(solid_buffer);
       free(candidates);
       free(best_manifold.loops);
-   
+      
+      /*
+       * Do final checks on the mesh to make sure we diddn't introduce any
+       * errors
+       */
+      if( cxr_non_manifold_err( pullmesh ) || cxr_reflex_err( pullmesh ) )
+      {
+         *err = k_soliderr_bad_result;
+         return NULL;
+      }
+
       return pullmesh;
    }
 
@@ -1925,6 +2032,9 @@ static cxr_mesh *cxr_pull_best_solid(
    free(candidates);
    free(best_manifold.loops);
 
+   if( cxr_non_manifold_err( mesh ) || cxr_reflex_err( mesh ) )
+      *err = k_soliderr_bad_result;
+
    return NULL;
 }
 
@@ -2050,7 +2160,14 @@ CXR_API void cxr_free_world( cxr_world *world )
 
    cxr_ab_free( &world->abverts );
    cxr_ab_free( &world->absolids );
-   free( world->materials );
+
+   if( world->materials )
+   {
+      for( int i=0; i<world->material_count; i++ )
+         free( world->materials[i].name );
+
+      free( world->materials );
+   }
    free( world );
 }
 
@@ -2176,6 +2293,13 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode )
       size_t dsize = sizeof(cxr_material) * src->material_count;
       world->materials = malloc( dsize );
       memcpy( world->materials, src->materials, dsize );
+
+      for( int i=0; i<src->material_count; i++ )
+      {
+         world->materials[i].name = malloc(strlen(src->materials[i].name) +1);
+         strcpy( world->materials[i].name, src->materials[i].name );
+      }
+      world->material_count = src->material_count;
    }
    else world->materials = NULL;
 
@@ -2726,7 +2850,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world,
     * TODO(harry): Error checking is needed here for bad input data
     */
 
-   int dispedge[16];
+   int dispedge[17];
    v2f corner_uvs[4];
    int dispedge_count;
    int disp_count = 0;
@@ -3011,7 +3135,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world,
                tx = (double)k/(double)(5-1);
                v3_lerp( lside0, lside1, tx, lref );
                v3_muls( verts[grid[index]], ctx->scale, vworld );
-               v3_add( ctx->offset, vworld, ctx->offset );
+               v3_add( ctx->offset, vworld, vworld );
 
                v3_sub( vworld, lref, vdelta );
                v3_copy( vdelta, normals[index] );
@@ -3031,7 +3155,6 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world,
                                             world_corners[side[0]] );
             
             cxr_vdf_kv( output, "material", matptr->name );
-
             cxr_vdf_kaxis( output, "uaxis", 
                   texinfo_shared.uaxis, 
                   texinfo_shared.offset[0],