From: hgn Date: Thu, 7 Apr 2022 20:56:06 +0000 (+0100) Subject: Well defined displacements BatChest X-Git-Url: https://skaterift.com/git/?p=convexer.git;a=commitdiff_plain;h=48fae7bf7445a7913f84cdfba5d8633d554b6c9b Well defined displacements BatChest --- diff --git a/src/convexer.c b/src/convexer.c index ed04caf..185917a 100644 --- a/src/convexer.c +++ b/src/convexer.c @@ -31,6 +31,8 @@ stb/ C Sean Barrets image I/O */ +const char *cxr_build_time = __DATE__ " @" __TIME__; + #include #include #include @@ -38,8 +40,6 @@ #include #include -const char *cxr_build_time = __DATE__ " @" __TIME__; - typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; @@ -59,6 +59,7 @@ typedef v3f m4x3f[4]; typedef v3f boxf[2]; #define CXR_EPSILON 0.001 +#define CXR_BIG_NUMBER 1e300 #define CXR_INTERIOR_ANGLE_MAX 0.998 #define CXR_API #define CXR_DIRTY_OPTIMISATION 1 @@ -375,6 +376,32 @@ static void cxr_vdf_kaxis(struct cxr_vdf *vdf, const char *strk, v3f normal, dou { 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(struct 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(struct 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, "%f", doubles[i] ); + else fprintf( vdf->fp, "%f ", doubles[i] ); + } + fprintf( vdf->fp, "\"\n" ); +} +static void cxr_vdf_karrv3f(struct 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] ); + } + fprintf( vdf->fp, "\"\n" ); +} static void cxr_vdf_plane(struct 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", @@ -1781,9 +1808,32 @@ CXR_API struct cxr_input_mesh *cxr_decompose(struct cxr_input_mesh *src) return NULL; } +static int cxr_cardinal( v3f a, int ignore ) +{ + int component = 0; + double component_max = -CXR_BIG_NUMBER; + + for( int i=0; i<3; i++ ) + { + if( i == ignore ) continue; + + if( fabs(a[i]) > component_max ) + { + component_max = fabs(a[i]); + component = i; + } + } + double d = a[component] >= 0.0? 1.0: -1.0; + v3_zero( a ); + a[component] = d; + + return component; +} + // Convert contiguous mesh to patch of displacments // -static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, +static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_input_mesh *inputmesh, + struct cxr_vdf *output, struct cxr_auto_buffer *abverts) { // Create a graph which maps vertices by their connections @@ -1833,7 +1883,8 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, // These are clamped to be cardinal directions as to make the VMF somewhat // human editable. - v3f avg_normal; + v3f avg_normal, refv, refu, refn; + v3_zero(refv); v3_zero(refu); v4_zero(refn); for( int i=0; ipolys.count; i++ ) { @@ -1844,30 +1895,34 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, v3_normalize( avg_normal ); // TODO: 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... - - double component_max = fabs( avg_normal[2] ); - int component = 2; + int n_cardinal = cxr_cardinal( avg_normal, -1 ); - for( int i=0; i<2; i++ ) - { - if( fabs(avg_normal[i]) > component_max ) - { - component_max = fabs(avg_normal[i]); - component = i; - } - } - double d = avg_normal[component] >= 0.0? 1.0: -1.0; - v3_zero( avg_normal ); - avg_normal[component] = d; - // 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; + v2f uv_center; + v3f face_center; + + v2_fill( uvboundmin, CXR_BIG_NUMBER ); + v2_fill( uvboundmax, -CXR_BIG_NUMBER ); + v3_fill( faceboundmin, CXR_BIG_NUMBER ); + v3_fill( faceboundmax, -CXR_BIG_NUMBER ); + for( int i=0; ipolys.count; i++ ) { struct cxr_polygon *poly = cxr_ab_ptr( &mesh->polys, i ); + + for( int j=0; jloop_total; j++ ) + { + struct cxr_loop *lp0 = cxr_ab_ptr(&mesh->loops, poly->loop_start+j); + v2_minv( lp0->uv, uvboundmin, uvboundmin); + v2_maxv( lp0->uv, uvboundmax, uvboundmax); + v3_minv( cxr_ab_ptr(abverts,lp0->index), faceboundmin, faceboundmin ); + v3_maxv( cxr_ab_ptr(abverts,lp0->index), faceboundmax, faceboundmax ); + } for( int j=0; jloop_total-2; j++ ) { @@ -1889,6 +1944,11 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, } } + v3_add( faceboundmax, faceboundmin, face_center ); + v3_muls( face_center, 0.5, face_center ); + v2_add( uvboundmin, uvboundmax, uv_center ); + v2_muls( uv_center, 0.5, uv_center ); + sf = sqrt( face_area / uv_area ); int corner_count = 0; @@ -1923,6 +1983,7 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, v2f corner_uvs[4]; int dispedge_count; int disp_count = 0; + struct cxr_texinfo texinfo_shared; for( int i=0; ipolys.count; i++ ) { @@ -1940,6 +2001,11 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, if( info->corner ) { int corner_count = 1; + + struct 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; @@ -1950,7 +2016,6 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, basepoly->loop_total = -1; cxr_debug_box( cxr_ab_ptr(abverts,l0->index),0.08,(v4f){0.0,0.0,1.0,1.0}); - disp_count ++; // Collect edges // -------------------- @@ -2009,12 +2074,14 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, if( !coninfo->boundary ) 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; @@ -2036,12 +2103,7 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, v2f va, vb; v2_sub( corner_uvs[1], corner_uvs[0], va ); v2_sub( corner_uvs[2], corner_uvs[0], vb ); - - if( v2_cross( va,vb ) < 0.0 ) - cxr_log( "Uv is flipped!\n" ); - else - cxr_log( "Uv is normal\n" ); - + // Connect up the grid // // 0 1 2 3 4 @@ -2056,21 +2118,15 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, // int grid[25]; - for( int j=0; j<25; j++ ) grid[j] = 0; - for( int j=0; j<5; j++ ) - { - grid[j] = dispedge[j]; - vertinfo[dispedge[j]].used = 1; - } - for( int j=1; j<5; j++ ) - { - grid[j*5] = dispedge[16-j]; - vertinfo[dispedge[16-j]].used = 1; - } + 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]; - for( int j=1; j<5; j++ ) + // Grid fill + for( int j=1; j<4; j++ ) { - for( int k=1; k<5; k++ ) + for( int k=1; k<4; k++ ) { int s0 = grid[(j-1)*5+k], s1 = grid[j*5+k-1]; @@ -2087,11 +2143,11 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, int cona = graph[va->con_start+l], conb = graph[vb->con_start+m]; - if( vertinfo[cona].used || vertinfo[conb].used ) - continue; - if( cona == conb ) { + if( vertinfo[cona].used || vertinfo[cona].boundary ) + continue; + grid[ j*5+k ] = cona; vertinfo[cona].used = 1; @@ -2109,10 +2165,219 @@ static void cxr_write_disp(struct cxr_mesh *mesh, struct cxr_vdf *output, IL_MATCHED_DISP_INTERIOR_VERT:; } } + + for( int j=0; j<5; j++ ) + { + cxr_log( "%d %d %d %d %d\n", grid[j*5+0],grid[j*5+1],grid[j*5+2],grid[j*5+3],grid[j*5+4]); + } + + // Create brush vertices based on UV map - // Release grid - for( int j=0; j<25; j++ ) - vertinfo[grid[j]].used = 0; + // Create V reference based on first displacement. + // TODO: This is not the most stable 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 ) + { + struct cxr_texinfo tx; + v3f tri_ref[3]; + v3_copy( cxr_ab_ptr(abverts,dispedge[0]), tri_ref[0] ); + v3_copy( cxr_ab_ptr(abverts,dispedge[4]), tri_ref[1] ); + v3_copy( cxr_ab_ptr(abverts,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, n_cardinal ); + v3_copy( avg_normal, refn ); + int u_cardinal = 0; + if( u_cardinal == n_cardinal || u_cardinal == v_cardinal ) u_cardinal ++; + if( u_cardinal == n_cardinal || u_cardinal == v_cardinal ) u_cardinal ++; + + v3_zero(refu); + refu[u_cardinal] = tx.uaxis[u_cardinal] > 0.0? 1.0: -1.0; + + v3f p0, pv, pu, pn; + + 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 ); + + 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} ); + } + + // Create world cordinates + v3f world_corners[8]; + v2f world_uv[4]; + + 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 *colour = colours_random[cxr_range(disp_count,8)]; + 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 ); + + for( int j=0; j<4; j++ ) + v3_muladds( world_corners[j], refn, -1.0, world_corners[j+4] ); + + // Apply world transform + for( int j=0; j<8; j++ ) + { + v3_muls( world_corners[j], cxr_context.scale_factor, world_corners[j] ); + world_corners[j][2] += cxr_context.offset_z; + } + + if( disp_count == 0 ) + { + 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); + + v3_lerp( world_corners[0], world_corners[3], ty, lside0 ); + v3_lerp( world_corners[1], world_corners[2], ty, lside1 ); + + 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( cxr_ab_ptr(abverts, 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] ); + } + } + + 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]] ); + + 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 ); + + 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_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 ++; } } } @@ -2293,7 +2558,7 @@ CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf * IL_SOLID_IS_DISPLACEMENT:; pinf->is_displacement = 1; - cxr_write_disp( pinf->pmesh, output, &abverts ); + cxr_write_disp( pinf->pmesh, src, output, &abverts ); } // Preprocessor 3: Breakup non-convex shapes into sub-solids diff --git a/src/cxr_math.h b/src/cxr_math.h index 5f1bfc2..d03d886 100644 --- a/src/cxr_math.h +++ b/src/cxr_math.h @@ -38,6 +38,11 @@ CXR_INLINE void v2_zero( v2f a ) a[0] = 0.0; a[1] = 0.0; } +CXR_INLINE void v2_fill( v2f a, double v ) +{ + a[0] = v; a[1] = v; +} + CXR_INLINE void v2_copy( v2f a, v2f b ) { b[0] = a[0]; b[1] = a[1];