stb/ C Sean Barrets image I/O
*/
+const char *cxr_build_time = __DATE__ " @" __TIME__;
+
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-const char *cxr_build_time = __DATE__ " @" __TIME__;
-
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
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
{
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; i<count; i++ )
+ {
+ if( i == count-1 ) fprintf( vdf->fp, "%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; i<count; i++ )
+ {
+ if( i == count-1 ) fprintf( vdf->fp, "%f %f %f", vecs[i][0], vecs[i][1], vecs[i][2] );
+ else fprintf( vdf->fp, "%f %f %f ", vecs[i][0], vecs[i][1], vecs[i][2] );
+ }
+ 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",
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
// 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; i<mesh->polys.count; i++ )
{
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; i<mesh->polys.count; i++ )
{
struct cxr_polygon *poly = cxr_ab_ptr( &mesh->polys, i );
+
+ for( int j=0; j<poly->loop_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; j<poly->loop_total-2; j++ )
{
}
}
+ 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;
v2f corner_uvs[4];
int dispedge_count;
int disp_count = 0;
+ struct cxr_texinfo texinfo_shared;
for( int i=0; i<mesh->polys.count; i++ )
{
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;
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
// --------------------
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;
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
//
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];
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;
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 ++;
}
}
}
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