increase error checking
[convexer.git] / cxr / cxr.h
index 5a4542af6d1a3f4055233c6611630e13b771e10b..0af9b8c780fa68fd0b72755e5223b9ca7710c192 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
@@ -107,7 +107,7 @@ typedef struct cxr_tri_mesh cxr_tri_mesh;
 
 /* Main convexer algorithms */
 /* Convex decomp from mesh */
-CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src );
+CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode );
 CXR_API void cxr_free_world( cxr_world *world );
 CXR_API cxr_tri_mesh *cxr_world_preview( cxr_world *world );
 CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh );
@@ -170,7 +170,7 @@ struct cxr_static_mesh
    struct cxr_material
    {
       i32 res[2];
-      const char *name;
+      char *name;
    }
    *materials;
 
@@ -276,7 +276,10 @@ enum cxr_soliderr
    k_soliderr_non_manifold,
    k_soliderr_bad_manifold,
    k_soliderr_no_solids,
-   k_soliderr_degenerate_implicit
+   k_soliderr_degenerate_implicit,
+   k_soliderr_non_coplanar_vertices,
+   k_soliderr_non_convex_poly,
+   k_soliderr_bad_result
 };
 
 /*
@@ -487,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",
@@ -496,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",
@@ -511,7 +514,7 @@ 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",
@@ -522,7 +525,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src )
    }
    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" );
@@ -709,9 +712,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 ++ )
@@ -1535,6 +1544,55 @@ static int cxr_build_implicit_geo( cxr_mesh *mesh, int new_polys, int start )
    return 1;
 }
 
+static int cxr_reflex_err( cxr_mesh *mesh )
+{
+   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
+      cxr_log( "non-manifold edges are in the mesh: "
+               "implicit internal geometry does not have full support\n" );
+
+      v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
+
+      for( int i=0; i<mesh->abloops.count; i++ )
+      {
+         cxr_loop *lp = &mesh->loops[i];
+
+         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
  *
@@ -1551,16 +1609,12 @@ static cxr_mesh *cxr_pull_best_solid(
 {
    *err = k_soliderr_none;
 
-   if( !cxr_mesh_link_loops(mesh) )
+   if( cxr_non_manifold_err( mesh ) )
    {
-#ifdef CXR_DEBUG
-      cxr_log( "non-manifold edges are in the mesh: "
-               "implicit internal geometry does not have full support\n" );
-#endif
       *err = k_soliderr_non_manifold;
       return NULL;
    }
-   
+
    int *edge_tagged = cxr_mesh_reflex_edges( mesh );
    int *vertex_tagged = cxr_mesh_reflex_vertices( mesh );
 
@@ -1902,7 +1956,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;
    }
 
@@ -1910,6 +1974,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;
 }
 
@@ -1948,6 +2015,40 @@ static cxr_mesh *cxr_to_internal_format(
    return mesh;
 }
 
+static int cxr_poly_convex( cxr_mesh *mesh, cxr_polygon *poly )
+{
+   v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
+
+   for( int i=0; i<poly->loop_total; i++ )
+   {
+      int li0 = poly->loop_start + i,
+          li1 = poly->loop_start + cxr_range( i+1, poly->loop_total ),
+          li2 = poly->loop_start + cxr_range( i+2, poly->loop_total );
+      int i0 = mesh->loops[li0].index,
+          i1 = mesh->loops[li1].index,
+          i2 = mesh->loops[li2].index;
+
+      v3f v0, v1, c;
+
+      v3_sub( verts[i1], verts[i0], v0 );
+      v3_sub( verts[i2], verts[i1], v1 );
+
+      v3_cross( v0, v1, c );
+      if( v3_dot( c, poly->normal ) <= 0.0 )
+      {
+#if CXR_DEBUG
+         cxr_debug_line( verts[i0], verts[i1], colour_error );
+         cxr_debug_box( verts[i1], 0.1, colour_error );
+         cxr_debug_line( verts[i1], verts[i2], colour_error );
+         cxr_debug_line( verts[i1], poly->center, colour_error );
+#endif
+         return 0;
+      }
+   }
+
+   return 1;
+}
+
 static int cxr_solid_checkerr( cxr_mesh *mesh )
 {
    v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
@@ -2001,7 +2102,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 );
 }
 
@@ -2113,8 +2221,9 @@ CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh )
    free( mesh );
 }
 
-CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
+CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode )
 {
+   u32 error = 0x00;
    cxr_world *world = malloc( sizeof(*world) );
    
    /* Copy data to internal formats */
@@ -2126,6 +2235,13 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
       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;
 
@@ -2164,12 +2280,20 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
             if( edge->freestyle )
                goto displacement;
          }
+
+         if( !cxr_poly_convex( pinf->pmesh, poly ) )
+         {
+            pinf->invalid = 1;
+            invalid_count ++;
+            error = k_soliderr_non_convex_poly;
+         }
       }
       
       if( cxr_solid_checkerr( pinf->pmesh ) )
       {
          pinf->invalid = 1;
          invalid_count ++;
+         error = k_soliderr_non_coplanar_vertices;
       }
 
       continue;
@@ -2182,7 +2306,6 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
     * Main convex decomp algorithm
     */
    int sources_count = world->absolids.count;
-   u32 error = 0x00;
    
    if( invalid_count )
       goto decomp_failed;
@@ -2228,6 +2351,10 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
 decomp_failed:
    cxr_log( "Error %d\n", error );
    cxr_free_world( world );
+
+   if( perrcode )
+      *perrcode = error;
+
    return NULL;
 }
 
@@ -2665,7 +2792,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;
@@ -2950,7 +3077,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] );
@@ -2970,7 +3097,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],