Refactor el big #2
authorhgn <hgodden00@gmail.com>
Sun, 10 Apr 2022 04:46:14 +0000 (05:46 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 10 Apr 2022 04:46:14 +0000 (05:46 +0100)
__init__.py
src/convexer.c
src/cxr_math.h
src/cxr_mem.h

index 624bce098f7740768c3f6e2204ff5c4b25e67bdb..8cc9638c943c8d88247529c9640eda238b4049ab 100644 (file)
@@ -33,7 +33,7 @@ libc_dlclose.argtypes = [c_void_p]
 c_libcxr_log_callback = None
 c_libcxr_line_callback = None
 
-libcxr_decompose = None
+libcxr_write_test_data = None
 libcxr_context_reset = None
 libcxr_set_offset = None
 libcxr_set_scale_factor = None
@@ -223,14 +223,13 @@ class CXR_RELOAD(bpy.types.Operator):
       print( F"libcxr build time: {build_time.value}" )
    
       # Public API
-      global libcxr_decompose
-      global libcxr_convert_mesh_to_vmf
+      global libcxr_write_test_data, libcxr_convert_mesh_to_vmf
 
-      libcxr_decompose = libcxr.cxr_decompose
-      libcxr_decompose.argtypes = [\
+      libcxr_write_test_data = libcxr.cxr_write_test_data
+      libcxr_write_test_data.argtypes = [\
          POINTER(cxr_input_mesh)
       ]
-      libcxr_decompose.restype = c_int32
+      libcxr_write_test_data.restype = c_int32
 
       libcxr_convert_mesh_to_vmf = libcxr.cxr_convert_mesh_to_vmf
       libcxr_convert_mesh_to_vmf.argtypes = [\
@@ -587,8 +586,8 @@ def ent_lights(obj,context):
    kvs = ent_baseclass([ent_origin],\
    {
       "_distance": (0.0 if obj.data.cxr_data.realtime else -1.0),
-      "_light": [int(pow(obj.data.color[i],1.0/2.2)*255.0) for i in range(3)] + \
-                [int(obj.data.energy * bpy.context.scene.cxr_data.light_scale) ],
+      "_light": [int(pow(obj.data.color[i],1.0/2.2)*255.0) for i in range(3)] +\
+                [int(obj.data.energy * bpy.context.scene.cxr_data.light_scale)],
       "_lightHDR": '-1 -1 -1 1',
       "_lightscaleHDR": 1
    })
@@ -607,9 +606,11 @@ def ent_lights(obj,context):
       
       kvs['pitch'] = math.asin(fwd[2]) * 57.295779513
       kvs['angles'] = [ 0.0, math.atan2(fwd[1],fwd[0]) * 57.295779513, 0.0 ]
-      kvs['_quadratic_attn'] = 0.0  # Source spotlights + quadratic falloff look awful.
-                                    # Blender's default has a much more 'accurate' look
-                                    # They appear correct when using linear scale.
+      kvs['_quadratic_attn'] = 0.0  # Source spotlights + quadratic falloff look
+                                    # Really bad...
+                                    #
+                                    # Blender's default has a much more 'nice'
+                                    # look.
       kvs['_linear_attn'] = 1.0
    
    elif obj.data.type == 'POINT':
@@ -993,9 +994,12 @@ class CXR_WRITE_VMF(bpy.types.Operator):
          _collect.geo = []
          _collect.heros = []
 
-         transform_main = cxr_object_context( context.scene.cxr_data.scale_factor, 0.0 )
-         transform_sky = cxr_object_context( context.scene.cxr_data.skybox_scale_factor, \
-                                             context.scene.cxr_data.skybox_offset )
+         transform_main = cxr_object_context( \
+               context.scene.cxr_data.scale_factor, 0.0 )
+
+         transform_sky = cxr_object_context( \
+               context.scene.cxr_data.skybox_scale_factor, \
+               context.scene.cxr_data.skybox_offset )
          
          if 'main' in bpy.data.collections:
             _collect( bpy.data.collections['main'], transform_main )
@@ -1047,9 +1051,9 @@ class CXR_WRITE_VMF(bpy.types.Operator):
 
       return {'FINISHED'}
 
-class CXR_DECOMPOSE_SOLID(bpy.types.Operator):
-   bl_idname="convexer.decompose_solid"
-   bl_label="Decompose Solid"
+class CXR_DEV_OPERATOR(bpy.types.Operator):
+   bl_idname="convexer.dev_test"
+   bl_label="Export development data"
 
    def execute(_,context):
       libcxr_use()
@@ -1058,7 +1062,7 @@ class CXR_DECOMPOSE_SOLID(bpy.types.Operator):
       mesh_src = mesh_cxr_format(context.active_object)
       
       libcxr_reset_debug_lines()
-      libcxr_decompose( pointer(mesh_src) )
+      libcxr_write_test_data( pointer(mesh_src) )
       libcxr_batch_debug_lines()
          
       scene_redraw()
@@ -1073,7 +1077,7 @@ class CXR_INTERFACE(bpy.types.Panel):
 
    def draw(_,context):
       _.layout.operator("convexer.reload")
-      _.layout.operator("convexer.decompose_solid")
+      _.layout.operator("convexer.dev_test")
       _.layout.operator("convexer.write_vmf")
 
       settings = context.scene.cxr_data
@@ -1299,7 +1303,8 @@ class CXR_MATERIAL_PANEL(bpy.types.Panel):
                expandview = True
                drawthis = True
 
-               if 'shaders' in pdef and properties.shader not in pdef['shaders']:
+               if ('shaders' in pdef) and \
+                  (properties.shader not in pdef['shaders']):
                   continue
 
                if ptype == 'intrinsic':
@@ -1325,7 +1330,7 @@ class CXR_MATERIAL_PANEL(bpy.types.Panel):
                      else:
                         # hidden intrinsic value.
                         # Means its a float array or something not an image
-                        thisnode.label( text=F"-- hidden intrinsic '{decl}' --" )
+                        thisnode.label(text=F"-- hidden intrinsic '{decl}' --")
                   else:
                      thisnode.prop(properties,decl)
                   if expandview: _mview(pdef,thisnode)
@@ -1368,12 +1373,15 @@ class CXR_ENTITY_PANEL(bpy.types.Panel):
 
       if active_object == None: return
       
-      default_context = cxr_object_context( bpy.context.scene.cxr_data.scale_factor, 0.0 )
+      default_context = cxr_object_context( \
+            bpy.context.scene.cxr_data.scale_factor, 0.0 )
+
       ecn = cxr_intrinsic_classname( active_object )
       classname = cxr_custom_class( active_object )
 
       if ecn == None: 
-         if active_object.type == 'MESH': _.layout.prop( active_object.cxr_data, 'brushclass' )
+         if active_object.type == 'MESH': 
+            _.layout.prop( active_object.cxr_data, 'brushclass' )
          else: _.layout.prop( active_object.cxr_data, 'classname' )
 
          if classname == 'NONE':
@@ -1502,13 +1510,17 @@ class CXR_SCENE_SETTINGS(bpy.types.PropertyGroup):
    opt_vrad: bpy.props.StringProperty( name="args" )
 
    debug: bpy.props.BoolProperty(name="Debug",default=False)
-   scale_factor: bpy.props.FloatProperty(name="VMF Scale factor",default=32.0,min=1.0)
-   skybox_scale_factor: bpy.props.FloatProperty(name="Sky Scale factor",default=1.0,min=0.01)
+   scale_factor: bpy.props.FloatProperty( name="VMF Scale factor", \
+         default=32.0,min=1.0)
+   skybox_scale_factor: bpy.props.FloatProperty( name="Sky Scale factor", \
+         default=1.0,min=0.01)
+
    skybox_offset: bpy.props.FloatProperty(name="Sky offset",default=-4096.0)
    light_scale: bpy.props.FloatProperty(name="Light Scale",default=1.0/5.0)
-   displacement_cardinal: bpy.props.BoolProperty(name="Cardinal displacements",default=True)
-   include_names: bpy.props.BoolProperty(name="Append original file names",default=True)
-   lightmap_scale: bpy.props.IntProperty(name="Global Lightmap Scale",default=12)
+   include_names: bpy.props.BoolProperty(name="Append original file names",\
+         default=True)
+   lightmap_scale: bpy.props.IntProperty(name="Global Lightmap Scale",\
+         default=12)
 
 class CXR_DETECT_COMPILERS(bpy.types.Operator):
    bl_idname="convexer.detect_compilers"
@@ -1526,7 +1538,7 @@ class CXR_DETECT_COMPILERS(bpy.types.Operator):
 
       return {'FINISHED'}
 
-classes = [ CXR_RELOAD, CXR_DECOMPOSE_SOLID, CXR_INTERFACE, \
+classes = [ CXR_RELOAD, CXR_DEV_OPERATOR, CXR_INTERFACE, \
             CXR_WRITE_VMF, CXR_MATERIAL_PANEL, CXR_IMAGE_SETTINGS,\
             CXR_MODEL_SETTINGS, CXR_ENTITY_SETTINGS, CXR_CUBEMAP_SETTINGS,\
             CXR_LIGHT_SETTINGS, CXR_SCENE_SETTINGS, CXR_DETECT_COMPILERS,\
@@ -1596,7 +1608,8 @@ def register():
                  '') for _ in cxr_shaders],\
       default = next(iter(cxr_shaders)))
    
-   annotations_dict["asset_id"] = bpy.props.IntProperty(name="intl_assetid",default=0)
+   annotations_dict["asset_id"] = bpy.props.IntProperty(name="intl_assetid",\
+         default=0)
 
    _dvmt_propogate( cxr_shader_params )
    vmt_param_dynamic_class = type(
index 773a3d354cf2e4ab55761b361ad5b221428fffd0..47ba1282f5992467a164e87726c44e01afa60ad0 100644 (file)
@@ -14,9 +14,6 @@
       - Light patch BSP files; remove unwanted realtime effects
       - Fastest VTF compressor (thanks to Richgel999 and stb)
 
-   To come:
-      - high quality level overviews automatically for CS:GO (csRadar)
-
    Program structure:
 
    File/folder       Lang     Purpose
@@ -75,9 +72,6 @@ typedef v3f                   boxf[2];
 #include "cxr_math.h"
 #include "cxr_mem.h"
 
-// Utility
-// ============================================
-
 static v4f colours_random[] = 
 {
    { 0.863, 0.078, 0.235, 0.4 },
@@ -98,7 +92,6 @@ static int cxr_range(int x, int bound)
    return x % bound;
 }
 
-
 typedef struct cxr_edge cxr_edge;
 typedef struct cxr_input_mesh cxr_input_mesh;
 typedef struct cxr_input_loop cxr_input_loop;
@@ -134,7 +127,7 @@ struct cxr_input_mesh
       i32 loop_start, loop_total;
       v3f normal;
       v3f center;
-      i32 material_id;  // -1: interior material
+      i32 material_id; /* -1: interior material (nodraw) */
    }
    *polys;
    
@@ -168,7 +161,7 @@ struct cxr_solid
 
 struct cxr_mesh
 {
-   struct cxr_auto_buffer
+   struct cxr_abuffer
       abedges,
       abloops,
       abpolys,
@@ -190,7 +183,9 @@ struct cxr_texinfo
    double winding;
 };
 
-// simple VDF writing interface
+/* 
+ * Simplified VDF writing interface. No allocations or nodes, just write to file
+ */
 struct cxr_vdf
 {
    FILE *fp;
@@ -243,9 +238,6 @@ static void cxr_mesh_update( cxr_mesh *mesh )
    mesh->loops = cxr_ab_ptr(&mesh->abloops, 0);
 }
 
-// Debugging callbacks
-// =============================================================================
-
 static v4f colour_error = { 1.0f, 0.0f, 0.0f, 1.0f };
 static v4f colour_face_graph = { 1.0f, 1.0f, 1.0f, 0.03f };
 static v4f colour_success = { 0.0f, 1.0f, 0.0f, 1.0f };
@@ -268,56 +260,6 @@ static void cxr_log( const char *fmt, ... )
    fputs(buf,stdout);
 }
 
-static void cxr_debug_line( v3f p0, v3f p1, v4f colour )
-{
-   if( cxr_line_func )
-      cxr_line_func( p0, p1, colour );
-}
-
-static void cxr_debug_box( v3f p0, double sz, v4f colour )
-{
-   v3f a,b,c,d,
-       a1,b1,c1,d1;
-   v3_add(p0, (v3f){-sz,-sz,-sz}, a);
-   v3_add(p0, (v3f){-sz, sz,-sz}, b);
-   v3_add(p0, (v3f){ sz, sz,-sz}, c);
-   v3_add(p0, (v3f){ sz,-sz,-sz}, d);
-   v3_add(p0, (v3f){-sz,-sz,sz}, a1);
-   v3_add(p0, (v3f){-sz, sz,sz}, b1);
-   v3_add(p0, (v3f){ sz, sz,sz}, c1);
-   v3_add(p0, (v3f){ sz,-sz,sz}, d1);
-
-   cxr_debug_line( a,b, colour );
-   cxr_debug_line( b,c, colour );
-   cxr_debug_line( c,d, colour );
-   cxr_debug_line( d,a, colour );
-   cxr_debug_line( a1,b1, colour );
-   cxr_debug_line( b1,c1, colour );
-   cxr_debug_line( c1,d1, colour );
-   cxr_debug_line( d1,a1, colour );
-   cxr_debug_line( a,a1, colour );
-   cxr_debug_line( b,b1, colour );
-   cxr_debug_line( c,c1, colour );
-   cxr_debug_line( d,d1, colour );
-}
-
-static void cxr_debug_arrow( v3f p0, v3f p1, v3f normal, double sz, v4f colour )
-{
-   v3f dir, tan, p2, p3;
-   v3_sub(p1,p0,dir);
-   v3_normalize(dir);
-
-   v3_cross(dir,normal,tan);
-   v3_muladds( p1,dir, -sz, p2 );
-   v3_muladds( p2,tan,sz,p3 );
-   cxr_debug_line( p1, p3, colour );
-   v3_muladds( p2,tan,-sz,p3 );
-   cxr_debug_line( p1, p3, colour );
-   cxr_debug_line( p0, p1, colour );
-}
-
-// Public API
-// =========================================================================
 
 CXR_API void cxr_context_reset(void)
 {
@@ -387,35 +329,45 @@ CXR_API void cxr_vdf_node(cxr_vdf *vdf, const char *str)
    vdf->level ++;
 }
 
-CXR_API void cxr_vdf_edon(cxr_vdf *vdf)
+CXR_API void cxr_vdf_edon( cxr_vdf *vdf )
 {
    vdf->level --;
    cxr_vdf_put( vdf, "}\n" );
 }
 
-CXR_API void cxr_vdf_kv(cxr_vdf *vdf, const char *strk, const char *strv)
+CXR_API void cxr_vdf_kv( cxr_vdf *vdf, const char *strk, const char *strv )
 {
    cxr_vdf_printf( vdf, "\"%s\" \"%s\"\n", strk, strv );
 }
 
-static void cxr_vdf_ki32(cxr_vdf *vdf, const char *strk, i32 val)
+/*
+ * Data-type specific Keyvalues
+ */
+static void cxr_vdf_ki32( cxr_vdf *vdf, const char *strk, i32 val )
 {
    cxr_vdf_printf( vdf, "\"%s\" \"%d\"\n", strk, val );
 }
-static void cxr_vdf_kdouble(cxr_vdf *vdf, const char *strk, double val)
+
+static void cxr_vdf_kdouble( cxr_vdf *vdf, const char *strk, double val )
 {
    cxr_vdf_printf( vdf, "\"%s\" \"%f\"\n", strk, val );
 }
-static void cxr_vdf_kaxis(cxr_vdf *vdf, const char *strk, v3f normal, double offset, double scale)
-{
-   cxr_vdf_printf( vdf, "\"%s\" \"[%f %f %f %f] %f\"\n", strk, normal[0],normal[1],normal[2],offset,scale );
+
+static void cxr_vdf_kaxis( cxr_vdf *vdf, const char *strk, 
+      v3f normal, double offset, double scale
+){
+   cxr_vdf_printf( vdf, "\"%s\" \"[%f %f %f %f] %f\"\n", 
+         strk, normal[0], normal[1],normal[2], offset, scale );
 }
-static void cxr_vdf_kv3f(cxr_vdf *vdf, const char *strk, v3f v)
+
+static void cxr_vdf_kv3f( cxr_vdf *vdf, const char *strk, v3f v )
 {
    cxr_vdf_printf( vdf, "\"%s\" \"[%f %f %f]\"\n", strk, v[0], v[1], v[2] );
 }
-static void cxr_vdf_karrdouble(cxr_vdf *vdf, const char *strk, int id, double *doubles, int count)
-{
+
+static void cxr_vdf_karrdouble( cxr_vdf *vdf, const char *strk, 
+      int id, double *doubles, int count
+){
    cxr_vdf_put(vdf,"");
    fprintf( vdf->fp, "\"%s%d\" \"", strk, id );
    for( int i=0; i<count; i++ )
@@ -425,35 +377,92 @@ static void cxr_vdf_karrdouble(cxr_vdf *vdf, const char *strk, int id, double *d
    }
    fprintf( vdf->fp, "\"\n" );
 }
-static void cxr_vdf_karrv3f(cxr_vdf *vdf, const char *strk, int id, v3f *vecs, int count)
-{
+
+static void cxr_vdf_karrv3f( cxr_vdf *vdf, const char *strk, 
+      int id, v3f *vecs, int count
+){
    cxr_vdf_put(vdf,"");
    fprintf( vdf->fp, "\"%s%d\" \"", strk, id );
    for( int i=0; i<count; i++ )
    {
-      if( i == count-1 ) fprintf( vdf->fp, "%f %f %f", vecs[i][0], vecs[i][1], vecs[i][2] );
-      else fprintf( vdf->fp, "%f %f %f ", vecs[i][0], vecs[i][1], vecs[i][2] );
+      const char *format = i == count-1? "%f %f %f": "%f %f %f ";
+      fprintf( vdf->fp, format, vecs[i][0], vecs[i][1], vecs[i][2] );
    }
    fprintf( vdf->fp, "\"\n" );
 }
-static void cxr_vdf_plane(cxr_vdf *vdf, const char *strk, v3f a, v3f b, v3f c )
+
+static void cxr_vdf_plane( cxr_vdf *vdf, const char *strk, v3f a, v3f b, v3f c )
 {
    cxr_vdf_printf( vdf, "\"%s\" \"(%f %f %f) (%f %f %f) (%f %f %f)\"\n",
          strk, a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2] );
 }
+
 static void cxr_vdf_colour255(cxr_vdf *vdf, const char *strk, v4f colour)
 {
    v4f scale;
    v4_muls( colour, 255.0, scale );
-   cxr_vdf_printf( vdf, "\"%s\" \"%d %d %d %d\"\n",strk,(int)scale[0], (int)scale[1], (int)scale[2], (int)scale[3]);
+   cxr_vdf_printf( vdf, "\"%s\" \"%d %d %d %d\"\n", 
+         strk,(int)scale[0], (int)scale[1], (int)scale[2], (int)scale[3]);
+}
+
+/*
+ * Debugging line drawing
+ */
+static void cxr_debug_line( v3f p0, v3f p1, v4f colour )
+{
+   if( cxr_line_func )
+      cxr_line_func( p0, p1, colour );
+}
+
+static void cxr_debug_box( v3f p0, double sz, v4f colour )
+{
+   v3f a,b,c,d,
+       a1,b1,c1,d1;
+   v3_add(p0, (v3f){-sz,-sz,-sz}, a);
+   v3_add(p0, (v3f){-sz, sz,-sz}, b);
+   v3_add(p0, (v3f){ sz, sz,-sz}, c);
+   v3_add(p0, (v3f){ sz,-sz,-sz}, d);
+   v3_add(p0, (v3f){-sz,-sz,sz}, a1);
+   v3_add(p0, (v3f){-sz, sz,sz}, b1);
+   v3_add(p0, (v3f){ sz, sz,sz}, c1);
+   v3_add(p0, (v3f){ sz,-sz,sz}, d1);
+
+   cxr_debug_line( a,b, colour );
+   cxr_debug_line( b,c, colour );
+   cxr_debug_line( c,d, colour );
+   cxr_debug_line( d,a, colour );
+   cxr_debug_line( a1,b1, colour );
+   cxr_debug_line( b1,c1, colour );
+   cxr_debug_line( c1,d1, colour );
+   cxr_debug_line( d1,a1, colour );
+   cxr_debug_line( a,a1, colour );
+   cxr_debug_line( b,b1, colour );
+   cxr_debug_line( c,c1, colour );
+   cxr_debug_line( d,d1, colour );
 }
 
-// Public API
-// =========================================================================
+/*
+ * Draw arrow with the tips oriented along normal
+ */
+static void cxr_debug_arrow( v3f p0, v3f p1, v3f normal, double sz, v4f colour )
+{
+   v3f dir, tan, p2, p3;
+   v3_sub(p1,p0,dir);
+   v3_normalize(dir);
+
+   v3_cross(dir,normal,tan);
+   v3_muladds( p1,dir, -sz, p2 );
+   v3_muladds( p2,tan,sz,p3 );
+   cxr_debug_line( p1, p3, colour );
+   v3_muladds( p2,tan,-sz,p3 );
+   cxr_debug_line( p1, p3, colour );
+   cxr_debug_line( p0, p1, colour );
+}
 
-static void cxr_debug_poly(cxr_mesh *mesh, 
-                           cxr_polygon *poly, 
-                           v4f colour )
+/*
+ * Draw arrows CCW around polygon, draw normal vector from center
+ */
+static void cxr_debug_poly( cxr_mesh *mesh, cxr_polygon *poly, v4f colour )
 {
    v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
 
@@ -488,11 +497,12 @@ static void cxr_debug_mesh(cxr_mesh *mesh, v4f colour )
    }
 }
 
-static cxr_mesh *cxr_alloc_mesh(int edge_count, 
-                                       int loop_count, 
-                                       int poly_count,
-                                       cxr_auto_buffer *abverts)
-{
+/*
+ * abverts is a pointer to an existing vertex buffer
+ */
+static cxr_mesh *cxr_alloc_mesh( int edge_count, int loop_count, int poly_count,
+      cxr_abuffer *abverts
+){
    cxr_mesh *mesh = malloc(sizeof(cxr_mesh));
    cxr_ab_init(&mesh->abedges, sizeof(cxr_edge), edge_count);
    cxr_ab_init(&mesh->abloops, sizeof(cxr_loop), loop_count);
@@ -504,7 +514,7 @@ static cxr_mesh *cxr_alloc_mesh(int edge_count,
    return mesh;
 }
 
-static void cxr_free_mesh(cxr_mesh *mesh)
+static void cxr_free_mesh( cxr_mesh *mesh )
 {
    cxr_ab_free(&mesh->abedges);
    cxr_ab_free(&mesh->abloops);
@@ -515,9 +525,9 @@ static void cxr_free_mesh(cxr_mesh *mesh)
 /*
  * Rebuilds edge data for mesh (useful to get rid of orphaned edges)
  */
-static void cxr_mesh_clean_edges(cxr_mesh *mesh)
+static void cxr_mesh_clean_edges( cxr_mesh *mesh )
 {
-   cxr_auto_buffer new_edges;
+   cxr_abuffer new_edges;
    cxr_ab_init( &new_edges, sizeof(cxr_edge), mesh->abedges.count );
 
    for( int i=0; i<mesh->abpolys.count; i++ )
@@ -532,7 +542,7 @@ static void cxr_mesh_clean_edges(cxr_mesh *mesh)
          int i0 = cxr_min(lp0->index, lp1->index),
              i1 = cxr_max(lp0->index, lp1->index);
          
-         // See if edge exists before adding it
+         /* Check if edge exists before adding */
          for( int k=0; k<new_edges.count; k++ )
          {
             cxr_edge *edge = cxr_ab_ptr(&new_edges,k);
@@ -548,8 +558,10 @@ static void cxr_mesh_clean_edges(cxr_mesh *mesh)
          lp0->edge_index = new_edges.count;
 
          cxr_edge edge = { i0, i1 };
-         // --- ! ---
-         // Copy extra information (sharp,freestyle.. etc) here!
+
+         /*
+          * Copy extra information from original edges
+          */
 
          if( orig_edge_id < mesh->abedges.count )
          {
@@ -561,7 +573,6 @@ static void cxr_mesh_clean_edges(cxr_mesh *mesh)
             edge.freestyle = 0;
          }
 
-         // --- ! ---
          cxr_ab_push( &new_edges, &edge );
 
 IL_EDGE_CREATED:;
@@ -578,9 +589,9 @@ IL_EDGE_CREATED:;
  * Remove 0-length faces from mesh (we mark them light that for deletion
  * Remove all unused loops as a result of removing those faces
  */
-static void cxr_mesh_clean_faces(cxr_mesh *mesh)
+static void cxr_mesh_clean_faces( cxr_mesh *mesh )
 {
-   cxr_auto_buffer loops_new;
+   cxr_abuffer loops_new;
    cxr_ab_init( &loops_new, sizeof(cxr_loop), mesh->abloops.count );
 
    int new_length=0;
@@ -623,7 +634,7 @@ static void cxr_mesh_clean_faces(cxr_mesh *mesh)
  *
  * Returns 0 if there is non-manifold geomtry (aka: not watertight)
  */
-static int cxr_mesh_link_loops(cxr_mesh *mesh)
+static int cxr_mesh_link_loops( cxr_mesh *mesh )
 {
    i32 *polygon_edge_map = malloc(mesh->abedges.count*2 *sizeof(i32));
 
@@ -681,69 +692,78 @@ static int cxr_mesh_link_loops(cxr_mesh *mesh)
 }
 
 /* 
- * Add polygon to mesh based on array of loops
+ * Create new empty polygon with known loop count
+ * Must be filled and completed by the following functions!
  */
-static cxr_polygon *cxr_create_poly( 
-   cxr_mesh *mesh,
-   cxr_loop poly[],
-   int len, 
-   int start, 
-   int max )
+static int cxr_create_poly( cxr_mesh *mesh, int loop_count )
 {
    v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
 
-   if( len < 3 )
+   if( loop_count < 3 )
    {
-      cxr_log( "tried to add new poly with length %d!\n", len );
-      
-      if( len >= 2 )
-      {
-         for( int j=0; j<len; j++ )
-         {
-            int i0 = poly[j].index,
-                i1 = poly[cxr_range(j+1,len)].index;
+      cxr_log( "tried to add new poly with length %d!\n", loop_count );
+      return 0;
+   }
 
-            cxr_debug_line( verts[i0], verts[i1], colour_error );
-         }
-      }
+   cxr_ab_reserve( &mesh->abpolys, 1 );
+   cxr_ab_reserve( &mesh->abloops, loop_count );
+   cxr_mesh_update( mesh );
 
-      return NULL;
-   }
+   cxr_polygon *poly = &mesh->polys[ mesh->abpolys.count ];
+
+   poly->loop_start = mesh->abloops.count;
+   poly->loop_total = 0;
+   poly->material_id = -1;
+   v3_zero( poly->center );
+
+   return 1;
+}
+
+/*
+ * Add one index to the polygon created by the above function
+ */
+static void cxr_poly_push_index( cxr_mesh *mesh, int id )
+{
+   v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
 
    int nface_id = mesh->abpolys.count;
-   cxr_polygon *new_face = cxr_ab_empty(&mesh->abpolys);
+   cxr_polygon *poly = &mesh->polys[ nface_id ];
 
-   /* Calculate new face information; normal, center etc */
+   cxr_loop *new_loop = &mesh->loops[ poly->loop_start + poly->loop_total ];
 
-   new_face->loop_start = mesh->abloops.count;
-   new_face->loop_total = len;
-   new_face->material_id = -1;
-   v3_zero( new_face->center );
+   new_loop->poly_left = nface_id;
+   new_loop->poly_right = -1;
+   new_loop->index = id;
+   new_loop->edge_index = 0;
+   v2_zero(new_loop->uv);
 
-   for( int j=0; j<len; j++ )
-   {
-      int i0 = poly[cxr_range(start+j,max)].index;
-      cxr_loop *new_loop = cxr_ab_empty(&mesh->abloops);
+   v3_add( poly->center, verts[new_loop->index], poly->center );
 
-      new_loop->poly_left = nface_id;
-      new_loop->poly_right = -1;
-      new_loop->index = i0;
-      new_loop->edge_index = 0;
-      v2_zero(new_loop->uv);
+   poly->loop_total ++;
+   mesh->abloops.count ++;
+}
 
-      v3_add( new_face->center, verts[new_loop->index], new_face->center );
-   }
-   v3_divs( new_face->center, new_face->loop_total, new_face->center );
-   
-   cxr_loop *lp0 = cxr_ab_ptr( &mesh->abloops, new_face->loop_start ),
-                   *lp1 = cxr_ab_ptr( &mesh->abloops, new_face->loop_start+1 ),
-                   *lp2 = cxr_ab_ptr( &mesh->abloops, new_face->loop_start+2 );
+/*
+ * Finalize and commit polygon into mesh 
+ */
+static void cxr_poly_finish( cxr_mesh *mesh )
+{
+   v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
+
+   int nface_id = mesh->abpolys.count;
+   cxr_polygon *poly = &mesh->polys[nface_id];
+
+   /* Average center and calc normal */
+
+   v3_divs( poly->center, poly->loop_total, poly->center );
+   cxr_loop *lp0 = &mesh->loops[ poly->loop_start],
+            *lp1 = &mesh->loops[ poly->loop_start+1 ],
+            *lp2 = &mesh->loops[ poly->loop_start+2 ];
    
    tri_normal( 
-    verts[lp0->index], verts[lp1->index], verts[lp2->index], new_face->normal);
-      
-   cxr_mesh_update( mesh );
-   return &mesh->polys[ nface_id ];
+    verts[lp0->index], verts[lp1->index], verts[lp2->index], poly->normal);
+
+   mesh->abpolys.count ++;
 }
 
 /* 
@@ -1164,7 +1184,8 @@ search_iterate:;
                   cxr_loop *lp1 = &mesh->loops[ newpoly->loop_start+l ];
                   cxr_polygon *future_face = &mesh->polys[ lp1->poly_right ];
 
-                  if( reflex_edges[ lp1->edge_index ] || lp1->poly_right == loop->poly_right ) 
+                  if( reflex_edges[ lp1->edge_index ] 
+                     || lp1->poly_right == loop->poly_right ) 
                      goto dont_check;
 
                   for( int m=0; m<solid_len; m++ )
@@ -1174,19 +1195,19 @@ search_iterate:;
                   for( int m=0; m<solid_len; m++ )
                   {
                      cxr_polygon *polym = &mesh->polys[solid[m]];
-                     if( v3_dot( polym->normal,future_face->normal) > CXR_PLANE_SIMILARITY_MAX)
+                     double pdist = v3_dot( polym->normal,future_face->normal);
+
+                     if( pdist > CXR_PLANE_SIMILARITY_MAX )
                         goto dont_check;
                   }
 
                   dont_check:;
                }
 
-               // Check for vertices in the new poly that exist on a current plane.
-               //  this condition is an invalid configuration and should not be added.
-
+               /* Check for vertices in the new polygon that exist on a current
+                * plane. This condition is invalid */
                solid[ solid_len ] = loop->poly_right;
 
-
                if( cxr_valid_solid(mesh,solid,solid_len+1 ) )
                {
                   faces_tagged[ loop->poly_right ] = faceid;
@@ -1204,320 +1225,409 @@ search_iterate:;
       goto search_iterate;
 
    return solid_len;
+}
 
-   // The current method can create some invalid configurations
-   // filter those out that dont work and un-tag the faces
+struct csolid
+{
+   int start, count, edge_count;
+   v3f center;
+};
 
-   /* TODO(harry):
-    *
-    * I think we have good enough filters now to ignore these,
-    * probably should make them show as warnings
-    *
-    */
-#if 0
-   if( !cxr_valid_solid( mesh, solid, solid_len ) )
+struct temp_manifold
+{
+   struct manifold_loop
    {
-      for( int l=0; l<solid_len; l++ )
-         faces_tagged[ solid[l] ] = -1;
-      
-      goto IL_CANCEL_SOLID;
+      cxr_loop loop;
+      int split;
    }
-   for( int j=0; j<solid_len-1; j++ )
+   *loops;
+
+   int loop_count,
+       split_count;
+
+   enum manifold_status
    {
-      for( int k=j+1; k<solid_len; k++ )
-      {
-         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;
-         }
-      }
+      k_manifold_err,
+      k_manifold_none,
+      k_manifold_fragmented,
+      k_manifold_complete,
    }
-   /*
-    */
-#endif
-}
-
+   status;
+};
 
-/* 
- * Return the best availible solid from mesh, and patch the existing mesh to
- * fill the gap where the new mesh left it.
+/*
+ * Create polygon from entire manifold structure.
  *
- * Returns NULL if shape is already convex or empty.
- * This function will not preserve edge data such as freestyle, sharp etc.
+ * Must be completely co-planar
  */
-static cxr_mesh *cxr_pull_best_solid(
-      cxr_mesh *mesh, 
-      int preserve_more_edges,
-      u32 *error )
+static void cxr_create_poly_full( cxr_mesh *mesh, struct temp_manifold *src )
 {
-   if( !cxr_mesh_link_loops(mesh) )
+   if( cxr_create_poly( mesh, src->loop_count ) )
    {
-      cxr_log( "non-manifold edges are in the mesh: "
-               "implicit internal geometry does not have full support\n" );
+      for( int l=0; l<src->loop_count; l++ )
+         cxr_poly_push_index( mesh, src->loops[ l ].loop.index);
 
-      return NULL;
+      cxr_poly_finish( mesh );
    }
-   
-   v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
-   int *edge_tagged = cxr_mesh_reflex_edges( mesh );
-   int *vertex_tagged = cxr_mesh_reflex_vertices( mesh );
+}
 
-   /*
-    * Connect all marked vertices that share an edge
-    */
+/*
+ * Links up all edges into a potential new manifold
+ *
+ * The return status can be:
+ *        (err): Critical programming error
+ *         none: No manifold to create
+ *   fragmented: Multiple sections exist, not just one
+ *     complete: Optimial manifold was created
+ */
+static void cxr_link_manifold( 
+   cxr_mesh *mesh, 
+   struct csolid *solid, 
+   int *solid_buffer,
+   struct temp_manifold *manifold
+   )
+{
+   cxr_loop **edge_list = malloc( sizeof(*edge_list) * solid->edge_count );
 
-   int *edge_important = malloc(mesh->abedges.count*sizeof(int));
-   for( int i=0; i< mesh->abedges.count; i++ )
-      edge_important[i] = 0;
+   int init_reverse = 0;
+   int unique_edge_count = 0;
 
-   for( int i=0; i<mesh->abpolys.count; i ++ )
+   /* Gather list of unique edges */
+
+   for( int j=0; j<solid->count; j++ )
    {
-      cxr_polygon *poly = &mesh->polys[i];
-      int not_tagged = -1,
-          tag_count = 0;
+      cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ];
 
-      for( int j=0; j<poly->loop_total; j++ )
+      for( int k=0; k<poly->loop_total; k++ )
       {
-         cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
+         cxr_loop *loop = &mesh->loops[ poly->loop_start+k ];
 
-         if( !edge_tagged[ loop->edge_index ] )
+         for( int l=0; l<unique_edge_count; l++ )
+            if( edge_list[l]->edge_index == loop->edge_index )
+               goto skip_edge;
+
+         for( int l=0; l<solid->count; l++ )
+            if( loop->poly_right == solid_buffer[solid->start+l] )
+               goto skip_edge;
+         
+         edge_list[ unique_edge_count ] = loop;
+
+         if( unique_edge_count == 0 )
          {
-            if( not_tagged == -1 )
-               not_tagged = loop->edge_index;
-            else
-               goto edge_unimportant;
+            cxr_edge *edgeptr = &mesh->edges[ loop->edge_index ];
+            if( edgeptr->i1 == loop->index )
+               init_reverse = 1;
          }
-      }
-      
-      if( not_tagged != -1 )
-         edge_important[not_tagged]=1;
 
-      edge_unimportant:;
+         unique_edge_count ++;
+         skip_edge:;
+      }
    }
-
-   // Connect edges that have verts tagged, but is not a hot edge
-   for( int i=0; i<mesh->abedges.count; i ++ )
+   
+   if( unique_edge_count == 0 )
    {
-      if( edge_important[i] && preserve_more_edges ) continue;
-
-      cxr_edge *edge = &mesh->edges[i];
-      if( vertex_tagged[edge->i0] && vertex_tagged[edge->i1] )
-         edge_tagged[i] = 1;
+      free( edge_list );
+      manifold->status = k_manifold_none;
+      return;
    }
+   
+   /* Link edges together to form manifold */
+   manifold->loops = malloc( solid->edge_count*sizeof(struct manifold_loop));
+   manifold->split_count = 0;
+   manifold->loop_count = 0;
 
-   free( edge_important );
+   cxr_edge *current = &mesh->edges[ edge_list[0]->edge_index ];
 
-#if 0
-   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});
+   int endpt = (!init_reverse)? current->i0: current->i1,
+       start = endpt,
+     curface = edge_list[0]->poly_left;
 
-   for( int i=0; i < mesh->edges.count; i++ )
+   manifold_continue:
+   for( int j=0; j<unique_edge_count; j++ )
    {
-      cxr_edge *edge = cxr_ab_ptr( &mesh->edges, i );
-      if( edge_tagged[i] )
-         cxr_debug_line( vertices[ edge->i0 ], vertices[ edge->i1 ], (v4f){0.0,0.0,0.0,1.0});
+      cxr_edge *other = &mesh->edges[ edge_list[j]->edge_index ];
+      if( other == current )
+         continue;
 
-      if( hot_edge[i] )
-         cxr_debug_line( vertices[ edge->i0 ], vertices[ edge->i1 ], (v4f){0.0,1.0,1.0,1.0});
-   }
-#endif
+      if( other->i0 == endpt || other->i1 == endpt )
+      {
+         current = other;
+         int lastpt = endpt;
 
-   // count regions
-   int *faces_tagged = malloc(mesh->abpolys.count*sizeof(int));
-   for( int i=0; i<mesh->abpolys.count; i++ )
-      faces_tagged[i] = -1;
-   
-   int *solid_buffer = malloc( mesh->abpolys.count*sizeof(int) );
-   int solid_buffer_len = 0;
-   struct csolid
-   {
-      int start,count,edge_count;
-      v3f center;
-   }
-   *candidates = malloc( mesh->abpolys.count *sizeof(struct csolid) );
-   int candidate_count = 0;
-   
-   struct tedge
-   {
-      int i0, i1;
-   } 
-   *edge_references = malloc( mesh->abedges.count *sizeof(struct tedge) );
-
-   for( int i=0; i<mesh->abpolys.count; i++ )
-   {
-      if( faces_tagged[i] != -1 ) continue;
-      faces_tagged[i] = i;
-      
-      int *solid = &solid_buffer[ solid_buffer_len ];
-      int solid_len = 0;
-      
-      solid[solid_len++] = i;
-      
-      int search_start = 0;
-      
-      // Iterative search that connects regions of planes governed by rules:
-      //   - edge can add other face if the edge has less than two reflexes
-      //   - the face can no longer be used in future searches
+         if( other->i0 == endpt ) endpt = current->i1;
+         else endpt = current->i0;
 
-   IL_SEARCH_CONTINUE:;
+         struct manifold_loop *ml = &manifold->loops[ manifold->loop_count ++ ];
+         
+         if( curface==edge_list[j]->poly_left )
+         {
+            ml->split = 1;
+            manifold->split_count ++;
+         }
+         else
+            ml->split = 0;
+         
+         ml->loop.edge_index = edge_list[j]->edge_index;
+         ml->loop.poly_left = edge_list[j]->poly_left;
+         ml->loop.index = lastpt;
+         ml->loop.poly_right = edge_list[j]->poly_right;
 
-      int changed = 0;
-      for( int j=search_start; j<solid_len; j++ )
-      {
-         cxr_polygon *poly = &mesh->polys[solid[j]];
+         curface = edge_list[j]->poly_left;
 
-         for( int k=0; k<poly->loop_total; k++ )
+         if(endpt == start)
          {
-            cxr_loop *loop = &mesh->loops[poly->loop_start+k];
-            cxr_edge *edge = &mesh->edges[loop->edge_index];
-
-            if( faces_tagged[ loop->poly_right ] == -1 )
-            {
-               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
-                  //
-                  // This can sort out SOME invalid configs, but not all.
-                  // It would be nice to find a more robust clustering algorithm for this.
-                  //
-
-                  cxr_polygon *poly_to_add = &mesh->polys[loop->poly_right];
-                  
-                  if( cxr_solid_overlap(mesh, poly, poly_to_add, loop->edge_index, 0 ))
-                     goto IL_SKIP_PLANE_ADD;
+            if( manifold->loop_count < unique_edge_count )
+               manifold->status = k_manifold_fragmented;
+            else
+               manifold->status = k_manifold_complete;
 
-                  for( int l=0; l < poly_to_add->loop_total; l++ )
-                  {
-                     cxr_loop *loop1 = &mesh->loops[poly_to_add->loop_start+l];
-                     cxr_polygon *future_face = &mesh->polys[loop1->poly_right];
+            goto manifold_complete;
+         }
 
-                     if( edge_tagged[ loop1->edge_index ] || loop1->poly_right == loop->poly_right ) 
-                        goto IL_SKIP_SIMILAR_PLANES;
+         goto manifold_continue;
+      }
+   }
+   
+   /* Incomplete links */
+   manifold->status = k_manifold_err;
 
-                     for( int m=0; m<solid_len; m++ )
-                        if( solid[m] == loop1->poly_right )
-                           goto IL_SKIP_SIMILAR_PLANES;
-                     
-                     for( int m=0; m<solid_len; m++ )
-                     {
-                        cxr_polygon *polym = &mesh->polys[solid[m]];
-                        if( v3_dot( polym->normal,future_face->normal) > CXR_PLANE_SIMILARITY_MAX)
-                           goto IL_SKIP_PLANE_ADD;
-                     }
+manifold_complete:
 
-                     IL_SKIP_SIMILAR_PLANES:;
-                  }
+   free( edge_list );
+   return;
+}
 
-                  // Check for vertices in the new poly that exist on a current plane.
-                  //  this condition is an invalid configuration and should not be added.
+/*
+ * Reconstruct implied internal geometry where the manifold doesn't have
+ * enough information (vertices) to create a full result.
+ */
+static int cxr_build_implicit_geo( cxr_mesh *mesh, int new_polys, int start )
+{
+   for( int i=0; i<new_polys-2; i++ )
+   {
+      for( int j=i+1; j<new_polys-1; j++ )
+      {
+         for( int k=j+1; k<new_polys; k++ )
+         {
+            cxr_polygon *ptri = &mesh->polys[ start+i ],
+                        *ptrj = &mesh->polys[ start+j ],
+                        *ptrk = &mesh->polys[ start+k ];
+
+            v4f planei, planej, planek;
+            normal_to_plane(ptri->normal,ptri->center,planei);
+            normal_to_plane(ptrj->normal,ptrj->center,planej);
+            normal_to_plane(ptrk->normal,ptrk->center,planek);
+            
+            v3f intersect;
 
-                  solid[ solid_len ] = loop->poly_right;
+            if( plane_intersect(planei,planej,planek,intersect) )
+            {
+               /* Make sure the point is inside the convex region */
+               
+               int point_valid = 1;
+               for( int l=0; l<mesh->abpolys.count; l++ )
+               {
+                  cxr_polygon *ptrl = &mesh->polys[l];
+                  v4f planel;
 
+                  normal_to_plane(ptrl->normal, ptrl->center, planel);
 
-                  if( cxr_valid_solid(mesh,solid,solid_len+1 ) )
+                  if( plane_polarity( planel, intersect ) > 0.01 )
                   {
-                     faces_tagged[ loop->poly_right ] = i;
-                     changed = 1;
-                     solid_len ++;
+                     cxr_log( "degen vert, planes %d, %d, %d [max:%d]\n", 
+                               i,j,k, new_polys );
+
+                     cxr_debug_poly( mesh, ptri, colours_random[3] );
+                     cxr_debug_poly( mesh, ptrj, colours_random[1] );
+                     cxr_debug_poly( mesh, ptrk, colours_random[2] );
+
+                     return 0;
                   }
                }
+               
+               /* Extend faces to include this vert */
+
+               int nvertid = mesh->p_abverts->count;
+               cxr_ab_push( mesh->p_abverts, intersect );
 
-               IL_SKIP_PLANE_ADD:;
+               ptrj->loop_start += 1;
+               ptrk->loop_start += 2;
+               
+               cxr_ab_reserve( &mesh->abloops, 3);
+
+               int newi = ptri->loop_start+ptri->loop_total,
+                   newj = ptrj->loop_start+ptrj->loop_total,
+                   newk = ptrk->loop_start+ptrk->loop_total;
+
+               cxr_loop 
+                  *lloopi = cxr_ab_empty_at(&mesh->abloops, newi),
+                  *lloopj = cxr_ab_empty_at(&mesh->abloops, newj),
+                  *lloopk = cxr_ab_empty_at(&mesh->abloops, newk);
+
+               lloopi->index = nvertid;
+               lloopi->edge_index = 0; 
+               lloopi->poly_left = start + i;
+               lloopi->poly_right = -1; 
+
+               lloopj->index = nvertid;
+               lloopj->poly_left = start + j;
+               lloopj->edge_index = 0; 
+               lloopj->poly_right = -1; 
+
+               lloopk->index = nvertid;
+               lloopk->edge_index = 0;
+               lloopk->poly_left = start + k;
+               lloopk->poly_right = -1;
+               
+               v2_zero(lloopi->uv);
+               v2_zero(lloopj->uv);
+               v2_zero(lloopk->uv);
+
+               ptri->loop_total ++;
+               ptrj->loop_total ++;
+               ptrk->loop_total ++;
+
+               double qi = 1.0/(double)ptri->loop_total,
+                      qj = 1.0/(double)ptrj->loop_total,
+                      qk = 1.0/(double)ptrk->loop_total;
+
+               /* Adjust centers of faces */
+               v3_lerp( ptri->center, intersect, qi, ptri->center );
+               v3_lerp( ptrj->center, intersect, qj, ptrj->center );
+               v3_lerp( ptrk->center, intersect, qk, ptrk->center );
             }
          }
       }
-      search_start = solid_len;
-      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
-
-      /* TODO(harry):
-       *
-       * I think we have good enough filters now to ignore these,
-       * probably should make them show as warnings
-       *
-       */
-      if( !cxr_valid_solid( mesh, solid, solid_len ) )
-      {
-         for( int l=0; l<solid_len; l++ )
-            faces_tagged[ solid[l] ] = -1;
-         
-         goto IL_CANCEL_SOLID;
-      }
-      for( int j=0; j<solid_len-1; j++ )
+   }
+
+   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,
+      u32 *error )
+{
+   if( !cxr_mesh_link_loops(mesh) )
+   {
+      cxr_log( "non-manifold edges are in the mesh: "
+               "implicit internal geometry does not have full support\n" );
+
+      return NULL;
+   }
+   
+   int *edge_tagged = cxr_mesh_reflex_edges( mesh );
+   int *vertex_tagged = cxr_mesh_reflex_vertices( mesh );
+
+   /*
+    * Connect all marked vertices that share an edge
+    */
+
+   int *edge_important = malloc(mesh->abedges.count*sizeof(int));
+   for( int i=0; i< mesh->abedges.count; i++ )
+      edge_important[i] = 0;
+
+   for( int i=0; i<mesh->abpolys.count; i ++ )
+   {
+      cxr_polygon *poly = &mesh->polys[i];
+      int not_tagged = -1,
+          tag_count = 0;
+
+      for( int j=0; j<poly->loop_total; j++ )
       {
-         for( int k=j+1; k<solid_len; k++ )
+         cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
+
+         if( !edge_tagged[ loop->edge_index ] )
          {
-            cxr_polygon *polyj = &mesh->polys[solid[j]],
-                               *polyk = &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;
-            }
+            if( not_tagged == -1 )
+               not_tagged = loop->edge_index;
+            else
+               goto edge_unimportant;
          }
       }
-      /*
-       */
+      
+      if( not_tagged != -1 )
+         edge_important[not_tagged]=1;
 
-      // Add entry
+      edge_unimportant:;
+   }
+
+   /*
+    * Connect edges where both vertices are reflex, only if we are not
+    * preserving them
+    */
+   for( int i=0; i<mesh->abedges.count; i ++ )
+   {
+      if( edge_important[i] && preserve_more_edges ) continue;
+
+      cxr_edge *edge = &mesh->edges[i];
+      if( vertex_tagged[edge->i0] && vertex_tagged[edge->i1] )
+         edge_tagged[i] = 1;
+   }
+
+   free( edge_important );
+   
+   int *faces_tagged = malloc(mesh->abpolys.count*sizeof(int));
+   for( int i=0; i<mesh->abpolys.count; i++ )
+      faces_tagged[i] = -1;
+   
+   struct csolid *candidates;
+   int *solid_buffer = malloc( mesh->abpolys.count*sizeof(int) ),
+        solid_buffer_len = 0,
+        candidate_count = 0;
+
+   candidates = malloc( mesh->abpolys.count *sizeof(struct csolid) );
+   
+   /* 
+    * Create a valid, non-overlapping solid for every face present in the mesh
+    */
+   for( int i=0; i<mesh->abpolys.count; i++ )
+   {
+      if( faces_tagged[i] != -1 ) continue;
+      faces_tagged[i] = i;
+
+      int *solid = &solid_buffer[ solid_buffer_len ];
+      int len = cxr_buildsolid( mesh, i, solid, edge_tagged, faces_tagged );
+
+      /* add entry */
       struct csolid *csolid = &candidates[candidate_count ++];
       csolid->start = solid_buffer_len;
-      csolid->count = solid_len;
+      csolid->count = len;
       csolid->edge_count = 0;
       
       v3_zero( csolid->center );
-      for( int j=0; j<solid_len; j++ )
+      for( int j=0; j<len; j++ )
       {
          cxr_polygon *polyj = &mesh->polys[ solid[j] ];
          v3_add( polyj->center, csolid->center, csolid->center );
          csolid->edge_count += polyj->loop_total;
       }
-      v3_divs( csolid->center, solid_len, csolid->center );
-
-      solid_buffer_len += solid_len;
-
-      IL_CANCEL_SOLID:;
+      v3_divs( csolid->center, len, csolid->center );
+      solid_buffer_len += len;
    }
 
-   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
+   free( edge_tagged );
+   free( vertex_tagged );
+   free( faces_tagged );
 
+   /*
+    * Choosing the best solid: most defined manifold
+    */
    struct csolid *best_solid = NULL;
    int fewest_manifold_splits = INT32_MAX;
 
-   cxr_loop *best_manifold = malloc( mesh->abloops.count *sizeof(cxr_loop) );
-   int *best_manifold_splits = malloc( mesh->abloops.count *sizeof(int) );
-   int best_manifold_len = 0;
+   struct temp_manifold best_manifold = { .loops = NULL, .loop_count = 0 };
    int max_solid_faces = 0;
    
-   /*
-   int *edge_list = malloc( mesh->edges.count *sizeof(int) );
-   int *edge_lefts = malloc( mesh->edges.count *sizeof(int) );
-   */
-   cxr_loop **edge_list = malloc( sizeof(*edge_list) *mesh->abedges.count);
-
-   cxr_loop *manifold = malloc(mesh->abedges.count*2*sizeof(cxr_loop));
-   int *splits = malloc(mesh->abedges.count*2*sizeof(int));
-
    for( int i=0; i<candidate_count; i++ )
    {
       struct csolid *solid = &candidates[i];
@@ -1525,196 +1635,68 @@ static cxr_mesh *cxr_pull_best_solid(
 
       if( solid->count <= 2 )
          continue;
-
-      int init_reverse = 0;
-      int unique_edge_count = 0;
-
-      for( int j=0; j<solid->count; j++ )
-      {
-         cxr_polygon *poly = cxr_ab_ptr(&mesh->abpolys, solid_buffer[solid->start+j]);
-
-         for( int k=0; k<poly->loop_total; k++ )
-         {
-            cxr_loop *loop = cxr_ab_ptr(&mesh->abloops, poly->loop_start+k);
-
-            for( int l=0; l<unique_edge_count; l++ )
-               if( edge_list[l]->edge_index == loop->edge_index )
-                  goto IL_EDGE_ALREADY_PRESENT;
-
-            // Check if right edge references a polygon that is not 
-            // present inside the current solid candidate
-            for( int l=0; l<solid->count; l++ )
-               if( loop->poly_right == solid_buffer[solid->start+l] )
-                  goto IL_EDGE_ALREADY_PRESENT;
-            
-            edge_list[ unique_edge_count ] = loop;
-
-            if( unique_edge_count == 0 )
-            {
-               cxr_edge *edgeptr = cxr_ab_ptr(&mesh->abedges, loop->edge_index);
-               if( edgeptr->i1 == loop->index )
-                  init_reverse = 1;
-            }
-
-            unique_edge_count ++;
-            IL_EDGE_ALREADY_PRESENT:;
-         }
-      }
-      
-      // This solid is already fully connected
-      if( unique_edge_count == 0 )
-         continue;
       
-      // Link edges together to create new manifold
-      // Corners are created when two edges share a polygon face
-      // This way we can split it into regions
+      struct temp_manifold manifold;
+      cxr_link_manifold( mesh, solid, solid_buffer, &manifold);
       
-      for( int j=0; j<mesh->p_abverts->count; j++ )
-         vertex_tagged[j] = -1;
-      
-      // Start with a loop edge which was tagged
-      cxr_edge *current = &mesh->edges[edge_list[0]->edge_index];
-
-      int endpt = (!init_reverse)? current->i0: current->i1,
-          start = endpt,
-        curface = edge_list[0]->poly_left;
-
-      int manifold_len = 0;
-      int split_count = 0;
-
-      IL_MANIFOLD_CONTINUE:
-      for( int j=0; j<unique_edge_count; j++ )
-      {
-         cxr_edge *other = &mesh->edges[ edge_list[j]->edge_index ];
-         if( other == current )
-            continue;
-
-         if( other->i0 == endpt || other->i1 == endpt )
-         {
-            current = other;
-            int lastpt = endpt;
-
-            if( other->i0 == endpt ) endpt = current->i1;
-            else endpt = current->i0;
-            
-            if( curface==edge_list[j]->poly_left )
-            {
-               splits[ manifold_len ] = 1;
-               split_count ++;
-            }
-            else
-               splits[ manifold_len ] = 0;
-            
-            // Append to intermidiary manifold
-            manifold[ manifold_len ].edge_index = edge_list[j]->edge_index;
-            manifold[ manifold_len ].poly_left = edge_list[j]->poly_left;
-            manifold[ manifold_len ].index = lastpt;
-            manifold[ manifold_len ].poly_right = edge_list[j]->poly_right;
-            manifold_len ++;
-
-            curface = edge_list[j]->poly_left;
-
-            if(endpt == start) goto IL_MANIFOLD_COMPLETE;
-            goto IL_MANIFOLD_CONTINUE;
-         }
-      }
-      cxr_log( "Failed to link manifold, count: %d\n", manifold_len );
-      
-      for( int j=0; j<solid->count; j++ )
-      {
-         cxr_log( "p%d\n", solid_buffer[solid->start+j] );
-         cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ];
-         cxr_debug_poly( mesh, poly, (v4f){0.2,0.0,0.0,1.0} );
-      }
-
-      for( int j=0; j<unique_edge_count; j++ )
+      if( manifold.status == k_manifold_err )
       {
-         cxr_edge *uedge = &mesh->edges[ edge_list[j]->edge_index ];
-         cxr_debug_line( verts[uedge->i0], verts[uedge->i1], 
-               (v4f){0.4,0.0,0.0,1.0});
+         *error = CXR_ERROR_BAD_MANIFOLD;
+         free(solid_buffer);
+         free(candidates);
+         free(manifold.loops);
+         free(best_manifold.loops);
+         return NULL;
       }
 
-      for( int j=0; j<manifold_len-1; j++ )
+      if( manifold.status == k_manifold_complete )
       {
-         cxr_loop *lp0 = &manifold[j],
-                         *lp1 = &manifold[cxr_range(j+1,manifold_len)];
-
-         cxr_debug_line(verts[lp0->index],verts[lp1->index], colour_error );
-      }
-
-      cxr_debug_mesh( mesh, (v4f){0.0,0.0,0.0, 0.9} );
-      *error = CXR_ERROR_BAD_MANIFOLD;
-
-      free(edge_list);
-      free(manifold);
-      free(splits);
-      free(edge_tagged);
-      free(vertex_tagged);
-      free(faces_tagged);
-      free(solid_buffer);
-      free(candidates);
-      free(best_manifold);
-      free(best_manifold_splits);
-      return NULL;
-
-      IL_MANIFOLD_COMPLETE:;
-      
-      if( manifold_len < unique_edge_count )
-         continue;
-      else
-      {
-         if( split_count < fewest_manifold_splits )
+         if( manifold.split_count < fewest_manifold_splits )
          {
-            fewest_manifold_splits = split_count;
+            fewest_manifold_splits = manifold.split_count;
             best_solid = solid;
 
-            for( int j=0; j<manifold_len; j++ )
-            {
-               best_manifold[j] = manifold[j];
-               best_manifold_splits[j] = splits[j];
-            }
-            best_manifold_len = manifold_len;
+            free( best_manifold.loops );
+            best_manifold = manifold;
+            continue;
          }
       }
+      
+      if( manifold.status != k_manifold_none )
+         free( manifold.loops );
    }
 
-   free(edge_list);
-   free(manifold);
-   free(splits);
-
    if( max_solid_faces < 2 )
    {
       *error = CXR_ERROR_NO_SOLIDS;
-      free(edge_tagged);
-      free(vertex_tagged);
-      free(faces_tagged);
       free(solid_buffer);
       free(candidates);
-      free(best_manifold);
-      free(best_manifold_splits);
+      free(best_manifold.loops);
       return NULL;
    }
 
    if( best_solid != NULL )
    {
-      cxr_mesh *pullmesh = 
-         cxr_alloc_mesh( best_solid->edge_count, best_solid->edge_count, best_solid->count, mesh->p_abverts );
+      cxr_mesh *pullmesh = cxr_alloc_mesh( best_solid->edge_count, 
+                                           best_solid->edge_count, 
+                                           best_solid->count, 
+                                           mesh->p_abverts );
       
-      // Add existing faces to pullsolid, and delete from main mesh
       for( int i=0; i<best_solid->count; i++ )
       {
          int nface_id = pullmesh->abpolys.count;
+         int exist_plane_id = solid_buffer[best_solid->start+i];
 
-         cxr_polygon *exist_face = &mesh->polys[ solid_buffer[best_solid->start+i] ];
-         cxr_polygon *new_face = cxr_ab_empty( &pullmesh->abpolys );
+         cxr_polygon *exist_face = &mesh->polys[ exist_plane_id ],
+                     *new_face = cxr_ab_empty( &pullmesh->abpolys );
          
          *new_face = *exist_face;
          new_face->loop_start = pullmesh->abloops.count;
 
          for( int j=0; j<exist_face->loop_total; j++ )
          {
-            cxr_loop *exist_loop = cxr_ab_ptr(&mesh->abloops, exist_face->loop_start+j);
-            cxr_loop *new_loop = cxr_ab_empty(&pullmesh->abloops);
+            cxr_loop *exist_loop = &mesh->loops[ exist_face->loop_start+j ],
+                     *new_loop = cxr_ab_empty(&pullmesh->abloops);
 
             new_loop->index = exist_loop->index;
             new_loop->poly_left = nface_id;
@@ -1731,209 +1713,123 @@ static cxr_mesh *cxr_pull_best_solid(
 
       if( fewest_manifold_splits != 0 )
       {
-         // This is a really strange observation, however it *seems* to apply to the kinds
-         // of geometry we are dealing with. If the split count is odd, the manifold can be
-         // created easily, no folding required.
-         //
-         // 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(harry): Find a well defined rule here.
-
+         /* Unusual observation:
+          *   If the split count is odd, the manifold can be created easily
+          *
+          *   If it is even, implicit internal geometry is needed to be
+          *   constructed. So the manifold gets folded as we create it segment
+          *   by segment.
+          *
+          *   I'm not sure if this is a well defined rule of geometry, but seems
+          *   to apply to the data we care about.
+          */
          int collapse_used_segments = (u32)fewest_manifold_splits & 0x1? 0: 1;
 
-         IL_MANIFOLD_BUILD_REPEAT:
+         manifold_repeat:
          
-         for( int j=0; j<best_manifold_len; j++ )
+         for( int j=0; j < best_manifold.loop_count; j++ )
          {
-            if( best_manifold_splits[j] )
+            if( !best_manifold.loops[j].split ) continue;
+
+            cxr_loop *loop = &best_manifold.loops[j].loop;
+
+            for( int k=1; k< best_manifold.loop_count; k++ )
             {
-               cxr_loop *loop = &best_manifold[j];
+               int index1 = cxr_range(j+k, best_manifold.loop_count );
+               cxr_loop *loop1 = &best_manifold.loops[index1].loop;
 
-               for( int k=1; k<best_manifold_len; k++ )
+               if( best_manifold.loops[index1].split )
                {
-                  int index1 = cxr_range(j+k,best_manifold_len);
-                  cxr_loop *loop1 = &best_manifold[index1];
-
-                  if( best_manifold_splits[index1] )
-                  {
-                     if( k==1 )
-                        break;
+                  if( k==1 )
+                     break;
 
-                     new_polys ++;
+                  new_polys ++;
 
-                     if( new_polys > best_manifold_len )
+                  if( new_polys > best_manifold.loop_count )
+                  {
+                     cxr_log( "Programming error: Too many new polys!\n" );
+                     exit(1);
+                  }
+                  
+                  if( cxr_create_poly( pullmesh, k+1 ) )
+                  {
+                     for( int l=0; l<k+1; l++ )
                      {
-                        cxr_log( "Programming error: Too many new polys!\n" );
-                        exit(1);
+                        int i0 = cxr_range(j+l, best_manifold.loop_count ),
+                            index = best_manifold.loops[ i0 ].loop.index;
+
+                        cxr_poly_push_index( pullmesh, index );
                      }
+                     cxr_poly_finish( pullmesh );
+                  }
 
-                     cxr_create_poly( pullmesh, best_manifold, k+1, j, best_manifold_len );
+                  /* Collapse down manifold */
+                  if( collapse_used_segments )
+                  {
+                     best_manifold.loops[j].split = 0;
+                     best_manifold.loops[index1].split = 0;
 
-                     // Remove new section from manifold
-                     if( collapse_used_segments )
+                     int new_length = (best_manifold.loop_count-(k-1));
+                     
+                     struct temp_manifold new_manifold = { 
+                        .loop_count = new_length 
+                     };
+                     new_manifold.loops = 
+                        malloc( new_length*sizeof(*new_manifold.loops) );
+                     
+                     for( int l=0; l<new_length; l ++ )
                      {
-                        best_manifold_splits[j] = 0;
-                        best_manifold_splits[index1] = 0;
-
-                        int new_length = (best_manifold_len-(k-1));
-
-                        cxr_loop *new_manifold = malloc( new_length*sizeof(cxr_loop) );
-                        int *new_manifold_splits = malloc( new_length*sizeof(int) );
-
-                        for( int l=0; l<new_length; l ++ )
-                        {
-                           int i_src = cxr_range( j+k+l, best_manifold_len );
-                           new_manifold[l] = best_manifold[i_src];
-                           new_manifold_splits[l] = best_manifold_splits[i_src];
-                        }
-
-                        free( best_manifold );
-                        free( best_manifold_splits );
-                        best_manifold = new_manifold;
-                        best_manifold_splits = new_manifold_splits;
-
-                        best_manifold_len = new_length;
-
-                        goto IL_MANIFOLD_BUILD_REPEAT;
+                        int i_src = cxr_range( j+k+l, best_manifold.loop_count);
+                        new_manifold.loops[l] = best_manifold.loops[i_src];
                      }
 
-                     j=j+k-1;
-                     break;
+                     free( best_manifold.loops );
+                     best_manifold = new_manifold;
+
+                     goto manifold_repeat;
                   }
+
+                  j=j+k-1;
+                  break;
                }
             }
          }
          
-         if( best_manifold_len && collapse_used_segments )
+         if( best_manifold.loop_count && collapse_used_segments )
          {
-            cxr_create_poly( pullmesh, best_manifold, best_manifold_len, 0, best_manifold_len );
+            cxr_create_poly_full( pullmesh, &best_manifold );
             new_polys ++;
          }
       }
       else
       {
-         cxr_create_poly( pullmesh, best_manifold, best_manifold_len, 0, best_manifold_len );
+         cxr_create_poly_full( pullmesh, &best_manifold );
          new_polys = 1;
       }
 
-      // vert_buffer may be reallocated by the next section of code,
-      //             force a NULLref on vertices to catch any errors
-      verts = NULL;
-      struct cxr_auto_buffer *abverts = mesh->p_abverts;
-
-      // Implicit geometry reconstruction
-      //   If there's 3 or more planes, collide them together to see if any new
-      //   vertices need to be created on the mesh
       if( new_polys >= 3 )
       {
-         for( int i=0; i<new_polys-2; i++ )
+         if( !cxr_build_implicit_geo( pullmesh, new_polys, pullmesh_new_start ))
          {
-            for( int j=i+1; j<new_polys-1; j++ )
-            {
-               for( int k=j+1; k<new_polys; k++ )
-               {
-                  cxr_polygon *ptri = &pullmesh->polys[ pullmesh_new_start+i ],
-                              *ptrj = &pullmesh->polys[ pullmesh_new_start+j ],
-                              *ptrk = &pullmesh->polys[ pullmesh_new_start+k ];
-
-                  v4f planei, planej, planek;
-                  normal_to_plane(ptri->normal,ptri->center,planei);
-                  normal_to_plane(ptrj->normal,ptrj->center,planej);
-                  normal_to_plane(ptrk->normal,ptrk->center,planek);
-                  
-                  v3f intersect;
-
-                  if( plane_intersect(planei,planej,planek,intersect) )
-                  {
-                     // cxr_debug_box( intersect, 0.05, colour_error );
-
-                     // Make sure this point is within the convex region, otherwise treat
-                     // it as a degenerate case
-                     
-                     int point_valid = 1;
-                     for( int l=0; l<pullmesh->abpolys.count; l++ )
-                     {
-                        cxr_polygon *ptrl = &pullmesh->polys[l];
-                        v4f planel;
-
-                        normal_to_plane(ptrl->normal, ptrl->center, planel);
-
-                        if( plane_polarity( planel, intersect ) > 0.01 )
-                        {
-                           cxr_log( "degen vert, planes %d, %d, %d [max:%d]\n", i,j,k, new_polys );
-                           *error = CXR_ERROR_DEGEN_IMPLICIT;
-
-                           cxr_debug_poly( pullmesh, ptri, colours_random[3] );
-                           cxr_debug_poly( pullmesh, ptrj, colours_random[1] );
-                           cxr_debug_poly( pullmesh, ptrk, colours_random[2] );
-
-                           point_valid = 0;
-                           break;
-                        }
-                     }
-                     
-                     if( !point_valid ) continue;
-
-                     // Extend faces to include this point
-                     int nvertid = abverts->count;
-                     cxr_ab_push( abverts, intersect );
-   
-                     ptrj->loop_start += 1;
-                     ptrk->loop_start += 2;
-                     
-                     cxr_ab_reserve( &pullmesh->abloops, 3);
-
-                     int newi = ptri->loop_start+ptri->loop_total,
-                         newj = ptrj->loop_start+ptrj->loop_total,
-                         newk = ptrk->loop_start+ptrk->loop_total;
-
-                     cxr_loop 
-                        *lloopi = cxr_ab_empty_at(&pullmesh->abloops, newi),
-                        *lloopj = cxr_ab_empty_at(&pullmesh->abloops, newj),
-                        *lloopk = cxr_ab_empty_at(&pullmesh->abloops, newk);
-                     
-                     lloopi->index = nvertid;
-                     lloopi->edge_index = 0; 
-                     lloopi->poly_left = pullmesh_new_start+i;
-                     lloopi->poly_right = -1; 
-
-                     lloopj->index = nvertid;
-                     lloopj->poly_left = pullmesh_new_start+j;
-                     lloopj->edge_index = 0; 
-                     lloopj->poly_right = -1; 
-
-                     lloopk->index = nvertid;
-                     lloopk->edge_index = 0;
-                     lloopk->poly_left = pullmesh_new_start+k;
-                     lloopk->poly_right = -1;
-                     
-                     v2_zero(lloopi->uv);
-                     v2_zero(lloopj->uv);
-                     v2_zero(lloopk->uv);
-
-                     ptri->loop_total ++;
-                     ptrj->loop_total ++;
-                     ptrk->loop_total ++;
-
-                     // Adjust centers of faces
-                     v3_lerp( ptri->center, intersect, 1.0/(double)ptri->loop_total, ptri->center );
-                     v3_lerp( ptrj->center, intersect, 1.0/(double)ptrj->loop_total, ptrj->center );
-                     v3_lerp( ptrk->center, intersect, 1.0/(double)ptrk->loop_total, ptrk->center );
-                  }
-               }
-            }
+            free(solid_buffer);
+            free(candidates);
+            free(best_manifold.loops);
+            
+            cxr_free_mesh( pullmesh );
+            *error = CXR_ERROR_DEGEN_IMPLICIT;
+            return NULL;
          }
       }
 
-      // Copy faces from pullsolid into orig mesh
-
+      /*
+       * Copy faces from the pullmesh into original, to patch up where there
+       * would be gaps created
+       */
       for( int i=0; i<new_polys; i++ )
       {
          int rface_id = mesh->abpolys.count;
-
-         cxr_polygon *pface = cxr_ab_ptr(&pullmesh->abpolys,pullmesh_new_start+i);
-         cxr_polygon *rip_face = cxr_ab_empty(&mesh->abpolys);
+         cxr_polygon *pface = &pullmesh->polys[pullmesh_new_start+i],
+                  *rip_face = cxr_ab_empty(&mesh->abpolys);
 
          rip_face->loop_start = mesh->abloops.count;
          rip_face->loop_total = pface->loop_total;
@@ -1941,8 +1837,9 @@ static cxr_mesh *cxr_pull_best_solid(
          
          for( int j=0; j<rip_face->loop_total; j++ )
          {
-            cxr_loop *ploop = cxr_ab_ptr(&pullmesh->abloops, pface->loop_start+pface->loop_total-j-1),
-                            *rloop = cxr_ab_empty(&mesh->abloops);
+            cxr_loop *ploop = 
+               &pullmesh->loops[ pface->loop_start+pface->loop_total-j-1 ],
+               *rloop = cxr_ab_empty(&mesh->abloops);
 
             rloop->index = ploop->index;
             rloop->poly_left = rface_id;
@@ -1959,28 +1856,20 @@ static cxr_mesh *cxr_pull_best_solid(
       cxr_mesh_update( pullmesh );
 
       cxr_mesh_clean_faces( mesh );
-      cxr_mesh_clean_faces( pullmesh );
       cxr_mesh_clean_edges( mesh );
+      cxr_mesh_clean_faces( pullmesh );
       cxr_mesh_clean_edges( pullmesh );
 
-      free(edge_tagged);
-      free(vertex_tagged);
-      free(faces_tagged);
       free(solid_buffer);
       free(candidates);
-      free(best_manifold);
-      free(best_manifold_splits);
+      free(best_manifold.loops);
    
       return pullmesh;
    }
 
-   free(edge_tagged);
-   free(vertex_tagged);
-   free(faces_tagged);
    free(solid_buffer);
    free(candidates);
-   free(best_manifold);
-   free(best_manifold_splits);
+   free(best_manifold.loops);
 
    return NULL;
 }
@@ -1989,14 +1878,18 @@ static cxr_mesh *cxr_pull_best_solid(
  * Convert from the format we recieve from blender into our internal format
  * with auto buffers.
  */
-static cxr_mesh *cxr_to_internal_format(cxr_input_mesh *src, cxr_auto_buffer *abverts)
-{
-   cxr_mesh *mesh = cxr_alloc_mesh( src->edge_count, src->loop_count, src->poly_count, abverts );
+static cxr_mesh *cxr_to_internal_format( 
+   cxr_input_mesh *src, 
+   cxr_abuffer *abverts
+){
+   cxr_mesh *mesh = cxr_alloc_mesh( src->edge_count, src->loop_count, 
+                                    src->poly_count, abverts );
+
    cxr_ab_init( abverts, sizeof(v3f), src->vertex_count );
 
-   memcpy( cxr_ab_ptr( &mesh->abedges, 0 ), src->edges, src->edge_count*sizeof(cxr_edge));
-   memcpy( cxr_ab_ptr( &mesh->abpolys, 0 ), src->polys, src->poly_count*sizeof(cxr_polygon));
-   memcpy( cxr_ab_ptr( abverts, 0 ), src->vertices, src->vertex_count*sizeof(v3f));
+   memcpy( mesh->abedges.arr, src->edges, src->edge_count*sizeof(cxr_edge));
+   memcpy( mesh->abpolys.arr, src->polys, src->poly_count*sizeof(cxr_polygon));
+   memcpy( abverts->arr, src->vertices, src->vertex_count*sizeof(v3f));
    mesh->abedges.count = src->edge_count;
    mesh->abloops.count = src->loop_count;
    mesh->abpolys.count = src->poly_count;
@@ -2032,15 +1925,15 @@ static double support_distance( v3f verts[3], v3f dir, double coef )
    );
 }
 
-// Main function
-static void cxr_calculate_axis( 
-   cxr_texinfo *transform,
-   v3f verts[3],
-   v2f uvs[3],
-   v2f texture_res
-   )
-{
-   v2f tT, bT; // Tangent/bitangent pairs for UV space and world
+/*
+ * Convert regular UV'd triangle int Source's u/vaxis vectors
+ *
+ * This supports affine move, scale, rotation, parallel skewing
+ */
+static void cxr_calculate_axis( cxr_texinfo *transform, v3f verts[3],
+   v2f uvs[3], v2f texture_res
+){
+   v2f tT, bT; /* Tangent/bitangent pairs for UV space and world */
    v3f tW, bW;
 
    v2_sub( uvs[0], uvs[1], tT );
@@ -2048,7 +1941,7 @@ static void cxr_calculate_axis(
    v3_sub( verts[0], verts[1], tW );
    v3_sub( verts[2], verts[1], bW );
 
-   // Use arbitrary projection if there is no UV 
+   /* Use arbitrary projection if there is no UV */
    if( v2_length( tT ) < 0.0001 || v2_length( bT ) < 0.0001 )
    {
       v3f uaxis, normal, vaxis;
@@ -2069,15 +1962,15 @@ static void cxr_calculate_axis(
       return;
    }
    
-   // Detect if UV is reversed
+   /* Detect if UV is reversed */
    double winding = v2_cross( tT, bT ) >= 0.0f? 1.0f: -1.0f;
    
-   // UV projection reference
+   /* UV projection reference */
    v2f vY, vX;
    v2_muls((v2f){1,0}, winding, vX);
    v2_muls((v2f){0,1}, winding, vY);
 
-   // Reproject reference into world space, including skew
+   /* Reproject reference into world space, including skew */
    v3f uaxis1, vaxis1;
 
    v3_muls( tW, v2_cross(vX,bT) / v2_cross(bT,tT), uaxis1 );
@@ -2089,7 +1982,7 @@ static void cxr_calculate_axis(
    v3_normalize( uaxis1 );
    v3_normalize( vaxis1 );
 
-   // Apply source transform to axis (yes, they also need to be swapped)
+   /* Apply source transform to axis (yes, they also need to be swapped) */
    v3f norm, uaxis, vaxis;
 
    v3_cross( bW, tW, norm );
@@ -2097,7 +1990,7 @@ static void cxr_calculate_axis(
    v3_cross( vaxis1, norm, uaxis );
    v3_cross( uaxis1, norm, vaxis );
 
-   // real UV scale
+   /* real UV scale */
    v2f uvmin, uvmax, uvdelta;
    v2_minv( uvs[0], uvs[1], uvmin );
    v2_minv( uvmin, uvs[2], uvmin );
@@ -2106,7 +1999,7 @@ static void cxr_calculate_axis(
    
    v2_sub( uvmax, uvmin, uvdelta );
 
-   // world-uv scale
+   /* world-uv scale */
    v2f uvminw, uvmaxw, uvdeltaw;
    uvminw[0] = -support_distance( verts, uaxis, -1.0f );
    uvmaxw[0] =  support_distance( verts, uaxis,  1.0f );
@@ -2115,12 +2008,12 @@ static void cxr_calculate_axis(
 
    v2_sub( uvmaxw, uvminw, uvdeltaw );
    
-   // VMf uv scale
+   /* VMf uv scale */
    v2f uv_scale;
    v2_div( uvdeltaw, uvdelta, uv_scale );
    v2_div( uv_scale, texture_res, uv_scale );
 
-   // Find offset via 'natural' point
+   /* Find offset via 'natural' point */
    v2f target_uv, natural_uv, tex_offset;
    v2_mul( uvs[0], texture_res, target_uv );
    
@@ -2131,7 +2024,7 @@ static void cxr_calculate_axis(
    tex_offset[0] =   target_uv[0]-natural_uv[0];
    tex_offset[1] = -(target_uv[1]-natural_uv[1]);
 
-   // Copy everything into output
+   /* Copy everything into output */
    v3_copy( uaxis, transform->uaxis );
    v3_copy( vaxis, transform->vaxis );
    v2_copy( tex_offset, transform->offset );
@@ -2139,33 +2032,35 @@ static void cxr_calculate_axis(
    transform->winding = winding;
 }
 
-CXR_API cxr_input_mesh *cxr_decompose(cxr_input_mesh *src)
+CXR_API cxr_input_mesh *cxr_write_test_data( cxr_input_mesh *src )
 {
-   FILE *yeet = fopen( "/home/harry/Documents/blender_addons_remote/addons/convexer/src/solid.h", "w" );
+   FILE *fp = fopen( 
+      "/home/harry/Documents/blender_addons_remote/addons/convexer/src/solid.h",
+      "w" );
  
-   fprintf( yeet, "v3f test_verts[] = {\n" );
+   fprintf( fp, "v3f test_verts[] = {\n" );
    for( int i=0; i<src->vertex_count; i ++ )
    {
-      fprintf( yeet, "   { %f, %f, %f },\n",
+      fprintf( fp, "   { %f, %f, %f },\n",
             src->vertices[i][0],
             src->vertices[i][1],
             src->vertices[i][2] );
    }
-   fprintf( yeet, "};\n" );
+   fprintf( fp, "};\n" );
 
-   fprintf( yeet, "struct cxr_input_loop test_loops[] = {\n" );
+   fprintf( fp, "struct cxr_input_loop test_loops[] = {\n" );
    for( int i=0; i<src->loop_count; i ++ )
    {
-      fprintf( yeet, "   {%d, %d},\n",
+      fprintf( fp, "   {%d, %d},\n",
          src->loops[i].index,
          src->loops[i].edge_index);
    }
-   fprintf( yeet, "};\n" );
+   fprintf( fp, "};\n" );
 
-   fprintf( yeet, "struct cxr_polygon test_polys[] = {\n" );
+   fprintf( fp, "struct cxr_polygon test_polys[] = {\n" );
    for( int i=0; i <src->poly_count; i++ )
    {
-      fprintf( yeet, "   {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n",
+      fprintf( fp, "   {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n",
          src->polys[i].loop_start,
          src->polys[i].loop_total,
          src->polys[i].normal[0],
@@ -2175,31 +2070,31 @@ CXR_API cxr_input_mesh *cxr_decompose(cxr_input_mesh *src)
          src->polys[i].center[1],
          src->polys[i].center[2] );
    }
-   fprintf( yeet, "};\n" );
+   fprintf( fp, "};\n" );
 
-   fprintf( yeet, "struct cxr_edge test_edges[] = {\n" );
+   fprintf( fp, "struct cxr_edge test_edges[] = {\n" );
    for( int i=0; i<src->edge_count; i++ )
    {
-      fprintf( yeet, "   {%d, %d, %d},\n",
+      fprintf( fp, "   {%d, %d, %d},\n",
          src->edges[i].i0,
          src->edges[i].i1,
          src->edges[i].freestyle
       );
    }
-   fprintf( yeet, "};\n" );
-
-   fprintf( yeet, "struct cxr_input_mesh test_mesh = {\n" );
-   fprintf( yeet, "   .vertices = test_verts,\n" );
-   fprintf( yeet, "   .loops = test_loops,\n" );
-   fprintf( yeet, "   .edges = test_edges,\n" );
-   fprintf( yeet, "   .polys = test_polys,\n" );
-   fprintf( yeet, "   .poly_count=%d,\n", src->poly_count );
-   fprintf( yeet, "   .vertex_count=%d,\n", src->vertex_count );
-   fprintf( yeet, "   .edge_count=%d,\n",src->edge_count );
-   fprintf( yeet, "   .loop_count=%d\n", src->loop_count );
-   fprintf( yeet, "};\n" );
+   fprintf( fp, "};\n" );
+
+   fprintf( fp, "struct cxr_input_mesh test_mesh = {\n" );
+   fprintf( fp, "   .vertices = test_verts,\n" );
+   fprintf( fp, "   .loops = test_loops,\n" );
+   fprintf( fp, "   .edges = test_edges,\n" );
+   fprintf( fp, "   .polys = test_polys,\n" );
+   fprintf( fp, "   .poly_count=%d,\n", src->poly_count );
+   fprintf( fp, "   .vertex_count=%d,\n", src->vertex_count );
+   fprintf( fp, "   .edge_count=%d,\n",src->edge_count );
+   fprintf( fp, "   .loop_count=%d\n", src->loop_count );
+   fprintf( fp, "};\n" );
  
-   fclose( yeet );
+   fclose( fp );
 
    return NULL;
 }
@@ -2226,17 +2121,17 @@ static int cxr_cardinal( v3f a, int ignore )
    return component;
 }
 
-// Convert contiguous mesh to patch of displacments
-//
-static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh, 
-   cxr_vdf *output )
-{
+/*
+ * Convert contiguous mesh to displacement patch
+ */
+static void cxr_write_disp( cxr_mesh *mesh, cxr_input_mesh *inputmesh, 
+      cxr_vdf *output
+){
    v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
 
-   // Create a graph which maps vertices by their connections
    struct vertinfo
    {
-      int con_start, con_count;     // Index into the connection graph
+      int con_start, con_count;
       int boundary,
           used,
           search,
@@ -2274,11 +2169,10 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
       }
    }
 
-   // Find best normal for brush patch. VBSP uses the original brush
-   // as reference for decal projection.
-   //
-   // These are clamped to be cardinal directions as to make the VMF somewhat
-   // human editable.
+   /* 
+    * Find best normal for brush patch. VBSP uses the original brush as an
+    * reference for decal projection in-game
+    */
 
    v3f avg_normal, refv, refu, refn;
    v3_zero(refv); v3_zero(refu); v3_zero(refn);
@@ -2289,15 +2183,19 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
       v3_add( poly->normal, avg_normal, avg_normal );
    }
    v3_divs( avg_normal, mesh->abpolys.count, avg_normal );
-   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 );
+   if( v3_length( avg_normal ) <= 1e-6 )
+      v3_copy( (v3f){ 0.0, 0.0, 1.0 }, avg_normal );
+   else
+      v3_normalize( avg_normal );      
+
+   /*
+    * Approximately match the area of the result brush faces to the actual
+    * area.
+    *
+    * Necessary for accuracy and even lightmap texel allocation
+    */
 
-   // Approximately matching the area of the result brush faces to the actual area
-   // this is to assign a 'best guess' amount of lightmap texels.
-   //
    double uv_area = 0.0, face_area = 0.0, sf;
    v2f uvboundmin, uvboundmax;
    v3f faceboundmin, faceboundmax;
@@ -2311,15 +2209,15 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
 
    for( int i=0; i<mesh->abpolys.count; i++ )
    {
-      cxr_polygon *poly = cxr_ab_ptr( &mesh->abpolys, i );
+      cxr_polygon *poly = &mesh->polys[i];
       
       for( int j=0; j<poly->loop_total; j++ )
       {
-         cxr_loop *lp0 = cxr_ab_ptr(&mesh->abloops, poly->loop_start+j);
+         cxr_loop *lp0 = &mesh->loops[ poly->loop_start+j ];
          v2_minv( lp0->uv, uvboundmin, uvboundmin);
          v2_maxv( lp0->uv, uvboundmax, uvboundmax);
-         v3_minv( cxr_ab_ptr(mesh->p_abverts,lp0->index), faceboundmin, faceboundmin );
-         v3_maxv( cxr_ab_ptr(mesh->p_abverts,lp0->index), faceboundmax, faceboundmax );
+         v3_minv( verts[lp0->index], faceboundmin, faceboundmin );
+         v3_maxv( verts[lp0->index], faceboundmax, faceboundmax );
       }
 
       for( int j=0; j<poly->loop_total-2; j++ )
@@ -2351,7 +2249,11 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
    sf = sqrt( face_area / uv_area );
    int corner_count = 0;
 
-   // Vertex classification
+   /*
+    * Vertex classification
+    *   boundary vertices:   they exist on a freestyle edge
+    *   corners:             only connected to other boundaries
+    */
    for( int i=0; i<mesh->p_abverts->count; i++ )
    {
       struct vertinfo *info = &vertinfo[i];
@@ -2369,18 +2271,21 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
          else
             non_manifold = 0;
       }
+
       if( count > 2 || non_manifold )
       {
          info->corner = 1;
          corner_count ++;
-
-         //cxr_debug_box( cxr_ab_ptr(abverts,i), 0.1, colour_success );
       }
    }
    
-   // TODO(harry): This currently only supports power 2 displacements
-   //              its quite straightforward to upgrade it.
-   //
+   /* 
+    * TODO(harry): This currently only supports power 2 displacements
+    *              its quite straightforward to upgrade it.
+    *
+    * TODO(harry): Error checking is needed here for bad input data
+    */
+
    int dispedge[16];
    v2f corner_uvs[4];
    int dispedge_count;
@@ -2398,401 +2303,401 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
          cxr_loop *l0 = &mesh->loops[ basepoly->loop_start+i0 ],
                   *l1 = &mesh->loops[ basepoly->loop_start+i1 ];
          struct vertinfo *info = &vertinfo[ l0->index ];
+         
+         if( !info->corner )
+            continue;
 
-         if( info->corner )
-         { 
-            int corner_count = 1;
-            
-            cxr_material *matptr = 
-               basepoly->material_id < 0 || inputmesh->material_count == 0? 
-                  &cxr_nodraw:
-                  &inputmesh->materials[ basepoly->material_id ];
-
-            dispedge_count = 2;
-            dispedge[0] = l0->index;
-            dispedge[1] = l1->index;
-            v2_copy( l0->uv, corner_uvs[0] );
-            
-            // 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});
-
-            // Collect edges 
-            // --------------------
+         int corner_count = 1;
+         
+         cxr_material *matptr = 
+            basepoly->material_id < 0 || inputmesh->material_count == 0? 
+               &cxr_nodraw:
+               &inputmesh->materials[ basepoly->material_id ];
+
+         dispedge_count = 2;
+         dispedge[0] = l0->index;
+         dispedge[1] = l1->index;
+         v2_copy( l0->uv, corner_uvs[0] );
+        
+         /* Consume (use) face from orignal mesh */
+         basepoly->loop_total = -1;
+         
+         while( dispedge_count < 17 )
+         {
+            struct vertinfo *edge_head =
+               &vertinfo[dispedge[dispedge_count-1]];
 
-            while( dispedge_count < 17 )
+            int newvert = 0;
+            
+            if( edge_head->corner )
             {
-               struct vertinfo *edge_head = &vertinfo[dispedge[dispedge_count-1]];
-               int newvert = 0;
-               
-               if( edge_head->corner )
+               /* Find polygon that has edge C-1 -> C */
+               for( int j=0; j<mesh->abpolys.count && !newvert; j++ )
                {
-                  // Find a polygon that has the edge C-1 -> C
-                  for( int j=0; j<mesh->abpolys.count && !newvert; j++ )
+                  cxr_polygon *poly = &mesh->polys[j];
+
+                  for( int k=0; k<poly->loop_total; k ++ )
                   {
-                     cxr_polygon *poly = &mesh->polys[j];
+                     int i0 = k,
+                         i1 = cxr_range(k+1,poly->loop_total);
+
+                     cxr_loop *l0 = &mesh->loops[ poly->loop_start+i0 ],
+                              *l1 = &mesh->loops[ poly->loop_start+i1 ];
 
-                     for( int k=0; k<poly->loop_total; k ++ )
+                     if( l0->index == dispedge[dispedge_count-2] &&
+                         l1->index == dispedge[dispedge_count-1] )
                      {
-                        int i0 = k,
-                            i1 = cxr_range(k+1,poly->loop_total);
-
-                        cxr_loop *l0 = &mesh->loops[ poly->loop_start+i0 ],
-                                 *l1 = &mesh->loops[ poly->loop_start+i1 ];
-
-                        if( l0->index == dispedge[dispedge_count-2] &&
-                            l1->index == dispedge[dispedge_count-1] )
-                        {
-                           // Take the vertex after that edge
-                           v2_copy( l1->uv, corner_uvs[corner_count ++] );
-
-                           int i2 = cxr_range(i1+1,poly->loop_total);
-                           cxr_loop *l2 = &mesh->loops[ poly->loop_start+i2 ];
-
-                           dispedge[dispedge_count ++] = l2->index;
-                           newvert = 1;
-                           poly->loop_total = -1;
-                           break;
-                        }
+                        /* Take the next edge */
+                        v2_copy( l1->uv, corner_uvs[corner_count ++] );
+
+                        int i2 = cxr_range(i1+1,poly->loop_total);
+                        cxr_loop *l2 = &mesh->loops[ poly->loop_start+i2 ];
+
+                        dispedge[dispedge_count ++] = l2->index;
+                        newvert = 1;
+                        poly->loop_total = -1;
+                        break;
                      }
                   }
                }
-               else
+            }
+            else
+            {
+               for( int j=0; j<edge_head->con_count; j++ )
                {
-                  for( int j=0; j<edge_head->con_count; j++ )
-                  {
-                     int con = graph[edge_head->con_start+j];
+                  int con = graph[edge_head->con_start+j];
 
-                     if( con == -1 )
-                        continue;
+                  if( con == -1 )
+                     continue;
 
-                     if( dispedge_count > 1 )
-                        if( con == dispedge[dispedge_count-2] )
-                           continue;
-                     
-                     struct vertinfo *coninfo = &vertinfo[con];
-                     
-                     if( !coninfo->boundary )
+                  if( dispedge_count > 1 )
+                     if( con == dispedge[dispedge_count-2] )
                         continue;
-                     
-                     /*
-                     cxr_debug_arrow( cxr_ab_ptr(abverts,dispedge[dispedge_count-1]),
-                                      cxr_ab_ptr(abverts,con),
-                                      (v3f){0,0,1},
-                                      0.1,
-                                      colour_success );
-                     */
-
-                     dispedge[ dispedge_count ++ ] = con;
-                     newvert = 1;
-
-                     break;
-                  }
-               }
+                  
+                  struct vertinfo *coninfo = &vertinfo[con];
+                  
+                  if( !coninfo->boundary )
+                     continue;
+                  
+                  dispedge[ dispedge_count ++ ] = con;
+                  newvert = 1;
 
-               if( !newvert )
-               {
-                  cxr_debug_box(verts[dispedge[dispedge_count-1]], 0.1, colour_error);
                   break;
                }
             }
-            
-            // --------------------
-            // Edges collected
-            
-            v2f va, vb;
-            v2_sub( corner_uvs[1], corner_uvs[0], va );
-            v2_sub( corner_uvs[2], corner_uvs[0], vb );
-            
-            // Connect up the grid
-            //
-            //   0 1 2 3 4
-            //  15 a b c d
-            //  14 e f g h
-            //  13 i j k l
-            //  12 m n o p
-            //
-            //  Example: a := common unused vertex that is connected to
-            //                by 1 and 15. Or y-1, and x-1 on the grid.
-            //           g := c and f common vert ^
-            //
-            int grid[25];
-            
-            for( int j=0; j<5; j++ ) grid[j] = dispedge[j];
-            for( int j=1; j<5; j++ ) grid[j*5+4] = dispedge[j+4];
-            for( int j=0; j<4; j++ ) grid[4*5+3-j] = dispedge[j+9];
-            for( int j=1; j<4; j++ ) grid[j*5] = dispedge[16-j];
 
-            // Grid fill
-            for( int j=1; j<4; j++ )
+            if( !newvert )
             {
-               for( int k=1; k<4; k++ )
-               {
-                  int s0 = grid[(j-1)*5+k],
-                      s1 = grid[j*5+k-1];
+               cxr_debug_box( verts[dispedge[dispedge_count-1]], 0.1, 
+                     colour_error);
+               break;
+            }
+         }
+         
+         /* All edges collected */
+         
+         v2f va, vb;
+         v2_sub( corner_uvs[1], corner_uvs[0], va );
+         v2_sub( corner_uvs[2], corner_uvs[0], vb );
+         
+         /* Connect up the grid
+          *
+          *   0 1 2 3 4
+          *  15 a b c d
+          *  14 e f g h
+          *  13 i j k l
+          *  12 m n o p
+          *
+          *  Example: a := common unused vertex that is connected to
+          *                by 1 and 15. Or y-1, and x-1 on the grid.
+          *           g := c and f common vert ^
+          */
+
+         int grid[25];
+         
+         for( int j=0; j<5; j++ ) grid[j] = dispedge[j];
+         for( int j=1; j<5; j++ ) grid[j*5+4] = dispedge[j+4];
+         for( int j=0; j<4; j++ ) grid[4*5+3-j] = dispedge[j+9];
+         for( int j=1; j<4; j++ ) grid[j*5] = dispedge[16-j];
 
-                  struct vertinfo *va = &vertinfo[s0],
-                                  *vb = &vertinfo[s1];
+         /* Fill in grid */
+         for( int j=1; j<4; j++ )
+         {
+            for( int k=1; k<4; k++ )
+            {
+               int s0 = grid[(j-1)*5+k],
+                   s1 = grid[j*5+k-1];
 
-                  // Find a common vertex between s0 and s1
-                  
-                  for( int l=0; l<va->con_count; l ++ )
+               struct vertinfo *va = &vertinfo[s0],
+                               *vb = &vertinfo[s1];
+               
+               /* Find common non-used vertex */
+               for( int l=0; l<va->con_count; l ++ )
+               {
+                  for( int m=0; m<vb->con_count; m ++ )
                   {
-                     for( int m=0; m<vb->con_count; m ++ )
-                     {
-                        int cona = graph[va->con_start+l],
-                            conb = graph[vb->con_start+m];
+                     int cona = graph[va->con_start+l],
+                         conb = graph[vb->con_start+m];
 
-                        if( cona == conb )
-                        {
-                           if( vertinfo[cona].used || vertinfo[cona].boundary )
-                              continue;
+                     if( cona == conb )
+                     {
+                        if( vertinfo[cona].used || vertinfo[cona].boundary )
+                           continue;
 
-                           grid[ j*5+k ] = cona;
-                           vertinfo[cona].used = 1;
+                        grid[ j*5+k ] = cona;
+                        vertinfo[cona].used = 1;
 
-                           goto IL_MATCHED_DISP_INTERIOR_VERT;
-                        }
+                        goto next_cell;
                      }
                   }
+               }
 
-                  // Broken displacement
-                  cxr_log( "Broken displacement!\n" );
-                  free( graph );
-                  free( vertinfo );
-                  return;
+               cxr_log( "Broken displacement!\n" );
+               free( graph );
+               free( vertinfo );
+               return;
 
-                  IL_MATCHED_DISP_INTERIOR_VERT:;
-               }
+               next_cell:;
             }
-
-            // Create brush vertices based on UV map
+         }
+         
+         /*
+         * Create V reference based on first displacement.
+         * 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.
+         *
+         *       Improvement can be made by selecting a first disp/triangle 
+         *       based on deterministic factors.
+         */
+         if( disp_count == 0 )
+         {
+            cxr_texinfo tx;
+            v3f tri_ref[3];
+            v3_copy( verts[dispedge[0]], tri_ref[0] );
+            v3_copy( verts[dispedge[4]], tri_ref[1] );
+            v3_copy( verts[dispedge[8]], tri_ref[2] );
+            cxr_calculate_axis( &tx, tri_ref, corner_uvs, (v2f){512,512} );
+
+            v3_muls( tx.vaxis, -1.0, refv );
+            int v_cardinal = cxr_cardinal( refv, -1 );
+
+            v3_cross( tx.vaxis, tx.uaxis, refn );
+            v3_muls( refn, -tx.winding, refn );
             
-            // Create V reference based on first displacement.
-            // 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.
-            //
-            //       Improvement can be made by selecting a first disp/triangle based
-            //       on deterministic factors.
-            //
-            if( disp_count == 0 )
-            {
-               cxr_texinfo tx;
-               v3f tri_ref[3];
-               v3_copy( verts[dispedge[0]], tri_ref[0] );
-               v3_copy( verts[dispedge[4]], tri_ref[1] );
-               v3_copy( verts[dispedge[8]], tri_ref[2] );
-               cxr_calculate_axis( &tx, tri_ref, corner_uvs, (v2f){512,512} );
+            /* Computing new reference vectors */
+            int n1_cardinal = cxr_cardinal( refn, v_cardinal );
+            
+            int u_cardinal = 0;
 
-               v3_muls( tx.vaxis, -1.0, refv );
-               int v_cardinal = cxr_cardinal( refv, -1 );
+            for( int j=0; j<2; j++ )
+               if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal ) 
+                  u_cardinal ++;
 
-               v3_cross( tx.vaxis, tx.uaxis, refn );
-               v3_muls( refn, -tx.winding, refn );
+            v3_zero(refu);
+            refu[u_cardinal] = tx.uaxis[u_cardinal] > 0.0? 1.0: -1.0;
 
-               int n1_cardinal = cxr_cardinal( refn, v_cardinal );
-               
-               //v3_copy( avg_normal, refn );
-               int u_cardinal = 0;
-               if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
-               if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal ) u_cardinal ++;
+            v3f p0, pv, pu, pn;
 
-               v3_zero(refu);
-               refu[u_cardinal] = tx.uaxis[u_cardinal] > 0.0? 1.0: -1.0;
+            v3_copy( face_center, p0 );
+            v3_muladds( face_center, refn, 1.5, pn );
+            v3_muladds( face_center, refv, 1.5, pv );
+            v3_muladds( face_center, refu, 1.5, pu );
+            
+            /* Draw reference vectors */
+            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});
+            }
+         }
 
-               v3f p0, pv, pu, pn;
+         /* Create world coordinates */
+         v3f world_corners[8];
+         v2f world_uv[4];
 
-               v3_copy( face_center, p0 );
-               v3_muladds( face_center, refn, 1.5, pn );
-               v3_muladds( face_center, refv, 1.5, pv );
-               v3_muladds( face_center, refu, 1.5, pu );
-               
-               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} );
-               }
-            }
+         for( int j=0; j<4; j++ )
+         {
+            v2f local_uv;
+            v2_sub( corner_uvs[j], uv_center, local_uv );
+            v2_copy( corner_uvs[j], world_uv[j] );
+            v2_muls( local_uv, sf, local_uv );
+
+            v3_muls( refu, local_uv[0], world_corners[j] );
+            v3_muladds( world_corners[j],refv,local_uv[1],world_corners[j] );
+            v3_add( face_center, world_corners[j], world_corners[j] );
+         }
 
-            // Create world cordinates
-            v3f world_corners[8];
-            v2f world_uv[4];
+         double *colour = colours_random[cxr_range(disp_count,8)];
 
+         for( int j=0; j<4; j++ )
+            v3_muladds( world_corners[j], refn, -1.0, world_corners[j+4] );
+         
+         if( cxr_settings.debug )
+         {
             for( int j=0; j<4; j++ )
             {
-               v2f local_uv;
-               v2_sub( corner_uvs[j], uv_center, local_uv );
-               v2_copy( corner_uvs[j], world_uv[j] );
-               v2_muls( local_uv, sf, local_uv );
-
-               v3_muls( refu, local_uv[0], world_corners[j] );
-               v3_muladds( world_corners[j], refv, local_uv[1], world_corners[j] );
-               v3_add( face_center, world_corners[j], world_corners[j] );
+               double *p0 = world_corners[j],
+                      *p1 = world_corners[cxr_range(j+1,4)];
+
+               cxr_debug_arrow( p0, p1, avg_normal, 0.1, colour );
             }
+         }
+         
+         /* Apply world transform */
+         for( int j=0; j<8; j++ )
+         {
+            double *p0 = world_corners[j];
+            v3_muls( p0, cxr_context.scale_factor, p0 );
 
-            double *colour = colours_random[cxr_range(disp_count,8)];
+            p0[2] += cxr_context.offset_z;
+         }
 
-            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_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" );
+         cxr_vdf_ki32( output, "id", ++ cxr_context.brush_count ); 
+
+         int sides[6][3] =
+         {{ 0, 1, 2 },
+          { 4, 6, 5 },
+          { 4, 1, 0 },
+          { 7, 0, 3 },
+          { 6, 2, 1 },
+          { 6, 3, 2 }};
+         
+         v3f normals[25];
+         double distances[25];
+         
+         v3f lside0, lside1, lref, vdelta, vworld;
+         double tx, ty;
+
+         for( int j=0; j<5; j++ )
+         {
+            ty = (double)j/(double)(5-1);
 
-            /*
-            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 );
-            */
+            v3_lerp( world_corners[0], world_corners[3], ty, lside0 );
+            v3_lerp( world_corners[1], world_corners[2], ty, lside1 );
 
-            // Apply world transform
-            for( int j=0; j<8; j++ )
+            for( int k=0; k<5; k++ )
             {
-               v3_muls( world_corners[j], cxr_context.scale_factor, world_corners[j] );
-               world_corners[j][2] += cxr_context.offset_z;
+               int index = j*5+k;
+
+               tx = (double)k/(double)(5-1);
+               v3_lerp( lside0, lside1, tx, lref );
+               v3_muls( verts[grid[index]], cxr_context.scale_factor, vworld );
+               vworld[2] += cxr_context.offset_z;
+
+               v3_sub( vworld, lref, vdelta );
+               v3_copy( vdelta, normals[index] );
+               v3_normalize( normals[index] );
+               distances[index] = v3_dot( vdelta, normals[index] );
             }
+         }
 
-            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" );
-            cxr_vdf_ki32( output, "id", ++ cxr_context.brush_count ); 
-
-            int sides[6][3] =
-            {{ 0, 1, 2 },
-             { 4, 6, 5 },
-             { 4, 1, 0 },
-             { 7, 0, 3 },
-             { 6, 2, 1 },
-             { 6, 3, 2 }};
+         for( int j=0; j<6; j++ )
+         {
+            int *side = sides[j];
+
+            cxr_vdf_node( output, "side" );
+            cxr_vdf_ki32( output, "id", ++ cxr_context.face_count );
+            cxr_vdf_plane( output, "plane", world_corners[side[2]],
+                                            world_corners[side[1]],
+                                            world_corners[side[0]] );
             
-            v3f normals[25];
-            double distances[25];
+            cxr_vdf_kv( output, "material", matptr->vmt_path );
+
+            cxr_vdf_kaxis( output, "uaxis", 
+                  texinfo_shared.uaxis, 
+                  texinfo_shared.offset[0], 
+                  texinfo_shared.scale[0] );
+            cxr_vdf_kaxis( output, "vaxis", 
+                  texinfo_shared.vaxis, 
+                  texinfo_shared.offset[1], 
+                  texinfo_shared.scale[1] );
+
+            cxr_vdf_kdouble( output, "rotation", 0.0 );
+            cxr_vdf_ki32( output, "lightmapscale", cxr_settings.lightmap_scale);
+            cxr_vdf_ki32( output, "smoothing_groups", 0 );
             
-            v3f lside0, lside1, lref, vdelta, vworld;
-            double tx, ty;
-
-            for( int j=0; j<5; j++ )
+            if( j == 0 )
             {
-               ty = (double)j/(double)(5-1);
-
-               v3_lerp( world_corners[0], world_corners[3], ty, lside0 );
-               v3_lerp( world_corners[1], world_corners[2], ty, lside1 );
-
+               cxr_vdf_node( output, "dispinfo" );
+               cxr_vdf_ki32( output, "power", 2 );
+               cxr_vdf_kv3f( output, "startposition", world_corners[0] );
+               cxr_vdf_ki32( output, "flags", 0 );
+               cxr_vdf_kdouble( output, "elevation", 0.0 );
+               cxr_vdf_ki32( output, "subdiv", 0 );
+               
+               cxr_vdf_node( output, "normals" );
                for( int k=0; k<5; k++ )
-               {
-                  int index = j*5+k;
-
-                  tx = (double)k/(double)(5-1);
-                  v3_lerp( lside0, lside1, tx, lref );
-                  v3_muls( verts[grid[index]], cxr_context.scale_factor, vworld );
-                  vworld[2] += cxr_context.offset_z;
-
-                  v3_sub( vworld, lref, vdelta );
-                  v3_copy( vdelta, normals[index] );
-                  v3_normalize( normals[index] );
-                  distances[index] = v3_dot( vdelta, normals[index] );
-               }
-            }
+                  cxr_vdf_karrv3f( output, "row", k, &normals[k*5], 5 );
+               cxr_vdf_edon( output );
+               
+               cxr_vdf_node( output, "distances" );
+               for( int k=0; k<5; k++ )
+                  cxr_vdf_karrdouble( output, "row", k, &distances[k*5], 5 );
+               cxr_vdf_edon( output );
+               
+               /*
+                * TODO: This might be needed for the compilers. Opens fine in 
+                * hammer
+                */
 
-            for( int j=0; j<6; j++ )
-            {
-               int *side = sides[j];
+               /*
+               cxr_vdf_node( output, "offsets" );
+               for( int k=0; k<5; k++ )
+                  cxr_vdf_printf( output, 
+                     "\"row%d\" \"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\"\n", k );
+               cxr_vdf_edon( output );
 
-               cxr_vdf_node( output, "side" );
-               cxr_vdf_ki32( output, "id", ++ cxr_context.face_count );
-               cxr_vdf_plane( output, "plane", world_corners[side[2]],
-                                               world_corners[side[1]],
-                                               world_corners[side[0]] );
+               cxr_vdf_node( output, "offset_normals" );
+               for( int k=0; k<5; k++ )
+                  cxr_vdf_printf( output, 
+                     "\"row%d\" \"0 0 1 0 0 1 0 0 1 0 0 1 0 0 1\"\n", k );
+               cxr_vdf_edon( output );
                
-               cxr_vdf_kv( output, "material", matptr->vmt_path );
-
-               cxr_vdf_kaxis( output, "uaxis", 
-                     texinfo_shared.uaxis, 
-                     texinfo_shared.offset[0], 
-                     texinfo_shared.scale[0] );
-               cxr_vdf_kaxis( output, "vaxis", 
-                     texinfo_shared.vaxis, 
-                     texinfo_shared.offset[1], 
-                     texinfo_shared.scale[1] );
-
-               cxr_vdf_kdouble( output, "rotation", 0.0 );
-               cxr_vdf_ki32( output, "lightmapscale", cxr_settings.lightmap_scale );
-               cxr_vdf_ki32( output, "smoothing_groups", 0 );
+               cxr_vdf_node( output, "alphas" );
+               for( int k=0; k<5; k++ )
+                  cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0\"\n", k );
+               cxr_vdf_edon( output );
                
-               if( j == 0 )
-               {
-                  cxr_vdf_node( output, "dispinfo" );
-                  cxr_vdf_ki32( output, "power", 2 );
-                  cxr_vdf_kv3f( output, "startposition", world_corners[0] );
-                  cxr_vdf_ki32( output, "flags", 0 );
-                  cxr_vdf_kdouble( output, "elevation", 0.0 );
-                  cxr_vdf_ki32( output, "subdiv", 0 );
-                  
-                  cxr_vdf_node( output, "normals" );
-                  for( int k=0; k<5; k++ )
-                     cxr_vdf_karrv3f( output, "row", k, &normals[k*5], 5 );
-                  cxr_vdf_edon( output );
-                  
-                  cxr_vdf_node( output, "distances" );
-                  for( int k=0; k<5; k++ )
-                     cxr_vdf_karrdouble( output, "row", k, &distances[k*5], 5 );
-                  cxr_vdf_edon( output );
-                  
-                  // TODO: This might be needed for compiling...
-                  /*
-                  cxr_vdf_node( output, "offsets" );
-                  for( int k=0; k<5; k++ )
-                     cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\"\n", k );
-                  cxr_vdf_edon( output );
-
-                  cxr_vdf_node( output, "offset_normals" );
-                  for( int k=0; k<5; k++ )
-                     cxr_vdf_printf( output, "\"row%d\" \"0 0 1 0 0 1 0 0 1 0 0 1 0 0 1\"\n", k );
-                  cxr_vdf_edon( output );
-                  
-                  cxr_vdf_node( output, "alphas" );
-                  for( int k=0; k<5; k++ )
-                     cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0\"\n", k );
-                  cxr_vdf_edon( output );
-                  
-                  cxr_vdf_node( output, "triangle_tags" );
-                  for( int k=0; k<5-1; k++ )
-                     cxr_vdf_printf( output, "\"row%d\" \"9 9 9 9 9 9 9 9\"\n", k );
-                  cxr_vdf_edon( output );
-                  
-                  cxr_vdf_node( output, "allowed_verts" );
-                  cxr_vdf_printf( output, "\"10\" \"-1 -1 -1 -1 -1 -1 -1 -1 -1 -1\"\n" );
-                  cxr_vdf_edon( output );
-                  */
-                  cxr_vdf_edon( output );
-               }
+               cxr_vdf_node( output, "triangle_tags" );
+               for( int k=0; k<5-1; k++ )
+                  cxr_vdf_printf( output, 
+                     "\"row%d\" \"9 9 9 9 9 9 9 9\"\n", k );
+               cxr_vdf_edon( output );
+               
+               cxr_vdf_node( output, "allowed_verts" );
+               cxr_vdf_printf( output, 
+                  "\"10\" \"-1 -1 -1 -1 -1 -1 -1 -1 -1 -1\"\n" );
+               cxr_vdf_edon( output );
+               */
 
                cxr_vdf_edon( output );
             }
 
-            cxr_vdf_node(output, "editor");
-            cxr_vdf_colour255(output,"color", colours_random[cxr_range(cxr_context.brush_count,8)]);
-            cxr_vdf_ki32(output,"visgroupshown",1);
-            cxr_vdf_ki32(output,"visgroupautoshown",1);
-            cxr_vdf_edon(output);
-
             cxr_vdf_edon( output );
-            disp_count ++;
          }
+
+         cxr_vdf_node( output, "editor");
+         cxr_vdf_colour255( output, "color", 
+               colours_random[cxr_range(cxr_context.brush_count,8)]);
+
+         cxr_vdf_ki32( output, "visgroupshown",1);
+         cxr_vdf_ki32( output, "visgroupautoshown",1);
+         cxr_vdf_edon( output );
+
+         cxr_vdf_edon( output );
+         disp_count ++;
       }
    }
 
@@ -2800,8 +2705,9 @@ static void cxr_write_disp(cxr_mesh *mesh, cxr_input_mesh *inputmesh,
    free( vertinfo );
 }
 
-static int cxr_solid_checkerr(cxr_mesh *mesh, cxr_auto_buffer *abverts )
+static int cxr_solid_checkerr( cxr_mesh *mesh )
 {
+   v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
    int err_count = 0;
 
    for( int i=0; i<mesh->abpolys.count; i++ )
@@ -2816,7 +2722,7 @@ static int cxr_solid_checkerr(cxr_mesh *mesh, cxr_auto_buffer *abverts )
       for( int j=0; j<poly->loop_total; j++ )
       {
          cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
-         double *vert = cxr_ab_ptr(abverts,loop->index);
+         double *vert = verts[ loop->index ];
 
          if( fabs(plane_polarity(plane,vert)) > 0.0025 )
          {
@@ -2840,8 +2746,7 @@ static int cxr_solid_checkerr(cxr_mesh *mesh, cxr_auto_buffer *abverts )
 
 CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
 {
-   // Split mesh into islands
-   cxr_auto_buffer abverts;
+   cxr_abuffer abverts;
    cxr_mesh *main_mesh = cxr_to_internal_format(src, &abverts);
 
    u32 error = 0x00;
@@ -2853,12 +2758,12 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
       int is_displacement, invalid;
    };
 
-   cxr_auto_buffer solids;
+   cxr_abuffer solids;
    cxr_ab_init( &solids, sizeof(struct solidinf), 2 );
 
-   // Preprocessor 1: Island seperation
-   //  ---------------
-
+   /
+    * Preprocessor 1: Island seperation
+    */
    while(1)
    {
       cxr_mesh *res = cxr_pull_island( main_mesh );
@@ -2870,27 +2775,28 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
    }
    cxr_ab_push( &solids, &(struct solidinf){main_mesh,0} );
    
-   // Preprocessor 2: Displacement break-out and error checking
-   //  ---------------
+   /*
+    * Preprocessor 2: Displacement processing & error checks
+    */
    for( int i=0; i<solids.count; i++ )
    {
       struct solidinf *pinf = cxr_ab_ptr(&solids,i);
       
       for( int j=0; j<pinf->pmesh->abpolys.count; j++ )
       {
-         cxr_polygon *poly = cxr_ab_ptr( &pinf->pmesh->abpolys, j );
+         cxr_polygon *poly = &pinf->pmesh->polys[ j ];
 
          for( int k=0; k<poly->loop_total; k++ )
          {
-            cxr_loop *lp = cxr_ab_ptr( &pinf->pmesh->abloops, poly->loop_start+k );
-            cxr_edge *edge = cxr_ab_ptr( &pinf->pmesh->abedges, lp->edge_index );
+            cxr_loop *lp = &pinf->pmesh->loops[ poly->loop_start+k ];
+            cxr_edge *edge = &pinf->pmesh->edges[ lp->edge_index ];
 
             if( edge->freestyle )
                goto IL_SOLID_IS_DISPLACEMENT;
          }
       }
       
-      if( cxr_solid_checkerr( pinf->pmesh, &abverts ) )
+      if( cxr_solid_checkerr( pinf->pmesh ) )
       {
          pinf->invalid = 1;
          invalid_count ++;
@@ -2903,8 +2809,9 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
       cxr_write_disp( pinf->pmesh, src, output );
    }
 
-   // Preprocessor 3: Breakup non-convex shapes into sub-solids
-   //  ---------------
+   /*
+    * Main convex decomp algorithm
+    */
    int sources_count = solids.count;
    
    for( int i=0; i<sources_count; i++ )
@@ -2928,7 +2835,7 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
          {
             if( error )
             {
-               // If no solids error we can rety while preserving 'hot' edges
+               /* Retry if non-critical error, with extra edges */
 
                if( error & CXR_ERROR_NO_SOLIDS )
                {
@@ -2973,8 +2880,7 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
       return error;
    }
 
-   // Turn all those solids into VMF brushes
-   // --------------------------------------
+   /* Write all solids as VMF brushes */
    for( int i=0; i<solids.count; i++ )
    {
       struct solidinf *solid = cxr_ab_ptr(&solids,i);
@@ -2986,8 +2892,9 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
       
       for( int j=0; j<solid->pmesh->abpolys.count; j++ )
       {
-         cxr_polygon *poly = cxr_ab_ptr( &solid->pmesh->abpolys, j );
-         cxr_loop *ploops = cxr_ab_ptr(&solid->pmesh->abloops, poly->loop_start);
+         cxr_polygon *poly = &solid->pmesh->polys[j];
+         cxr_loop *ploops = &solid->pmesh->loops[poly->loop_start];
+
          cxr_material *matptr = 
             poly->material_id < 0 || src->material_count == 0? 
                &cxr_nodraw:
@@ -2998,9 +2905,14 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
 
          v3f verts[3]; v2f uvs[3];
 
-         v3_muls( cxr_ab_ptr(&abverts, ploops[0].index), cxr_context.scale_factor, verts[0] );
-         v3_muls( cxr_ab_ptr(&abverts, ploops[1].index), cxr_context.scale_factor, verts[1] );
-         v3_muls( cxr_ab_ptr(&abverts, ploops[2].index), cxr_context.scale_factor, verts[2] );
+         int i0 = ploops[0].index,
+             i1 = ploops[1].index,
+             i2 = ploops[2].index;
+
+         v3_muls( cxr_ab_ptr(&abverts,i0), cxr_context.scale_factor, verts[0] );
+         v3_muls( cxr_ab_ptr(&abverts,i1), cxr_context.scale_factor, verts[1] );
+         v3_muls( cxr_ab_ptr(&abverts,i2), cxr_context.scale_factor, verts[2] );
+
          verts[0][2] += cxr_context.offset_z;
          verts[1][2] += cxr_context.offset_z;
          verts[2][2] += cxr_context.offset_z;
@@ -3012,24 +2924,27 @@ CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
          cxr_vdf_plane( output, "plane", verts[2], verts[1], verts[0] );
          cxr_vdf_kv( output, "material", matptr->vmt_path );
          
-         cxr_texinfo trans;
-         cxr_calculate_axis(&trans, verts, uvs, (double[2]){ matptr->res[0], matptr->res[1] });
+         cxr_texinfo tx;
+         cxr_calculate_axis( &tx, verts, uvs, 
+               (double[2]){ matptr->res[0], matptr->res[1] });
 
-         cxr_vdf_kaxis(output, "uaxis", trans.uaxis, trans.offset[0], trans.scale[0]);
-         cxr_vdf_kaxis(output, "vaxis", trans.vaxis, trans.offset[1], trans.scale[1]);
+         cxr_vdf_kaxis( output, "uaxis", tx.uaxis, tx.offset[0], tx.scale[0]);
+         cxr_vdf_kaxis( output, "vaxis", tx.vaxis, tx.offset[1], tx.scale[1]);
 
-         cxr_vdf_kdouble(output, "rotation", 0.0 );
-         cxr_vdf_ki32(output, "lightmapscale", cxr_settings.lightmap_scale );
-         cxr_vdf_ki32(output, "smoothing_groups", 0);
+         cxr_vdf_kdouble( output, "rotation", 0.0 );
+         cxr_vdf_ki32( output, "lightmapscale", cxr_settings.lightmap_scale );
+         cxr_vdf_ki32( output, "smoothing_groups", 0);
 
          cxr_vdf_edon( output );
       }
 
-      cxr_vdf_node(output, "editor");
-      cxr_vdf_colour255(output,"color", colours_random[cxr_range(cxr_context.brush_count,8)]);
-      cxr_vdf_ki32(output,"visgroupshown",1);
-      cxr_vdf_ki32(output,"visgroupautoshown",1);
-      cxr_vdf_edon(output);
+      cxr_vdf_node( output, "editor" );
+      cxr_vdf_colour255( output, "color", 
+            colours_random[cxr_range(cxr_context.brush_count,8)]);
+
+      cxr_vdf_ki32( output, "visgroupshown", 1 );
+      cxr_vdf_ki32( output, "visgroupautoshown", 1 );
+      cxr_vdf_edon( output );
 
       cxr_vdf_edon( output );
    }
@@ -3061,10 +2976,9 @@ CXR_API void cxr_settings_update( struct cxr_settings *settings )
    cxr_settings = *settings;
 }
 
-// Valve copyright stuff probably maybe
-//  whatever, my previous copyright decleration ends here
-// ----------------------------------------------------------
-
+/* 
+ * Valve Source SDK 2015 CS:GO
+ */
 #define        HEADER_LUMPS            64
 #define  LUMP_WORLDLIGHTS  54
 
@@ -3109,13 +3023,14 @@ struct worldlight
 };
 #pragma pack(pop)
 
-// Utility for patching BSP tools to remove -1 distance lights (we set them
-//  like that, because we want these lights to go away)
-//
-//  Yes, there is no way to do this in hammer
-//  Yes, the distance KV is unused but still gets compiled to this lump
-//  No, Entities only compile will not do this for you
-//
+/*
+ * Utility for patching BSP tools to remove -1 distance lights (we set them
+ *  like that, because we want these lights to go away)
+ *
+ *  Yes, there is no way to do this in hammer
+ *  Yes, the distance KV is unused but still gets compiled to this lump
+ *  No, Entities only compile will not do this for you
+ */
 CXR_API int cxr_lightpatch_bsp( const char *path )
 {
    printf( "Lightpatch: %s\n", path );
@@ -3128,17 +3043,17 @@ CXR_API int cxr_lightpatch_bsp( const char *path )
       return 0;
    }
 
-   // Read bsp
+   /* Read bsp */
    struct header header;
    fread( &header, sizeof(struct header), 1, fp );
    struct lump *lump = &header.lumps[ LUMP_WORLDLIGHTS ];
 
-   // Read worldlight array
+   /* Read worldlight array */
    struct worldlight *lights = malloc( lump->filelen );
    fseek( fp, lump->fileofs, SEEK_SET );
    fread( lights, lump->filelen, 1, fp );
 
-   // Remove all marked lights
+   /* Remove all marked lights */
    int light_count = lump->filelen / sizeof(struct worldlight);
    int new_count = 0;
 
@@ -3148,7 +3063,7 @@ CXR_API int cxr_lightpatch_bsp( const char *path )
    
    lump->filelen = new_count*sizeof(struct worldlight);
 
-   // Write changes
+   /* Write changes back to file */
    fseek( fp, lump->fileofs, SEEK_SET );
    fwrite( lights, lump->filelen, 1, fp );
    fseek( fp, 0, SEEK_SET );
index 031c61e4d2f7d787dc53a0efe7c58e9660083956..68f32ffe035ce6c79837ab4b764bf810b299874f 100644 (file)
@@ -1,9 +1,15 @@
-// Copyright (C) 2021 Harry Godden (hgn)
+/* Copyright (C) 2021 Harry Godden (hgn) 
+ *
+ * Straightforward implementations for:
+ *   Vector 2,3,4
+ *   Simple Matrices in 3x3 and 4x3
+ *   Plane maths
+ *   Other useful geometric functions
+ */
 
 #define CXR_INLINE static inline
 #define CXR_PIf 3.14159265358979323846264338327950288f
 
-// Simple min/max replacements
 CXR_INLINE double cxr_minf( double a, double b )
 {
        return a < b? a: b;
@@ -29,15 +35,14 @@ CXR_INLINE double cxr_clampf( double v, double a, double b )
    return cxr_minf( b, cxr_maxf( a, v ) );
 }
 
-// Convert degrees to radians
 CXR_INLINE double cxr_rad( double deg )
 {
        return deg * CXR_PIf / 180.0f;
 }
 
-// Vector 2
-// ==================================================================================================================
-
+/*
+ * Vector 2 Functions
+ */
 CXR_INLINE void v2_zero( v2f a )
 {
    a[0] = 0.0; a[1] = 0.0;
@@ -132,8 +137,9 @@ CXR_INLINE void v2_normalize( v2f a )
        v2_muls( a, 1.0 / v2_length( a ), a );
 }
 
-// Vector 3
-// ==================================================================================================================
+/* 
+ * Vector 3 Functions
+ */
 
 CXR_INLINE void v3_zero( v3f a )
 {
@@ -267,9 +273,9 @@ CXR_INLINE void v3_fill( v3f a, double v )
        a[2] = v;
 }
 
-// Vector 4
-// ==================================================================================================================
-
+/*
+ * Vector 4 Functions
+ */
 CXR_INLINE void v4_copy( v4f a, v4f b )
 {
        b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];
@@ -285,8 +291,9 @@ CXR_INLINE void v4_muls( v4f a, double s, v4f d )
        d[0] = a[0]*s; d[1] = a[1]*s; d[2] = a[2]*s; d[3] = a[3]*s;
 }
 
-// Matrix 3x3
-//======================================================================================================
+/*
+ * Matrix 3x3
+ */
 
 CXR_INLINE void m3x3_inv_transpose( m3x3f src, m3x3f dest )
 {
@@ -321,8 +328,9 @@ CXR_INLINE void m3x3_mulv( m3x3f m, v3f v, v3f d )
        v3_copy( res, d );
 }
 
-// Matrix 4x3
-// ==================================================================================================================
+/*
+ * Matrix 4x3
+ */
 
 #define M4X3_IDENTITY  {{1.0f, 0.0f, 0.0f, },\
                                                                { 0.0f, 1.0f, 0.0f, },\
@@ -387,8 +395,9 @@ CXR_INLINE void m4x3_mulv( m4x3f m, v3f v, v3f d )
        v3_copy( res, d );
 }
 
-// Affine transforms
-
+/*
+ * Affine transformations
+ */
 CXR_INLINE void m4x3_translate( m4x3f m, v3f v )
 {
        v3_muladds( m[3], m[0], v[0], m[3] );
@@ -451,7 +460,6 @@ CXR_INLINE void m4x3_rotate_z( m4x3f m, double angle )
        m4x3_mul( m, t, m );
 }
 
-// Warning: These functions are unoptimized..
 CXR_INLINE void m4x3_expand_aabb_point( m4x3f m, boxf box, v3f point )
 {
        v3f v;
@@ -507,63 +515,28 @@ CXR_INLINE void tri_to_plane( v3f a, v3f b, v3f c, v4f plane )
    plane[3] = v3_dot( plane, a );
 }
 
-// TODO update this code to use normal v3_x functions 
-CXR_INLINE void tri_to_plane1( double a[3], double b[3], double c[3], double p[4] )
-{
-       double edge0[3];
-       double edge1[3];
-       double l;
-       
-       edge0[0] = b[0] - a[0];
-       edge0[1] = b[1] - a[1];
-       edge0[2] = b[2] - a[2];
-       
-       edge1[0] = c[0] - a[0];
-       edge1[1] = c[1] - a[1];
-       edge1[2] = c[2] - a[2];
-       
-       p[0] = edge0[1] * edge1[2] - edge0[2] * edge1[1];
-       p[1] = edge0[2] * edge1[0] - edge0[0] * edge1[2];
-       p[2] = edge0[0] * edge1[1] - edge0[1] * edge1[0];
-       
-       l = sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
-       p[3] = (p[0] * a[0] + p[1] * a[1] + p[2] * a[2]) / l;
-       
-       p[0] = p[0] / l;
-       p[1] = p[1] / l;
-       p[2] = p[2] / l;
-}
-
-CXR_INLINE int plane_intersect( double a[4], double b[4], double c[4], double p[3] )
+CXR_INLINE int plane_intersect( v4f a, v4f b, v4f c, v3f p )
 {
        double const epsilon = 0.001;
        
-       double x[3];
+   v3f x, bc, ca, ab;
        double d;
        
-       x[0] = a[1] * b[2] - a[2] * b[1];
-       x[1] = a[2] * b[0] - a[0] * b[2];
-       x[2] = a[0] * b[1] - a[1] * b[0];
-       
-       d = x[0] * c[0] + x[1] * c[1] + x[2] * c[2];
+   v3_cross( a, b, x );
+   d = v3_dot( x, c );
        
        if( d < epsilon && d > -epsilon ) return 0;
        
-       p[0] = (b[1] * c[2] - b[2] * c[1]) * -a[3];
-       p[1] = (b[2] * c[0] - b[0] * c[2]) * -a[3];
-       p[2] = (b[0] * c[1] - b[1] * c[0]) * -a[3];
-       
-       p[0] += (c[1] * a[2] - c[2] * a[1]) * -b[3];
-       p[1] += (c[2] * a[0] - c[0] * a[2]) * -b[3];
-       p[2] += (c[0] * a[1] - c[1] * a[0]) * -b[3];
-       
-       p[0] += (a[1] * b[2] - a[2] * b[1]) * -c[3];
-       p[1] += (a[2] * b[0] - a[0] * b[2]) * -c[3];
-       p[2] += (a[0] * b[1] - a[1] * b[0]) * -c[3];
-       
-       p[0] = -p[0] / d;
-       p[1] = -p[1] / d;
-       p[2] = -p[2] / d;
+   v3_cross(b,c,bc);
+   v3_cross(c,a,ca);
+   v3_cross(a,b,ab);
+
+   v3_muls(       bc, -a[3], p );
+   v3_muladds( p, ca, -b[3], p );
+   v3_muladds( p, ab, -c[3], p );
+
+   v3_negate( p, p );
+   v3_divs( p, d, p );
        
        return 1;
 }
@@ -574,12 +547,11 @@ CXR_INLINE void normal_to_plane( v3f normal, v3f p, v4f plane )
    plane[3] = v3_dot( normal, p );
 }
 
-CXR_INLINE double plane_polarity( double p[4], double a[3] )
+CXR_INLINE double plane_polarity( v4f p, v3f a )
 {
        return 
-       (a[0] * p[0] + a[1] * p[1] + a[2] * p[2])
-       -(p[0]*p[3] * p[0] + p[1]*p[3] * p[1] + p[2]*p[3] * p[2])
-       ;
+       v3_dot( a, p ) 
+     - (p[0]*p[3]*p[0] + p[1]*p[3]*p[1] + p[2]*p[3]*p[2]);     
 }
 
 CXR_INLINE void plane_project_point( v4f plane, v3f a, v3f d )
index 23e61c4e8bec26aa925aa2cd742466a709de0171..437831489ce2c741a810b00401730467abf9b38d 100644 (file)
@@ -1,6 +1,6 @@
-typedef struct cxr_auto_buffer cxr_auto_buffer;
+typedef struct cxr_abuffer cxr_abuffer;
 
-struct cxr_auto_buffer
+struct cxr_abuffer
 {
    u8 *arr;
    u32 esize,
@@ -16,7 +16,7 @@ struct cxr_auto_buffer
  #define cxr_ab_ptr(b,i) __cxr_ab_ptr(b,i)
 #endif
 
-static void *__cxr_ab_ptr( struct cxr_auto_buffer *buffer, u32 index
+static void *__cxr_ab_ptr( struct cxr_abuffer *buffer, u32 index
 
 #ifdef CXR_DEBUG_ALLOCATORS
       ,const char *debug_str 
@@ -36,7 +36,7 @@ static void *__cxr_ab_ptr( struct cxr_auto_buffer *buffer, u32 index
    return buffer->arr + buffer->esize*index;
 }
 
-static void cxr_ab_reserve( struct cxr_auto_buffer *buffer, u32 count )
+static void cxr_ab_reserve( struct cxr_abuffer *buffer, u32 count )
 {
    if( buffer->count + count > buffer->capacity )
    {
@@ -45,13 +45,13 @@ static void cxr_ab_reserve( struct cxr_auto_buffer *buffer, u32 count )
    }
 }
 
-static void *cxr_ab_empty( struct cxr_auto_buffer *buffer )
+static void *cxr_ab_empty( struct cxr_abuffer *buffer )
 {
    cxr_ab_reserve( buffer, 1 );
    return cxr_ab_ptr( buffer, buffer->count ++ );
 }
 
-static void *cxr_ab_empty_at( struct cxr_auto_buffer *buffer, int at )
+static void *cxr_ab_empty_at( struct cxr_abuffer *buffer, int at )
 {
    cxr_ab_reserve( buffer, 1 );
    
@@ -61,7 +61,7 @@ static void *cxr_ab_empty_at( struct cxr_auto_buffer *buffer, int at )
       return cxr_ab_ptr( buffer, at );
    }
 
-   // Shift rest 1 right
+   /* Shift buffer to make room */
    memmove
    ( 
       cxr_ab_ptr( buffer, at+1 ), 
@@ -73,7 +73,7 @@ static void *cxr_ab_empty_at( struct cxr_auto_buffer *buffer, int at )
    return cxr_ab_ptr( buffer, at );
 }
 
-static void cxr_ab_push( struct cxr_auto_buffer *buffer, void *em )
+static void cxr_ab_push( struct cxr_abuffer *buffer, void *em )
 {
    cxr_ab_reserve( buffer, 1 );
 
@@ -81,7 +81,7 @@ static void cxr_ab_push( struct cxr_auto_buffer *buffer, void *em )
    buffer->count ++;
 }
 
-static void cxr_ab_init( struct cxr_auto_buffer *buffer, u32 esize, u32 cap )
+static void cxr_ab_init( struct cxr_abuffer *buffer, u32 esize, u32 cap )
 {
    buffer->esize = esize;
    buffer->capacity = cxr_max(1,cap);
@@ -90,12 +90,12 @@ static void cxr_ab_init( struct cxr_auto_buffer *buffer, u32 esize, u32 cap )
    buffer->arr = malloc( buffer->esize*buffer->capacity );
 }
 
-static void cxr_ab_clear( struct cxr_auto_buffer *buffer )
+static void cxr_ab_clear( struct cxr_abuffer *buffer )
 {
    buffer->count = 0;
 }
 
-static void cxr_ab_free( struct cxr_auto_buffer *buffer )
+static void cxr_ab_free( struct cxr_abuffer *buffer )
 {
    free( buffer->arr );
 }