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_PLANE_SIMILARITY_MAX 0.998
+#define CXR_BIG_NUMBER 1e300
#define CXR_INTERIOR_ANGLE_MAX 0.998
#define CXR_API
#define CXR_DIRTY_OPTIMISATION 1
struct cxr_edge
{
i32 i0, i1;
+ i32 freestyle;
}
*edges;
{
v3f uaxis, vaxis;
v2f offset, scale;
+ double winding;
};
// simple VDF writing interface
{
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",
struct cxr_edge edge = { i0, i1 };
// --- ! ---
// Copy extra information (sharp,freestyle.. etc) here!
- //
- // if orig_edge_id < mesh->edges.count: edge.foo = mesh->edges[orig].foo
+
+ if( orig_edge_id < mesh->edges.count )
+ {
+ struct cxr_edge *orig_edge = cxr_ab_ptr( &mesh->edges, orig_edge_id );
+ edge.freestyle = orig_edge->freestyle;
+ }
+ else
+ {
+ edge.freestyle = 0;
+ }
+
// --- ! ---
cxr_ab_push( &new_edges, &edge );
{
struct cxr_loop *loop = cxr_ab_ptr(&mesh->loops, polya->loop_start+j);
if( plane_polarity( planeb, vertices[loop->index] ) > 0.001 ||
- v3_dot(polya->normal,polyb->normal) > 0.98500 )
+ v3_dot(polya->normal,polyb->normal) > CXR_PLANE_SIMILARITY_MAX )
{
edge_tagged[i] = 1;
break;
struct cxr_polygon *polyj = cxr_ab_ptr(&mesh->polys,connected_planes[j]);
struct cxr_polygon *polyk = cxr_ab_ptr(&mesh->polys,connected_planes[k]);
- if( v3_dot(polyj->normal, polyk->normal) > 0.98500 )
+ if( v3_dot(polyj->normal, polyk->normal) > CXR_PLANE_SIMILARITY_MAX )
goto IL_TAG_VERT;
}
}
edge_tagged[i] = 1;
}
- /* Debug stuff --
- for( int i=0; i<vertex_count; i++ )
+
+ for( int i=0; i<vert_buffer->count; i++ )
if( vertex_tagged[i] )
cxr_debug_box( vertices[i], 0.03, (v4f){0.0,0.0,0.0,1.0});
if( hot_edge[i] )
cxr_debug_line( vertices[ edge->i0 ], vertices[ edge->i1 ], (v4f){0.0,1.0,1.0,1.0});
}
- */
+
// count regions
int *faces_tagged = malloc(mesh->polys.count*sizeof(int));
}
*candidates = malloc( mesh->polys.count *sizeof(struct csolid) );
int candidate_count = 0;
+
+ struct tedge
+ {
+ int i0, i1;
+ }
+ *edge_references = malloc( mesh->edges.count *sizeof(struct tedge) );
for( int i=0; i<mesh->polys.count; i++ )
{
{
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
//
- // TODO: is this unused due to hotedge improvements? leaving for safety...
+ // This can sort out SOME invalid configs, but not all.
+ // It would be nice to find a more robust clustering algorithm for this.
+ //
struct cxr_polygon *poly_to_add = cxr_ab_ptr(&mesh->polys, loop->poly_right );
for( int l=0; l < poly_to_add->loop_total; l++ )
for( int m=0; m<solid_len; m++ )
{
struct cxr_polygon *polym = cxr_ab_ptr(&mesh->polys,solid[m]);
- if( v3_dot( polym->normal, future_face->normal ) > 0.98500 )
+ if( v3_dot( polym->normal,future_face->normal) > CXR_PLANE_SIMILARITY_MAX)
goto IL_SKIP_PLANE_ADD;
}
IL_SKIP_SIMILAR_PLANES:;
}
- // This plane passed all checks so we can add it to the current solid
-
solid[ solid_len ++ ] = loop->poly_right;
faces_tagged[ loop->poly_right ] = i;
changed = 1;
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
+ for( int j=0; j<solid_len-1; j++ )
+ {
+ for( int k=j+1; k<solid_len; k++ )
+ {
+ struct cxr_polygon *polyj = cxr_ab_ptr(&mesh->polys, solid[j]),
+ *polyk = cxr_ab_ptr(&mesh->polys, solid[k]);
+
+ if( v3_dot( polyj->normal, polyk->normal ) > CXR_PLANE_SIMILARITY_MAX )
+ {
+ for( int l=0; l<solid_len; l++ )
+ faces_tagged[ solid[l] ] = -1;
+
+ goto IL_CANCEL_SOLID;
+ }
+ }
+ }
+
// Add entry
struct csolid *csolid = &candidates[candidate_count ++];
csolid->start = solid_buffer_len;
v3_divs( csolid->center, solid_len, csolid->center );
solid_buffer_len += solid_len;
+
+ IL_CANCEL_SOLID:;
}
+ 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
exist_face->loop_total = -1;
}
-
- // Split manifold up by unique planes if it has more than 1
- // otherwise, just use that face
- //
- // TODO: Need to build new manifold in sections, stably
- // currently there is an unsupported case where the manifold splits
- // are on located on an implicit face, causing 1-length manifolds.
int new_polys = 0;
int pullmesh_new_start = pullmesh->polys.count;
// 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: Find a well defined rule here.
+ // TODO(harry): Find a well defined rule here.
int collapse_used_segments = (u32)fewest_manifold_splits & 0x1? 0: 1;
v2_zero( transform->offset );
v2_div( (v2f){128.0, 128.0}, texture_res, transform->scale );
+ transform->winding = 1.0;
return;
}
// Detect if UV is reversed
double winding = v2_cross( tT, bT ) >= 0.0f? 1.0f: -1.0f;
-
+
// UV projection reference
v2f vY, vX;
v2_muls((v2f){1,0}, winding, vX);
v3_copy( vaxis, transform->vaxis );
v2_copy( tex_offset, transform->offset );
v2_copy( uv_scale, transform->scale );
+ transform->winding = winding;
}
CXR_API struct cxr_input_mesh *cxr_decompose(struct cxr_input_mesh *src)
struct cxr_auto_buffer solids;
cxr_ab_init( &solids, sizeof(struct cxr_mesh *), 2 );
- // TODO: Preprocessor stages
- // - Split mesh up into islands before doing anything here
- // - Snap vertices to grid (0.25u) ?
while(1)
{
struct cxr_mesh *res = cxr_pull_best_solid( main_mesh, &abverts, 0, &error );
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_input_mesh *inputmesh,
+ struct cxr_vdf *output,
+ struct cxr_auto_buffer *abverts)
+{
+ // Create a graph which maps vertices by their connections
+ struct vertinfo
+ {
+ int con_start, con_count; // Index into the connection graph
+ int boundary,
+ used,
+ search,
+ corner;
+
+ double alpha;
+ }
+ *vertinfo = malloc( sizeof(struct vertinfo)*abverts->count );
+ int *graph = malloc( sizeof(int) * mesh->edges.count*2 );
+
+ int con_pos = 0;
+ for( int i=0; i<abverts->count; i++ )
+ {
+ struct vertinfo *info = &vertinfo[i];
+ info->con_start = con_pos;
+ info->con_count = 0;
+ info->boundary = 0;
+ info->corner = 0;
+ info->used = 0;
+ info->search = 0;
+ info->alpha = 0.0;
+
+ for( int j=0; j<mesh->edges.count; j++ )
+ {
+ struct cxr_edge *edge = cxr_ab_ptr(&mesh->edges,j);
+
+ if( edge->i0 == i || edge->i1 == i )
+ {
+ graph[ con_pos ++ ] = edge->i0 == i? edge->i1: edge->i0;
+ info->con_count ++;
+
+ if( edge->freestyle )
+ info->boundary = 1;
+ }
+ }
+ }
+
+ // 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.
+
+ v3f avg_normal, refv, refu, refn;
+ v3_zero(refv); v3_zero(refu); v3_zero(refn);
+
+ for( int i=0; i<mesh->polys.count; i++ )
+ {
+ struct cxr_polygon *poly = cxr_ab_ptr( &mesh->polys, i );
+ v3_add( poly->normal, avg_normal, avg_normal );
+ }
+ v3_divs( avg_normal, mesh->polys.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 );
+
+ // 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++ )
+ {
+ struct cxr_loop *lp0 = cxr_ab_ptr(&mesh->loops, poly->loop_start),
+ *lp1 = cxr_ab_ptr(&mesh->loops, poly->loop_start+j+1),
+ *lp2 = cxr_ab_ptr(&mesh->loops, poly->loop_start+j+2);
+ v3f va, vb, orth;
+ v3_sub( cxr_ab_ptr(abverts,lp1->index), cxr_ab_ptr(abverts,lp0->index), va );
+ v3_sub( cxr_ab_ptr(abverts,lp2->index), cxr_ab_ptr(abverts,lp0->index), vb );
+ v3_cross( va, vb, orth );
+
+ face_area += v3_length( orth ) / 2.0;
+
+ v2f uva, uvb;
+ v2_sub( lp1->uv, lp0->uv, uva );
+ v2_sub( lp2->uv, lp0->uv, uvb );
+
+ uv_area += fabs(v2_cross( uva, uvb )) / 2.0;
+ }
+ }
+
+ 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;
+
+ // Vertex classification
+ for( int i=0; i<abverts->count; i++ )
+ {
+ struct vertinfo *info = &vertinfo[i];
+ if( !info->boundary ) continue;
+
+ int count = 0,
+ non_manifold = 1;
+
+ for( int j=0; j<info->con_count; j++ )
+ {
+ int con = graph[info->con_start+j];
+
+ if( vertinfo[con].boundary )
+ count ++;
+ 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.
+ //
+ int dispedge[16];
+ v2f corner_uvs[4];
+ int dispedge_count;
+ int disp_count = 0;
+
+ for( int i=0; i<mesh->polys.count; i++ )
+ {
+ struct cxr_polygon *basepoly = cxr_ab_ptr(&mesh->polys,i);
+
+ for( int h=0; h<basepoly->loop_total; h ++ )
+ {
+ int i0 = h,
+ i1 = cxr_range(h+1,basepoly->loop_total);
+
+ struct cxr_loop *l0 = cxr_ab_ptr(&mesh->loops, basepoly->loop_start+i0),
+ *l1 = cxr_ab_ptr(&mesh->loops, basepoly->loop_start+i1);
+ struct vertinfo *info = &vertinfo[ l0->index ];
+
+ 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;
+ 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
+ // --------------------
+
+ while( dispedge_count < 17 )
+ {
+ struct vertinfo *edge_head = &vertinfo[dispedge[dispedge_count-1]];
+ int newvert = 0;
+
+ if( edge_head->corner )
+ {
+ // Find a polygon that has the edge C-1 -> C
+ for( int j=0; j<mesh->polys.count && !newvert; j++ )
+ {
+ struct cxr_polygon *poly = cxr_ab_ptr(&mesh->polys,j);
+
+ for( int k=0; k<poly->loop_total; k ++ )
+ {
+ int i0 = k,
+ i1 = cxr_range(k+1,poly->loop_total);
+
+ struct cxr_loop *l0 = cxr_ab_ptr(&mesh->loops, poly->loop_start+i0),
+ *l1 = cxr_ab_ptr(&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);
+ struct cxr_loop *l2 = cxr_ab_ptr(&mesh->loops, poly->loop_start+i2);
+
+ dispedge[dispedge_count ++] = l2->index;
+ newvert = 1;
+ poly->loop_total = -1;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for( int j=0; j<edge_head->con_count; j++ )
+ {
+ int con = graph[edge_head->con_start+j];
+
+ if( con == -1 )
+ continue;
+
+ if( dispedge_count > 1 )
+ if( con == dispedge[dispedge_count-2] )
+ continue;
+
+ struct vertinfo *coninfo = &vertinfo[con];
+
+ 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;
+
+ break;
+ }
+ }
+
+ if( !newvert )
+ {
+ cxr_debug_box(cxr_ab_ptr(abverts,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++ )
+ {
+ for( int k=1; k<4; k++ )
+ {
+ int s0 = grid[(j-1)*5+k],
+ s1 = grid[j*5+k-1];
+
+ struct vertinfo *va = &vertinfo[s0],
+ *vb = &vertinfo[s1];
+
+ // Find a common vertex between s0 and s1
+
+ for( int l=0; l<va->con_count; l ++ )
+ {
+ for( int m=0; m<vb->con_count; 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;
+
+ grid[ j*5+k ] = cona;
+ vertinfo[cona].used = 1;
+
+ goto IL_MATCHED_DISP_INTERIOR_VERT;
+ }
+ }
+ }
+
+ // Broken displacement
+ cxr_log( "Broken displacement!\n" );
+ free( graph );
+ free( vertinfo );
+ return;
+
+ IL_MATCHED_DISP_INTERIOR_VERT:;
+ }
+ }
+
+ // 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 )
+ {
+ 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, -1 );
+
+ v3_cross( tx.vaxis, tx.uaxis, refn );
+ v3_muls( refn, -tx.winding, refn );
+
+ 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 ++;
+
+ 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 );
+
+ 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} );
+ }
+ }
+
+ // 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)];
+
+ 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_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 );
+ */
+
+ // 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;
+ }
+
+ struct 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);
+
+ 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 ++;
+ }
+ }
+ }
+
+ // Main loop
+#if 0
+ int pool[25];
+ for( int i=0; i<abverts->count; i++ )
+ {
+ struct vertinfo *info = &vertinfo[i];
+ if( info->boundary || info->used )
+ continue;
+
+ // Gather all vertices in this displacement
+ int poolcount = 1,
+ front_start = 0,
+ front_count = 1;
+ pool[0] = i;
+ info->used = 1;
+
+ IL_GATHER_LOOP:;
+
+ int new_front_start = poolcount;
+
+ for( int j=0; j<front_count; j++ )
+ {
+ struct vertinfo *frontvert = &vertinfo[pool[front_start+j]];
+
+ for( int k=0; k<frontvert->con_count; k++ )
+ {
+ int conid = graph[frontvert->con_start+k];
+ struct vertinfo *con = &vertinfo[conid];
+
+ if( frontvert->boundary && !con->boundary )
+ continue;
+
+ if( con->used )
+ continue;
+
+ if( poolcount == 25 )
+ goto IL_DISP_ERROR_COUNT;
+
+ con->used = 1;
+ pool[ poolcount ++ ] = conid;
+ }
+ }
+
+ if( poolcount > new_front_start )
+ {
+ front_start = new_front_start;
+ front_count = poolcount-front_start;
+
+ goto IL_GATHER_LOOP;
+ }
+
+ if( poolcount != 25 )
+ {
+IL_DISP_ERROR_COUNT:
+ for( int i=0; i<poolcount; i++ )
+ cxr_debug_box( cxr_ab_ptr(abverts,pool[i]), 0.02, colour_error );
+
+ free(graph);
+ free(vertinfo);
+
+ cxr_log("Invalid displacement (>25 verts)\n");
+ return;
+ }
+
+ int corners[4];
+ int corner_count = 0;
+ struct cxr_loop *cornerloops[4];
+
+ // Find corners, and get their loops (for uvs)
+ // note: the mesh must be split where there is texture seams
+ // so that two different uv'd loops cant ref the same vertex
+ //
+ for( int j=0; j<poolcount; j++ )
+ {
+ if( vertinfo[pool[j]].corner )
+ {
+ if( corner_count == 4 )
+ {
+ corner_count = -1;
+ break;
+ }
+
+ corners[corner_count] = j;
+
+ // find loop that includes this vert
+ for( int k=0; k<mesh->loops.count; k++ )
+ {
+ struct cxr_loop *lp = cxr_ab_ptr(&mesh->loops,k);
+ if( lp->index == pool[j] )
+ {
+ cornerloops[corner_count] = lp;
+ break;
+ }
+ }
+
+ corner_count ++;
+ }
+ }
+
+ if( corner_count !=4 )
+ {
+ free(graph);
+ free(vertinfo);
+ cxr_log( "Invalid displacement (!=4 corners)\n" );
+ return;
+ }
+
+ int pivot = corners[0];
+ }
+#endif
+
+ free( graph );
+ free( vertinfo );
+}
+
+static int cxr_solid_checkerr(struct cxr_mesh *mesh, struct cxr_auto_buffer *abverts )
+{
+ int err_count = 0;
+
+ for( int i=0; i<mesh->polys.count; i++ )
+ {
+ int plane_err = 0;
+
+ struct cxr_polygon *poly = cxr_ab_ptr(&mesh->polys,i);
+ v4f plane;
+
+ normal_to_plane( poly->normal, poly->center, plane );
+
+ for( int j=0; j<poly->loop_total; j++ )
+ {
+ struct cxr_loop *loop = cxr_ab_ptr(&mesh->loops, poly->loop_start+j);
+ double *vert = cxr_ab_ptr(abverts,loop->index);
+
+ if( fabs(plane_polarity(plane,vert)) > 0.0025 )
+ {
+ err_count ++;
+ plane_err ++;
+
+ v3f ref;
+ plane_project_point( plane, vert, ref );
+
+ cxr_debug_line( ref, vert, colour_error );
+ cxr_debug_box( vert, 0.1, colour_error );
+ }
+ }
+
+ if( plane_err )
+ cxr_debug_poly( mesh, poly, cxr_ab_ptr(abverts,0), colour_error );
+ }
+
+ return err_count;
+}
+
CXR_API i32 cxr_convert_mesh_to_vmf(struct cxr_input_mesh *src, struct cxr_vdf *output)
{
// Split mesh into islands
struct cxr_mesh *main_mesh = cxr_to_internal_format(src, &abverts);
u32 error = 0x00;
+ int invalid_count = 0;
struct solidinf
{
struct cxr_mesh *pmesh;
- int is_displacement;
+ int is_displacement, invalid;
};
struct cxr_auto_buffer solids;
cxr_ab_init( &solids, sizeof(struct solidinf), 2 );
- // TODO: Preprocessor stages
- // - Split mesh up into islands before doing anything here (DONE)
- // - Snap vertices to grid (0.25u) ?
-
// Preprocessor 1: Island seperation
// ---------------
}
cxr_ab_push( &solids, &(struct solidinf){main_mesh,0} );
- // Preprocessor 2: Displacement break-out
+ // Preprocessor 2: Displacement break-out and error checking
// ---------------
+ for( int i=0; i<solids.count; i++ )
+ {
+ struct solidinf *pinf = cxr_ab_ptr(&solids,i);
+
+ for( int j=0; j<pinf->pmesh->polys.count; j++ )
+ {
+ struct cxr_polygon *poly = cxr_ab_ptr( &pinf->pmesh->polys, j );
+
+ for( int k=0; k<poly->loop_total; k++ )
+ {
+ struct cxr_loop *lp = cxr_ab_ptr( &pinf->pmesh->loops, poly->loop_start+k );
+ struct cxr_edge *edge = cxr_ab_ptr( &pinf->pmesh->edges, lp->edge_index );
+
+ if( edge->freestyle )
+ goto IL_SOLID_IS_DISPLACEMENT;
+ }
+ }
+
+ if( cxr_solid_checkerr( pinf->pmesh, &abverts ) )
+ {
+ pinf->invalid = 1;
+ invalid_count ++;
+ }
+
+ continue;
+ IL_SOLID_IS_DISPLACEMENT:;
+
+ pinf->is_displacement = 1;
+ cxr_write_disp( pinf->pmesh, src, output, &abverts );
+ }
// Preprocessor 3: Breakup non-convex shapes into sub-solids
// ---------------
{
struct solidinf pinf = *(struct solidinf *)cxr_ab_ptr(&solids, i);
- if( pinf.is_displacement )
- // TODO: write displacements here...
+ if( pinf.is_displacement || pinf.invalid )
continue;
while(1)
if( error ) break;
}
+ else
+ break;
}
else
break;
for( int i=0; i<solids.count; i++ )
{
struct solidinf *solid = cxr_ab_ptr(&solids,i);
- cxr_debug_mesh( solid->pmesh, cxr_ab_ptr(&abverts,0), colours_random[cxr_range(i,8)] );
+
+ if( !solid->is_displacement )
+ cxr_debug_mesh( solid->pmesh, cxr_ab_ptr(&abverts,0), colours_random[cxr_range(i,8)] );
}
}