X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=cxr%2Fcxr.h;h=0585f1a240bcc66165bb3a1081844734eab2a04a;hb=2b81894272ade16dbe3f71514e8eb25b2962bf9e;hp=bb904c4d00ba8ce0522935f03787d0145e11c4e8;hpb=e6de1b2b1d8456cdebd75ccc7640bc0a5740a3b0;p=convexer.git diff --git a/cxr/cxr.h b/cxr/cxr.h index bb904c4..0585f1a 100644 --- a/cxr/cxr.h +++ b/cxr/cxr.h @@ -1,11 +1,13 @@ /* - CONVEXER v0.9 + CONVEXER v0.95 A GNU/Linux-first Source1 Hammer replacement built with Blender, for mapmakers Copyright (C) 2022 Harry Godden (hgn) +LICENSE: GPLv3.0, please see COPYING and LICENSE for more information + Features: - Brush decomposition into convex pieces for well defined geometry - Freely form displacements without limits @@ -13,7 +15,8 @@ - Compile models and model groups easily - It runs at an ok speed! - Light patch BSP files; remove unwanted realtime effects - - Fastest VTF compressor (thanks to Richgel999 and stb) + - Bestest VTF compressor (thanks to Richgel999 and stb) + - Pack content automatically Program structure: @@ -45,7 +48,7 @@ IMPLEMENTATION */ -#define CXR_API +#define CXR_API #define CXR_EPSILON 0.001 #define CXR_PLANE_SIMILARITY_MAX 0.998 #define CXR_BIG_NUMBER 1e300 @@ -98,6 +101,7 @@ typedef struct cxr_tri_mesh cxr_tri_mesh; #ifdef CXR_VALVE_MAP_FILE typedef struct cxr_vdf cxr_vdf; typedef struct cxr_texinfo cxr_texinfo; + typedef struct cxr_visgroup cxr_visgroup; typedef struct cxr_vmf_context cxr_vmf_context; #endif /* CXR_VALVE_MAP_FILE */ @@ -146,7 +150,7 @@ struct cxr_static_mesh struct cxr_edge { i32 i0, i1; - i32 freestyle; + i32 freestyle, sharp; } *edges; @@ -170,7 +174,7 @@ struct cxr_static_mesh struct cxr_material { i32 res[2]; - const char *name; + char *name; } *materials; @@ -241,6 +245,11 @@ struct cxr_texinfo double winding; }; +struct cxr_visgroup +{ + const char *name; +}; + /* * Simplified VDF writing interface. No allocations or nodes, just write to file */ @@ -258,10 +267,14 @@ struct cxr_vmf_context *detailvbsp, *detailmaterial; + cxr_visgroup *visgroups; + i32 visgroup_count; + /* Transform settings */ double scale; v3f offset; - i32 lightmap_scale; + i32 lightmap_scale, + visgroupid; /* Current stats */ i32 brush_count, @@ -278,7 +291,9 @@ enum cxr_soliderr k_soliderr_no_solids, k_soliderr_degenerate_implicit, k_soliderr_non_coplanar_vertices, - k_soliderr_non_convex_poly + k_soliderr_non_convex_poly, + k_soliderr_bad_result, + k_soliderr_invalid_input }; /* @@ -290,6 +305,13 @@ enum cxr_soliderr const char *cxr_build_time = __DATE__ " @" __TIME__; #endif +#if _WIN32 || _WIN64 +#if _WIN64 +#else +#warning 32 bit is not supported in blender 3.0 +#endif +#endif + static void (*cxr_log_func)(const char *str); static void (*cxr_line_func)( v3f p0, v3f p1, v4f colour ); @@ -489,7 +511,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_static_loop test_loops[] = {\n" ); + fprintf( fp, "cxr_static_loop test_loops[] = {\n" ); for( int i=0; iloop_count; i ++ ) { fprintf( fp, " {%d, %d},\n", @@ -498,7 +520,7 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_polygon test_polys[] = {\n" ); + fprintf( fp, "cxr_polygon test_polys[] = {\n" ); for( int i=0; i poly_count; i++ ) { fprintf( fp, " {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n", @@ -513,18 +535,19 @@ CXR_API void cxr_write_test_data( cxr_static_mesh *src ) } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_edge test_edges[] = {\n" ); + fprintf( fp, "cxr_edge test_edges[] = {\n" ); for( int i=0; iedge_count; i++ ) { - fprintf( fp, " {%d, %d, %d},\n", + fprintf( fp, " {%d, %d, %d, %d},\n", src->edges[i].i0, src->edges[i].i1, - src->edges[i].freestyle + src->edges[i].freestyle, + src->edges[i].sharp ); } fprintf( fp, "};\n" ); - fprintf( fp, "struct cxr_static_mesh test_mesh = {\n" ); + fprintf( fp, "cxr_static_mesh test_mesh = {\n" ); fprintf( fp, " .vertices = test_verts,\n" ); fprintf( fp, " .loops = test_loops,\n" ); fprintf( fp, " .edges = test_edges,\n" ); @@ -621,10 +644,12 @@ static void cxr_mesh_clean_edges( cxr_mesh *mesh ) { cxr_edge *orig_edge = &mesh->edges[ orig_edge_id ]; edge.freestyle = orig_edge->freestyle; + edge.sharp = orig_edge->sharp; } else { edge.freestyle = 0; + edge.sharp = 0; } cxr_ab_push( &new_edges, &edge ); @@ -711,9 +736,15 @@ static int cxr_mesh_link_loops( cxr_mesh *mesh ) if( *edge == -1 ) { *edge = i; - break; + goto next; } } + + /* Overflowed edge mapping... Duplicated faces. */ + free( polygon_edge_map ); + return 0; + + next:; } } for( int i = 0; i < mesh->abpolys.count; i ++ ) @@ -1001,7 +1032,7 @@ static int *cxr_mesh_reflex_edges( cxr_mesh *mesh ) edge_tagged[lp->edge_index] = 0; cxr_polygon *polya = &mesh->polys[ lp->poly_left ], - *polyb = &mesh->polys[ lp->poly_right ]; + *polyb = &mesh->polys[ lp->poly_right ]; v4f planeb; normal_to_plane(polyb->normal, polyb->center, planeb); @@ -1320,16 +1351,66 @@ static void cxr_link_manifold( struct temp_manifold *manifold ){ cxr_loop **edge_list = malloc( sizeof(*edge_list) * solid->edge_count ); + int *temp_solid = malloc( solid->count *sizeof(int) ); + int temp_solid_len = 0; int init_reverse = 0; int unique_edge_count = 0; + + /* Try remove splitting faces first */ + { + int split_total = 0; + for( int j=0; jcount; j++ ) + { + cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ]; + int interior_count = 0; + + for( int k=0; kloop_total; k++ ) + { + cxr_loop *loop = &mesh->loops[ poly->loop_start+k ]; - /* Gather list of unique edges */ + for( int l=0; lcount; l++ ) + if( loop->poly_right == solid_buffer[solid->start+l] ) + { + interior_count ++; + goto next; + } + + next:; + } + + if( interior_count < poly->loop_total-1 ) + { + split_total ++; + continue; + } + + temp_solid[ temp_solid_len ++ ] = solid_buffer[solid->start+j]; + } + + if( temp_solid_len < 3 || (split_total & 0x2) /* unkown reasons */ ) + { + } + else + { + /* Overwrite original solid */ + for( int j=0; jstart+j ] = temp_solid[ j ]; + + solid->count = temp_solid_len; + } + + free( temp_solid ); + } for( int j=0; jcount; j++ ) { cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ]; + /* when discarding, if a face has only one loop that points outwards, + * we keep it */ + + for( int k=0; kloop_total; k++ ) { cxr_loop *loop = &mesh->loops[ poly->loop_start+k ]; @@ -1537,22 +1618,30 @@ static int cxr_build_implicit_geo( cxr_mesh *mesh, int new_polys, int start ) return 1; } -/* - * Convexer's main algorithm - * - * Return the best availible convex solid from mesh, and patch the existing mesh - * to fill the gap where the new mesh left it. - * - * Returns NULL if shape is already convex or empty. - * This function will not preserve edge data such as freestyle, sharp etc. - */ -static cxr_mesh *cxr_pull_best_solid( - cxr_mesh *mesh, - int preserve_more_edges, - enum cxr_soliderr *err ) +static int cxr_reflex_err( cxr_mesh *mesh ) { - *err = k_soliderr_none; + int error = 0; + int *reflex_check = cxr_mesh_reflex_edges( mesh ); + + v3f *temp = cxr_ab_ptr(mesh->p_abverts, 0); + + for( int i=0; iabedges.count; i++ ) + { + if( reflex_check[i] ) + { + cxr_debug_line( temp[mesh->edges[i].i0], + temp[mesh->edges[i].i1], + colour_error ); + error ++; + } + } + + free( reflex_check ); + return error; +} +static int cxr_non_manifold_err( cxr_mesh *mesh ) +{ if( !cxr_mesh_link_loops(mesh) ) { #ifdef CXR_DEBUG @@ -1564,18 +1653,43 @@ static cxr_mesh *cxr_pull_best_solid( for( int i=0; iabloops.count; i++ ) { cxr_loop *lp = &mesh->loops[i]; + cxr_edge *edge = &mesh->edges[lp->edge_index]; + cxr_debug_line( verts[edge->i0], verts[edge->i1], colours_random[1] ); if( lp->poly_left == -1 || lp->poly_right == -1 ) { - cxr_edge *edge = &mesh->edges[lp->edge_index]; cxr_debug_line( verts[edge->i0], verts[edge->i1], colour_error ); } } #endif + return 1; + } + + return 0; +} + +/* + * Convexer's main algorithm + * + * Return the best availible convex solid from mesh, and patch the existing mesh + * to fill the gap where the new mesh left it. + * + * Returns NULL if shape is already convex or empty. + * This function will not preserve edge data such as freestyle, sharp etc. + */ +static cxr_mesh *cxr_pull_best_solid( + cxr_mesh *mesh, + int preserve_more_edges, + enum cxr_soliderr *err ) +{ + *err = k_soliderr_none; + + if( cxr_non_manifold_err( mesh ) ) + { *err = k_soliderr_non_manifold; return NULL; } - + int *edge_tagged = cxr_mesh_reflex_edges( mesh ); int *vertex_tagged = cxr_mesh_reflex_vertices( mesh ); @@ -1689,7 +1803,7 @@ static cxr_mesh *cxr_pull_best_solid( struct temp_manifold manifold; cxr_link_manifold( mesh, solid, solid_buffer, &manifold); - + if( manifold.status == k_manifold_err ) { *err = k_soliderr_bad_manifold; @@ -1917,7 +2031,17 @@ static cxr_mesh *cxr_pull_best_solid( free(solid_buffer); free(candidates); free(best_manifold.loops); - + + /* + * Do final checks on the mesh to make sure we diddn't introduce any + * errors + */ + if( cxr_non_manifold_err( pullmesh ) || cxr_reflex_err( pullmesh ) ) + { + *err = k_soliderr_bad_result; + return NULL; + } + return pullmesh; } @@ -1925,6 +2049,9 @@ static cxr_mesh *cxr_pull_best_solid( free(candidates); free(best_manifold.loops); + if( cxr_non_manifold_err( mesh ) || cxr_reflex_err( mesh ) ) + *err = k_soliderr_bad_result; + return NULL; } @@ -2050,7 +2177,14 @@ CXR_API void cxr_free_world( cxr_world *world ) cxr_ab_free( &world->abverts ); cxr_ab_free( &world->absolids ); - free( world->materials ); + + if( world->materials ) + { + for( int i=0; imaterial_count; i++ ) + free( world->materials[i].name ); + + free( world->materials ); + } free( world ); } @@ -2164,6 +2298,16 @@ CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh ) CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode ) { + /* Make sure data is in the mesh and isn't empty */ + if( !src->edge_count || !src->loop_count || !src->poly_count ) + { + cxr_log( "Error %d\n", k_soliderr_invalid_input ); + if( perrcode ) + *perrcode = k_soliderr_invalid_input; + + return NULL; + } + u32 error = 0x00; cxr_world *world = malloc( sizeof(*world) ); @@ -2176,6 +2320,13 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode ) size_t dsize = sizeof(cxr_material) * src->material_count; world->materials = malloc( dsize ); memcpy( world->materials, src->materials, dsize ); + + for( int i=0; imaterial_count; i++ ) + { + world->materials[i].name = malloc(strlen(src->materials[i].name) +1); + strcpy( world->materials[i].name, src->materials[i].name ); + } + world->material_count = src->material_count; } else world->materials = NULL; @@ -2726,7 +2877,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, * TODO(harry): Error checking is needed here for bad input data */ - int dispedge[16]; + int dispedge[17]; v2f corner_uvs[4]; int dispedge_count; int disp_count = 0; @@ -2828,6 +2979,8 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, if( !newvert ) { + free( graph ); + free( vertinfo ); return 0; } } @@ -2890,9 +3043,6 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, } } -#ifdef CXR_DEBUG - cxr_log( "Broken displacement!\n" ); -#endif free( graph ); free( vertinfo ); return 0; @@ -2944,6 +3094,8 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, v3_muladds( face_center, refn, 1.5, pn ); v3_muladds( face_center, refv, 1.5, pv ); v3_muladds( face_center, refu, 1.5, pu ); + + v3_muladds( face_center, refn, 2.0, face_center ); } /* Create world coordinates */ @@ -3011,7 +3163,7 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, tx = (double)k/(double)(5-1); v3_lerp( lside0, lside1, tx, lref ); v3_muls( verts[grid[index]], ctx->scale, vworld ); - v3_add( ctx->offset, vworld, ctx->offset ); + v3_add( ctx->offset, vworld, vworld ); v3_sub( vworld, lref, vdelta ); v3_copy( vdelta, normals[index] ); @@ -3031,7 +3183,6 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, world_corners[side[0]] ); cxr_vdf_kv( output, "material", matptr->name ); - cxr_vdf_kaxis( output, "uaxis", texinfo_shared.uaxis, texinfo_shared.offset[0], @@ -3108,7 +3259,8 @@ static int cxr_write_disp( cxr_mesh *mesh, cxr_world *world, cxr_vdf_node( output, "editor"); cxr_vdf_colour255( output, "color", colours_random[cxr_range(ctx->brush_count,8)]); - + + cxr_vdf_ki32( output, "visgroupid", ctx->visgroupid ); cxr_vdf_ki32( output, "visgroupshown",1); cxr_vdf_ki32( output, "visgroupautoshown",1); cxr_vdf_edon( output ); @@ -3138,6 +3290,15 @@ CXR_API void cxr_begin_vmf( cxr_vmf_context *ctx, cxr_vdf *output ) cxr_vdf_edon( output ); cxr_vdf_node( output, "visgroups" ); + + for( int i=0; ivisgroup_count; i++ ) + { + cxr_vdf_node( output, "visgroup" ); + cxr_vdf_kv( output, "name", ctx->visgroups[i].name ); + cxr_vdf_ki32( output, "visgroupid", i+1 ); + cxr_vdf_edon( output ); + } + cxr_vdf_edon( output ); cxr_vdf_node( output, "viewsettings" ); @@ -3183,7 +3344,10 @@ CXR_API void cxr_push_world_vmf( cxr_world *world, cxr_vmf_context *ctx, if( solid->displacement ) { - cxr_write_disp( solid->pmesh, world, ctx, output ); + if( !cxr_write_disp( solid->pmesh, world, ctx, output ) ) + { + cxr_log( "Warning: Invalid displacement\n" ); + } continue; } @@ -3242,6 +3406,7 @@ CXR_API void cxr_push_world_vmf( cxr_world *world, cxr_vmf_context *ctx, cxr_vdf_colour255( output, "color", colours_random[cxr_range(ctx->brush_count,8)]); + cxr_vdf_ki32( output, "visgroupid", ctx->visgroupid ); cxr_vdf_ki32( output, "visgroupshown", 1 ); cxr_vdf_ki32( output, "visgroupautoshown", 1 ); cxr_vdf_edon( output );