From e17cd5d7861480f19148b1eb5b15a520df838b1f Mon Sep 17 00:00:00 2001 From: hgn Date: Sun, 10 Apr 2022 05:46:14 +0100 Subject: [PATCH] Refactor el big #2 --- __init__.py | 73 +- src/convexer.c | 2289 +++++++++++++++++++++++------------------------- src/cxr_math.h | 114 +-- src/cxr_mem.h | 22 +- 4 files changed, 1199 insertions(+), 1299 deletions(-) diff --git a/__init__.py b/__init__.py index 624bce0..8cc9638 100644 --- a/__init__.py +++ b/__init__.py @@ -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( diff --git a/src/convexer.c b/src/convexer.c index 773a3d3..47ba128 100644 --- a/src/convexer.c +++ b/src/convexer.c @@ -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; ifp, "\"\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; ifp, "%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; iabpolys.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; kedge_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; jabpolys, 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; jabloops); + 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; mpolys[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; lpolys, 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; lloop_count ) ) { - cxr_log( "non-manifold edges are in the mesh: " - "implicit internal geometry does not have full support\n" ); + for( int l=0; lloop_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; iabpolys.count; i ++ ) + /* Gather list of unique edges */ + + for( int j=0; jcount; 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; jloop_total; j++ ) + for( int k=0; kloop_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; ledge_index == loop->edge_index ) + goto skip_edge; + + for( int l=0; lcount; 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; iabedges.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; icount; 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; jedges, 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; iabpolys.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; iabpolys.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; jpolys[solid[j]]; + curface = edge_list[j]->poly_left; - for( int k=0; kloop_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; mpoly_right ) - goto IL_SKIP_SIMILAR_PLANES; - - for( int m=0; mpolys[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; ipolys[ 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; labpolys.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; labedges.count*sizeof(int)); + for( int i=0; i< mesh->abedges.count; i++ ) + edge_important[i] = 0; + + for( int i=0; iabpolys.count; i ++ ) + { + cxr_polygon *poly = &mesh->polys[i]; + int not_tagged = -1, + tag_count = 0; + + for( int j=0; jloop_total; j++ ) { - for( int k=j+1; kloops[ 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; ledge_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; iabedges.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; iabpolys.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; iabpolys.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; jpolys[ 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; icount <= 2 ) continue; - - int init_reverse = 0; - int unique_edge_count = 0; - - for( int j=0; jcount; j++ ) - { - cxr_polygon *poly = cxr_ab_ptr(&mesh->abpolys, solid_buffer[solid->start+j]); - - for( int k=0; kloop_total; k++ ) - { - cxr_loop *loop = cxr_ab_ptr(&mesh->abloops, poly->loop_start+k); - - for( int l=0; ledge_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; lcount; 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; jp_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; jedges[ 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; jcount; 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; jedges[ 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; jindex],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; jedge_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; icount; 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; jloop_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 ) + 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; lp_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; ipolys[ 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; labpolys.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; iabpolys.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; jloop_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; ivertex_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; iloop_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 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; iedge_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; iabpolys.count; i++ ) { - cxr_polygon *poly = cxr_ab_ptr( &mesh->abpolys, i ); + cxr_polygon *poly = &mesh->polys[i]; for( int j=0; jloop_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; jloop_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; ip_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; jabpolys.count && !newvert; j++ ) { - // Find a polygon that has the edge C-1 -> C - for( int j=0; jabpolys.count && !newvert; j++ ) + cxr_polygon *poly = &mesh->polys[j]; + + for( int k=0; kloop_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; kloop_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; jcon_count; j++ ) { - for( int j=0; jcon_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; lcon_count; l ++ ) + struct vertinfo *va = &vertinfo[s0], + *vb = &vertinfo[s1]; + + /* Find common non-used vertex */ + for( int l=0; lcon_count; l ++ ) + { + for( int m=0; mcon_count; m ++ ) { - for( int m=0; mcon_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; iabpolys.count; i++ ) @@ -2816,7 +2722,7 @@ static int cxr_solid_checkerr(cxr_mesh *mesh, cxr_auto_buffer *abverts ) for( int j=0; jloop_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; ipmesh->abpolys.count; j++ ) { - cxr_polygon *poly = cxr_ab_ptr( &pinf->pmesh->abpolys, j ); + cxr_polygon *poly = &pinf->pmesh->polys[ j ]; for( int k=0; kloop_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; ipmesh->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 ); diff --git a/src/cxr_math.h b/src/cxr_math.h index 031c61e..68f32ff 100644 --- a/src/cxr_math.h +++ b/src/cxr_math.h @@ -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 ) diff --git a/src/cxr_mem.h b/src/cxr_mem.h index 23e61c4..4378314 100644 --- a/src/cxr_mem.h +++ b/src/cxr_mem.h @@ -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 ); } -- 2.25.1