From: hgn Date: Fri, 9 Jul 2021 11:28:39 +0000 (+0100) Subject: init X-Git-Url: https://harrygodden.com/git/?p=csRadar.git;a=commitdiff_plain;h=a97099abba0a239e20929f04ece9d6839c96ac14 init --- a97099abba0a239e20929f04ece9d6839c96ac14 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c70091b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.h linguist-language=C +*.vs linguist-language=GLSL +*.fs linguist-language=GLSL +*.gls linguist-language=GLSL diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..5431199 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Whitelist mode + +# Ignore all but directories +* +!*/ + +./build.linux + +# ALLOW ============================ +!.gitattributes +!.gitignore + +# Code sources _____________________ +# C source files +!*.c +!*.h + +# Blender projects +!*.blend + +# Build scripts +!*.sh +!*.bat diff --git a/csRadar.c b/csRadar.c new file mode 100644 index 0000000..ae383f7 --- /dev/null +++ b/csRadar.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +// CSR lib +#include "csrTypes.h" +#include "csrMath.h" +#include "csrMem.h" +#include "csrIO.h" +#include "csrComb.h" + +// Valve formats +#include "vdf.h" +#include "vpk.h" +#include "vmdl.h" +#include "vmf.h" + +#include "vfilesys.h" + +// CSR main +#include "csrDraw.h" +#include "csr32f.h" + + +//#include "stretchy_buffer.h" + +int main( int argc, char *argv[] ) +{ + vmf_vert *triangles = NULL; + vmf_vert oh_yeah_yeah = {0}; + + for( int i = 0; i < 22; i ++ ) + { + triangles = csr_sb_reserve( triangles, 3, sizeof( vmf_vert ) ); + + vmf_vert *tri = triangles + csr_sb_count( triangles ); + + tri[0] = oh_yeah_yeah; + tri[1] = oh_yeah_yeah; + tri[2] = oh_yeah_yeah; + + csr_sb_inc( triangles, 3 ); + } + + if( argc == 2 ) + { + + printf( "read: %s\n", argv[1] ); + vdf_node *node = vdf_open_file( argv[1] ); + + + vmf_solid solid_main; + + + solidgen_ctx_init( &solid_main ); + + vdf_node *world = vdf_next( node, "world", NULL ); + + vdf_foreach( world, "solid", brush ) + { + solidgen_push( &solid_main, brush ); + } + + vmf_vert *triangles = NULL; + + for( int i = 0; i < csr_sb_count( solid_main.indices )/3; i ++ ) + { + u32 * base = solid_main.indices + i*3; + + triangles = csr_sb_reserve( triangles, 3, sizeof( vmf_vert ) ); + + vmf_vert *tri = triangles + csr_sb_count( triangles ); + + tri[0] = solid_main.verts[ base[0] ]; + tri[1] = solid_main.verts[ base[1] ]; + tri[2] = solid_main.verts[ base[2] ]; + + csr_sb_inc( triangles, 3 ); + } + + csr_frag *image = (csr_frag *)csr_malloc( 1024*1024*sizeof(csr_frag) ); + draw_buffers( image, 1024, 1024, (v4f){ -1000.f, -1000.f, 1000.f, 1000.f }, triangles, csr_sb_count( triangles )/3 ); + + float *rgba_test = (float *)csr_malloc( 1024*1024*sizeof(float)*3 ); + + for( int i = 0; i < 1024*1024; i ++ ) + { + rgba_test[i*3+0] = image[i].qa; + rgba_test[i*3+1] = image[i].qb; + rgba_test[i*3+2] = image[i].depth; + } + + csr_32f_write( "hello.pfm", 1024, 1024, rgba_test ); + + free( rgba_test ); + free( image ); + csr_sb_free( triangles ); + + solidgen_to_obj( &solid_main, "hello.obj" ); + + vdf_free_r( node ); + } +} diff --git a/csr32f.h b/csr32f.h new file mode 100644 index 0000000..1586598 --- /dev/null +++ b/csr32f.h @@ -0,0 +1,27 @@ + // Implementation of: Portable float-map +// Spec: https://web.archive.org/web/20100708234919/gl.ict.usc.edu/HDRShop/PFM/PFM_Image_File_Format.html +// Known to work in: Substance Designer ( Use node format c32f ) + +// Write pfm file to disk (32 bpc, RGB floats) (96 bits per pixel) +int csr_32f_write( char const *path, int w, int h, float const *buffer ) +{ + FILE *write_ptr; + uint32_t image_size_bytes; + + write_ptr = fopen( path, "wb" ); + + if( !write_ptr ) + return 0; + + // Write header (little endian) + fprintf( write_ptr, "PF\n%d %d\n-1.0\n", w, h ); + + image_size_bytes = w * h * sizeof( float ) * 3; + + // Write data + fwrite( buffer, image_size_bytes, 1, write_ptr ); + + fclose( write_ptr ); + + return 1; +} diff --git a/csrComb.h b/csrComb.h new file mode 100644 index 0000000..492acdf --- /dev/null +++ b/csrComb.h @@ -0,0 +1,39 @@ +#ifndef CSR_COMB_H +#define CSR_COMB_H + +void csr_comb_init( int const M, int p[] ) +{ + for( int i = 0; i < M; i ++ ) + { + p[ i ] = i; + } +} + +int csr_comb( int M, int N, int p[] ) +{ + for( int j = M-1; j >= 0; --j ) + { + if( p[j] < N-(M-j) ) // Can J be incremented? + { + p[j] ++; + for( int k = j+1; k < M; k ++ ) // Adjust following indexes + { + p[k] = p[j]+k-j; + } + return 1; + } + } + return 0; +} + +void csr_comb_print( int M, int p[] ) +{ + for( int i = 0; i < M; i ++ ) + { + printf( "%d ", p[i] ); + } + + printf( "\n" ); +} + +#endif diff --git a/csrDraw.h b/csrDraw.h new file mode 100644 index 0000000..e0635bb --- /dev/null +++ b/csrDraw.h @@ -0,0 +1,88 @@ +#include + +typedef struct csr_frag csr_frag; + +struct csr_frag +{ + u32 id; // Triangle index + float qa, qb; // Quantities + + float depth; // 'depth testing' +}; + +void clear_depth( csr_frag fragments[], u32 x, u32 y ) +{ + for( u32 i = 0; i < x*y; i ++ ) + { + fragments[ i ].depth = INFINITY; + } +} + +void simple_raster( csr_frag fragments[], u32 x, u32 y, v4f cam_bounds, vmf_vert tri[3], int id ) +{ + // Very simplified tracing algorithm + + v2f bmin = { 0.f, 0.f }; + v2f bmax = { x, y }; + + v2_minv( tri[0].co, tri[1].co, bmin ); + v2_minv( tri[2].co, bmin, bmin ); + + v2_maxv( tri[0].co, tri[1].co, bmax ); + v2_maxv( tri[2].co, bmax, bmax ); + + float range_x = (cam_bounds[2]-cam_bounds[0])/(float)x; + float range_y = (cam_bounds[3]-cam_bounds[1])/(float)y; + + int start_x = csr_max( 0, floorf( (bmin[0]-cam_bounds[0])/range_x)); + int end_x = csr_min( x, floorf( (bmax[0]-cam_bounds[0])/range_x )); + int start_y = csr_max( 0, ceilf( (bmin[1]-cam_bounds[1])/range_y )); + int end_y = csr_min( y, ceilf( (bmax[1]-cam_bounds[1])/range_y )); + + v3f trace_dir = { 0.f, 0.f, -1.f }; + v3f trace_origin = { 0.f, 0.f, 16385.f }; + + for( u32 py = start_y; py < end_y; py ++ ) + { + trace_origin[1] = csr_lerpf( cam_bounds[1], cam_bounds[3], (float)py/(float)y ); + + for( u32 px = start_x; px < end_x; px ++ ) + { + trace_origin[0] = csr_lerpf( cam_bounds[0], cam_bounds[2], (float)px/(float)x ); + + csr_frag *frag = &fragments[ py*y + px ]; + + float tqa = 0.f, tqb = 0.f; + float tdepth = csr_ray_tri( trace_origin, trace_dir, tri[0].co, tri[1].co, tri[2].co, &tqa, &tqb ); + + if( tdepth < frag->depth ) + { + frag->depth = tdepth; + frag->id = id; + frag->qa = tqa; + frag->qb = tqb; + } + } + } +} + +// First pass 'fragmentize' +void draw_buffers( csr_frag fragments[], u32 x, u32 y, v4f cam_bounds, vmf_vert *triangles, u32 triangle_count ) +{ + clock_t t; + t = clock(); + printf("Timer starts\n"); + + clear_depth( fragments, x, y ); + + for( u32 i = 0; i < triangle_count; i ++ ) + { + vmf_vert *triangle = triangles + i*3; + simple_raster( fragments, x, y, cam_bounds, triangle, i ); + } + + printf("Timer ends \n"); + t = clock() - t; + double time_taken = ((double)t)/CLOCKS_PER_SEC; // calculate the elapsed time + printf("Tracing took %f seconds to execute\n", time_taken); +} diff --git a/csrIO.h b/csrIO.h new file mode 100644 index 0000000..898b41a --- /dev/null +++ b/csrIO.h @@ -0,0 +1,162 @@ +// Low level disk reading +//======================================================================================================================= + +i64 fs_file_size( FILE *fileptr ) +{ + fseek( fileptr, 0, SEEK_END ); + i64 fsize = ftell( fileptr ); + fseek( fileptr, 0, SEEK_SET ); + + return fsize; +} + +void *fs_disk_open_read( const char *path, int const reserve_end, i64 *size ) +{ + FILE *f = fopen( path, "rb" ); + if( f ) + { + i64 fsize = fs_file_size( f ); + void *buf = csr_malloc( fsize + reserve_end ); + + if( buf ) + { + // Invalid / corrupt read + if( fread( buf, 1, fsize, f ) != fsize ) + { + free( buf ); + buf = NULL; + } + } + + *size = fsize; + + fclose( f ); + return buf; + } + else + { + return NULL; + } +} + +char *fs_disk_load_text( const char *path, i64 *size ) +{ + char *buf; + i64 fsize; + + if( (buf = fs_disk_open_read( path, 1, &fsize )) ) + { + buf[ fsize ] = 0x00; + *size = fsize +1; + + return buf; + } + + return NULL; +} + +void *csr_asset_read_s( const char *path, i64 *size ) +{ + return fs_disk_open_read( path, 0, size ); +} + +void *csr_asset_read( const char *path ) +{ + i64 size; + return fs_disk_open_read( path, 0, &size ); +} + +char *csr_textasset_read_s( const char *path, i64 *size ) +{ + return fs_disk_load_text( path, size ); +} + +char *csr_textasset_read( const char *name ) +{ + i64 size; + return fs_disk_load_text( name, &size ); +} + +// Find file path extension, returns NULL if no ext (0x00) +char *csr_findext( char *path, char const delim ) +{ + char *c, *ptr; + + c = path; + ptr = NULL; + + while( *c ) + { + if( *c == delim ) + { + ptr = c + 1; + } + + c ++; + } + + return ptr; +} + +// gets rid of extension on string only left with folder/filename +void csr_stripext( char *path ) +{ + char *point, *start; + + // Skip folders + if( !(start = csr_findext( path, '/' )) ) + { + start = path; + } + + if( (point = csr_findext( start, '.' )) ) + { + if( point > path ) + { + *(point-1) = 0x00; + } + } +} + +// Convert windows paths to unix-ish ( \something\\blahblah .. ) -> /something/blahblah/ +void csr_path_winunix( char *path ) +{ + char *idx, *wr; + wr = idx = path; + + while( *idx ) + { + if( *idx == '\\' ) + { + *idx = '/'; + } + + if( idx > path ) + { + if( *(idx -1) == '/' && *idx == '/') idx ++; + } + + *( wr ++ ) = *idx; + + idx ++; + } + + *wr = 0x00; +} + +int csr_path_is_abs( char const *path ) +{ +#ifdef _WIN32 + if( strlen( path ) < 2 ) return 0; + return path[1] == ':'; +#else + if( strlen( path ) < 1 ) return 0; + return path[0] == '/'; +#endif +} + +#ifdef _WIN32 + #define GetWorkingDir _getcwd +#else + #define GetWorkingDir getcwd +#endif diff --git a/csrMath.h b/csrMath.h new file mode 100644 index 0000000..c758afc --- /dev/null +++ b/csrMath.h @@ -0,0 +1,418 @@ +// Util +// ================================================================================================================== + +float csr_minf( float a, float b ) +{ + if( a < b ) + return a; + return b; +} + +float csr_maxf( float a, float b ) +{ + if( a > b ) + return a; + return b; +} + +int csr_min( int a, int b ) +{ + if( a < b ) + return a; + return b; +} + +int csr_max( int a, int b ) +{ + if( a > b ) + return a; + return b; +} + +void v3d_v3f( double a[3], float b[3] ) +{ + b[0] = a[0]; + b[1] = a[1]; + b[2] = a[2]; +} + +// Vector 2 +// ================================================================================================================== + +void v2_copy( v2f a, v2f b ) +{ + b[0] = a[0]; b[1] = a[1]; +} + +void v2_minv( v2f a, v2f b, v2f dest ) +{ + dest[0] = csr_minf(a[0], b[0]); + dest[1] = csr_minf(a[1], b[1]); +} + +void v2_maxv( v2f a, v2f b, v2f dest ) +{ + dest[0] = csr_maxf(a[0], b[0]); + dest[1] = csr_maxf(a[1], b[1]); +} + +// Vector 3 +// ================================================================================================================== + +void v3_copy( v3f a, v3f b ) +{ + b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; +} + +void v3_add( v3f a, v3f b, v3f d ) +{ + d[0] = a[0]+b[0]; d[1] = a[1]+b[1]; d[2] = a[2]+b[2]; +} + +void v3_sub( v3f a, v3f b, v3f d ) +{ + d[0] = a[0]-b[0]; d[1] = a[1]-b[1]; d[2] = a[2]-b[2]; +} + +void v3_mul( v3f a, v3f b, v3f d ) +{ + d[0] = a[0]*b[0]; d[1] = a[1]*b[1]; d[2] = a[2]*b[2]; +} + +void v3_div( v3f a, v3f b, v3f d ) +{ + d[0] = a[0]/b[0]; d[1] = a[1]/b[1]; d[2] = a[2]/b[2]; +} + +void v3_muls( v3f a, float s, v3f d ) +{ + d[0] = a[0]*s; d[1] = a[1]*s; d[2] = a[2]*s; +} + +void v3_divs( v3f a, float s, v3f d ) +{ + d[0] = a[0]/s; d[1] = a[1]/s; d[2] = a[2]/s; +} + +void v3_muladds( v3f a, v3f b, float s, v3f d ) +{ + d[0] = a[0]+b[0]*s; d[1] = a[1]+b[1]*s; d[2] = a[2]+b[2]*s; +} + +float v3_dot( v3f a, v3f b ) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +void v3_cross( v3f a, v3f b, v3f d ) +{ + d[0] = a[1] * b[2] - a[2] * b[1]; + d[1] = a[2] * b[0] - a[0] * b[2]; + d[2] = a[0] * b[1] - a[1] * b[0]; +} + +float v3_length2( v3f a ) +{ + return v3_dot( a, a ); +} + +float v3_length( v3f a ) +{ + return sqrtf( v3_length2( a ) ); +} + +float v3_dist2( v3f a, v3f b ) +{ + v3f delta; + v3_sub( a, b, delta ); + return v3_length2( delta ); +} + +float v3_dist( v3f a, v3f b ) +{ + return sqrtf( v3_dist2( a, b ) ); +} + +void v3_normalize( v3f a ) +{ + v3_muls( a, 1.f / v3_length( a ), a ); +} + +float csr_lerpf( float a, float b, float t ) +{ + return a + t*(b-a); +} + +void v3_lerp( v3f a, v3f b, float t, v3f d ) +{ + d[0] = a[0] + t*(b[0]-a[0]); + d[1] = a[1] + t*(b[1]-a[1]); + d[2] = a[2] + t*(b[2]-a[2]); +} + +void v3_minv( v3f a, v3f b, v3f dest ) +{ + dest[0] = csr_minf(a[0], b[0]); + dest[1] = csr_minf(a[1], b[1]); + dest[2] = csr_minf(a[2], b[2]); +} + +void v3_maxv( v3f a, v3f b, v3f dest ) +{ + dest[0] = csr_maxf(a[0], b[0]); + dest[1] = csr_maxf(a[1], b[1]); + dest[2] = csr_maxf(a[2], b[2]); +} + +float v3_minf( v3f a ) +{ + return csr_minf( csr_minf( a[0], a[1] ), a[2] ); +} + +float v3_maxf( v3f a ) +{ + return csr_maxf( csr_maxf( a[0], a[1] ), a[2] ); +} + +void v3_fill( v3f a, float v ) +{ + a[0] = v; + a[1] = v; + a[2] = v; +} + + + +// Matrix 4x3 +// ================================================================================================================== + +#define M4X3_IDENTITY {{1.0f, 0.0f, 0.0f, },\ + { 0.0f, 1.0f, 0.0f, },\ + { 0.0f, 0.0f, 1.0f, },\ + { 0.0f, 0.0f, 0.0f }} + +void m4x3_mul( m4x3f a, m4x3f b, m4x3f d ) +{ + float + a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], + a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], + a20 = a[2][0], a21 = a[2][1], a22 = a[2][2], + a30 = a[3][0], a31 = a[3][1], a32 = a[3][2], + b00 = b[0][0], b01 = b[0][1], b02 = b[0][2], + b10 = b[1][0], b11 = b[1][1], b12 = b[1][2], + b20 = b[2][0], b21 = b[2][1], b22 = b[2][2], + b30 = b[3][0], b31 = b[3][1], b32 = b[3][2]; + + d[0][0] = a00*b00 + a10*b01 + a20*b02 + a30; + d[0][1] = a01*b00 + a11*b01 + a21*b02 + a31; + d[0][2] = a02*b00 + a12*b01 + a22*b02 + a32; + d[1][0] = a00*b10 + a10*b11 + a20*b12 + a30; + d[1][1] = a01*b10 + a11*b11 + a21*b12 + a31; + d[1][2] = a02*b10 + a12*b11 + a22*b12 + a32; + d[2][0] = a00*b20 + a10*b21 + a20*b22 + a30; + d[2][1] = a01*b20 + a11*b21 + a21*b22 + a31; + d[2][2] = a02*b20 + a12*b21 + a22*b22 + a32; + d[3][0] = a00*b30 + a10*b31 + a20*b32 + a30; + d[3][1] = a01*b30 + a11*b31 + a21*b32 + a31; + d[3][2] = a02*b30 + a12*b31 + a22*b32 + a32; +} + +void m4x3_mulv( m4x3f m, v3f v, v3f d ) +{ + v3f res; + + res[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]; + res[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]; + res[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]; + + v3_copy( res, d ); +} + +// Affine transforms + +void m4x3_translate( m4x3f m, v3f v ) +{ + v3_muladds( m[3], m[0], v[0], m[3] ); + v3_muladds( m[3], m[1], v[1], m[3] ); + v3_muladds( m[3], m[2], v[2], m[3] ); +} + +void m4x3_scale( m4x3f m, float s ) +{ + v3_muls( m[0], s, m[0] ); + v3_muls( m[1], s, m[1] ); + v3_muls( m[2], s, m[2] ); +} + +void m4x3_rotate_x( m4x3f m, float angle ) +{ + m4x3f t = M4X3_IDENTITY; + float c, s; + + c = cosf( angle ); + s = sinf( angle ); + + t[1][1] = c; + t[1][2] = s; + t[2][1] = -s; + t[2][2] = c; + + m4x3_mul( m, t, m ); +} + +void m4x3_rotate_y( m4x3f m, float angle ) +{ + m4x3f t = M4X3_IDENTITY; + float c, s; + + c = cosf( angle ); + s = sinf( angle ); + + t[0][0] = c; + t[0][2] = -s; + t[2][0] = s; + t[2][2] = c; + + m4x3_mul( m, t, m ); +} + +void m4x3_rotate_z( m4x3f m, float angle ) +{ + m4x3f t = M4X3_IDENTITY; + float c, s; + + c = cosf( angle ); + s = sinf( angle ); + + t[0][0] = c; + t[0][1] = s; + t[1][0] = -s; + t[1][1] = c; + + m4x3_mul( m, t, m ); +} + +// Planes (double precision) +// ================================================================================================================== + +void tri_to_plane( 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; +} + +int plane_intersect( double a[4], double b[4], double c[4], double p[4] ) +{ + double const epsilon = 1e-8f; + + double x[3]; + 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]; + + 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; + + return 1; +} + +double plane_polarity( double p[4], double a[3] ) +{ + 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]) + ; +} + +// Raycasting +// ================================================================================================================== + +int csr_slabs( v3f box[2], v3f o, v3f id ) +{ + v3f t0; v3f t1; + v3f tmin; v3f tmax; + + v3_sub( box[0], o, t0 ); + v3_sub( box[1], o, t1 ); + v3_mul( t0, id, t0 ); + v3_mul( t1, id, t1 ); + + v3_minv( t0, t1, tmin ); + v3_maxv( t0, t1, tmax ); + + return v3_maxf( tmin ) <= v3_minf( tmax ); +} + +float csr_ray_tri( v3f o, v3f d, v3f v0, v3f v1, v3f v2, float *u, float *v ) +{ + float const k_cullEpsilon = 0.0001f; + + v3f v0v1; + v3f v0v2; + v3f p; + float det, inv; + + v3f tv; + v3f qv; + + v3_sub( v1, v0, v0v1 ); + v3_sub( v2, v0, v0v2 ); + v3_cross( d, v0v2, p ); + + det = v3_dot( v0v1, p ); + + if( det < k_cullEpsilon ) return INFINITY; + + inv = 1.f / det; + + v3_sub( o, v0, tv ); + *u = v3_dot( tv, p ) * inv; + + if( *u < 0.f || *u > 1.f ) return INFINITY; + + v3_cross( tv, v0v1, qv ); + *v = v3_dot( d, qv ) * inv; + + if( *v < 0.f || *u + *v > 1.f ) return INFINITY; + + return v3_dot( v0v2, qv ) * inv; +} diff --git a/csrMem.h b/csrMem.h new file mode 100644 index 0000000..dbcc942 --- /dev/null +++ b/csrMem.h @@ -0,0 +1,90 @@ +#ifndef CSR_MEM_H +#define CSR_MEM_H + +#include "csrTypes.h" + +void *csr_malloc( size_t size ) +{ + void *ret = malloc( size ); + if( !ret ) + exit(1); + return ret; +} + +void *csr_sb_raw( void *arr ) +{ + return ((u32 *)arr)-3; +} + +u32 csr_sb_count( void *arr ) +{ + return arr? ((u32 *)csr_sb_raw( arr ))[1]: 0; +} + +u32 csr_sb_cap( void *arr ) +{ + return arr? ((u32 *)csr_sb_raw( arr ))[0]: 0; +} + +void *csr_sb_use( void *arr ) +{ + u32 *raw = (u32 *)csr_sb_raw( arr ); + void *data = ((u8 *)arr) + raw[1]*raw[2]; + + raw[1] ++; + + return data; +} + +void csr_sb_inc( void *arr, u32 amt ) +{ + u32 *raw = (u32 *)csr_sb_raw( arr ); + raw[1] += amt; +} + +void *csr_sb_reserve( void *arr, u32 amt, u32 esize ) +{ + u32 cap = arr? csr_sb_cap( arr ): 0; + u32 count = arr? csr_sb_count( arr ): 0; + + if( count + amt > cap ) + { + cap = csr_max( count + amt, cap * 2 ); + u32 *new_ptr = (u32 *)realloc( arr? csr_sb_raw( arr ): NULL, cap * esize + 3*sizeof(u32) ); + + if( !new_ptr ) + abort(); + + new_ptr[0] = cap; + new_ptr[1] = count; + new_ptr[2] = esize; + + return (void *)(new_ptr+3); + } + else + { + return arr; + } +} + +void csr_sb_free( void *arr ) +{ + if( arr ) + { + free( csr_sb_raw( arr ) ); + } +} + +// djb2 - Dan Bernstein +unsigned long djb2( unsigned char const *str ) +{ + unsigned long hash = 5381; + int c; + + while((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} + +#endif diff --git a/csrTypes.h b/csrTypes.h new file mode 100644 index 0000000..e65ad99 --- /dev/null +++ b/csrTypes.h @@ -0,0 +1,15 @@ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef unsigned int uint; + +typedef float v2f[2]; +typedef float v3f[3]; +typedef float v4f[4]; +typedef v3f m4x3f[4]; diff --git a/geometry_buffers.c b/geometry_buffers.c new file mode 100644 index 0000000..d3b18b6 --- /dev/null +++ b/geometry_buffers.c @@ -0,0 +1,7 @@ +int csr_run( csr_opts *context ) +{ + context->x; + context->y; + + context->etc; +} diff --git a/plugins/geometry_buffers.c b/plugins/geometry_buffers.c new file mode 100644 index 0000000..e69de29 diff --git a/vdf.h b/vdf.h new file mode 100644 index 0000000..ddfc2d3 --- /dev/null +++ b/vdf.h @@ -0,0 +1,602 @@ +#define vdf_foreach( NODE, STR, AS ) \ +int __vdf_it_##__LINE__ = 0; \ +vdf_node * AS;\ +while( (AS = vdf_next( NODE, STR, &__vdf_it_##__LINE__ )) ) + +#define kv_foreach( NODE, STR, AS ) \ +int __kv_it_##__LINE__ = 0; \ +const char * AS;\ +while( (AS = kv_iter( NODE, STR, &__kv_it_##__LINE__ )) ) + +#include +#include +#include +#include + +// TYPES +// ================================================================================================================== + +typedef struct vdf_kv vdf_kv; +typedef struct vdf_node vdf_node; +typedef struct vdf_ctx vdf_ctx; + +// API +// ================================================================================================================== + +// Open / close +vdf_node *vdf_open_file( const char *fn ); +void vdf_free_r( vdf_node *p ); + +// Nodes +// ----- + +// Starting from *it, get next child with matching name from node. +vdf_node *vdf_next( vdf_node *node, const char *name, int *it ); + +// Create new empty node attached to parent. name can be NULL +vdf_node *vdf_create_node( vdf_node *parent, const char *name ); + +// Save / printing +// --------------- + +void vdf_out( vdf_node *h, int lvl, int declare, FILE *file ); +void vdf_save( vdf_node *node, const char *fn ); +void vdf_print( vdf_node *node ); + +// Keyvalue pairs +// -------------- + +// Get value string pointer from node's dictionary +const char *kv_get( vdf_node *node, const char *key, const char *value_defalt ); + +// Iterate each keyvalue starting from *it until key is matched +char *kv_iter( vdf_node *node, const char *key, int *it ); + +// Get keyvalue from node as int / float +int kv_get_int( vdf_node *node, const char *key, const int default_value ); +float kv_get_float( vdf_node *node, const char *key, float default_value ); + +// Parse values into sepecifc type +void kv_int_array( vdf_node *node, const char *key, u32 count, int *arr ); +void kv_float_array( vdf_node *node, const char *key, u32 count, float *arr ); +void kv_double_array( vdf_node *node, const char *key, u32 count, double *arr ); + +// INTERNAL API +// ================================================================================================================== + +// Add keyvalue pair to node +void vdf_kv_append( vdf_node *p, const char *k, const char *v ); + +// (low level api for arrays) +void kv_parse_array( const char *source, u32 esize, u32 count, void(*interp_func)(const char *src, void *dest), void *arr ); +void vdf_str_to_float( const char *src, void *dest ); +void vdf_str_to_int( const char *src, void *dest ); + +// Parsing context +// --------------- +void vdf_newln( vdf_ctx *ctx ); +void vdf_endl( vdf_ctx *ctx ); +int vdf_line_control( vdf_ctx *ctx ); +void vdf_wait_endl( vdf_ctx *ctx ); +void vdf_parse_string( vdf_ctx *ctx ); +int vdf_parse_structure( vdf_ctx *ctx ); +void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr ); +void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf ); + +// Formatting +void vdf_out_indent( const int n, FILE *file ); + +// IMPLEMENTATION +// ================================================================================================================== + +struct vdf_kv +{ + char *key; + char *value; +}; + +struct vdf_node +{ + char *name; + + vdf_node *parent; + + vdf_node **nodes; + vdf_kv *pairs; +}; + +vdf_node *vdf_next( vdf_node *node, const char *name, int *it ) +{ + if( !node ) + return NULL; + + for( int i = it? *it: 0; i < csr_sb_count( node->nodes ); i ++ ) + { + if( !strcmp( name, node->nodes[i]->name )) + { + if( it ) *it = i+1; + return node->nodes[i]; + } + } + + return NULL; +} + +const char *kv_get( vdf_node *node, const char *key, const char *value_defalt ) +{ + if( node ) + { + for( int i = 0; i < csr_sb_count( node->pairs ); i ++ ) + { + if( !strcmp( node->pairs[ i ].key, key ) ) + { + return node->pairs[ i ].value; + } + } + } + + return value_defalt; +} + +char *kv_iter( vdf_node *node, const char *key, int *it ) +{ + char *val; + + if( node ) + { + while( *it < csr_sb_count( node->pairs ) ) + { + if( !strcmp( node->pairs[ *it ].key, key ) ) + { + val = node->pairs[ *it ].value; + *it = *it + 1; + return val; + } + + *it = *it + 1; + } + } + + return NULL; +} + +void vdf_str_to_int( const char *src, void *dest ) +{ + *((int *)dest) = atoi( src ); +} + +void vdf_str_to_float( const char *src, void *dest ) +{ + *((float *)dest) = atof( src ); +} + +void vdf_str_to_double( const char *src, void *dest ) +{ + *((double *)dest) = atof( src ); +} + +void kv_parse_array( const char *source, u32 esize, u32 count, void(*interp_func)(const char *src, void *dest), void *arr ) +{ + if( !source ) + return; + + char value_buf[ 64 ]; + int token = 0; + + u32 i = 0; + u32 k = 0; + + char const *c = source; + + while( *c ) + { + if( *c == ' ' || *c == '\t' || *c == '[' || *c == ']' || *c == '(' || *c == ')' ) + { + if( token ) + { + value_buf[ k ] = 0x00; + token = 0; + + interp_func( value_buf, ((u8 *)arr) + i*esize ); + i ++; + + if( i >= count ) + { + break; + } + } + } + else + { + if( !token ) + { + token = 1; + k = 0; + } + + if( token ) + { + if( k < sizeof( value_buf ) - 1 ) + { + value_buf[ k ++ ] = *c; + } + } + } + + c ++; + } + + // Add remaining case if we hit null + if( token && (i < count) ) + { + value_buf[ k ] = 0x00; + interp_func( value_buf, ((u8 *)arr) + i*esize ); + } +} + +void kv_int_array( vdf_node *node, const char *key, u32 count, int *arr ) +{ + kv_parse_array( kv_get( node, key, NULL ), sizeof(int), count, vdf_str_to_int, arr ); +} + +void kv_float_array( vdf_node *node, const char *key, u32 count, float *arr ) +{ + kv_parse_array( kv_get( node, key, NULL ), sizeof(float), count, vdf_str_to_float, arr ); +} + +void kv_double_array( vdf_node *node, const char *key, u32 count, double *arr ) +{ + kv_parse_array( kv_get( node, key, NULL ), sizeof(double), count, vdf_str_to_double, arr ); +} + +int kv_get_int( vdf_node *node, const char *key, const int default_value ) +{ + const char *v = kv_get( node, key, NULL ); + return v? atoi(v): default_value; +} + +float kv_get_float( vdf_node *node, const char *key, float default_value ) +{ + const char *v = kv_get( node, key, NULL ); + return v? atof( v ): default_value; +} + +vdf_node *vdf_create_node( vdf_node *parent, const char *name ) +{ + vdf_node *node = calloc( 1, sizeof( vdf_node ) ); + + if( name ) + { + node->name = csr_malloc( strlen( name )+1 ); + strcpy( node->name, name ); + } + + if( parent ) + { + node->parent = parent; + + parent->nodes = csr_sb_reserve( parent->nodes, 1, sizeof(vdf_node *) ); + + vdf_node **child = (vdf_node **)csr_sb_use( parent->nodes ); + *child = node; + } + + return node; +} + +void vdf_kv_append( vdf_node *p, const char *k, const char *v ) +{ + p->pairs = csr_sb_reserve( p->pairs, 1, sizeof(vdf_kv) ); + vdf_kv *kv = (vdf_kv *)csr_sb_use( p->pairs ); + + u32 sv = strlen(v)+1; + u32 sk = strlen(k)+1; + + kv->key = csr_malloc( sv+sk ); + kv->value = kv->key+sk; + + memcpy( kv->key, k, sk ); + memcpy( kv->value, v, sv ); +} + +void vdf_free_r( vdf_node *p ) +{ + for( int i = 0; i < csr_sb_count( p->pairs ); i ++ ) + { + free( p->pairs[ i ].key ); + } + + for( int i = 0; i < csr_sb_count( p->nodes ); i ++ ) + { + vdf_free_r( p->nodes[ i ] ); + } + + csr_sb_free( p->pairs ); + csr_sb_free( p->nodes ); + free( p->name ); + free( p ); +} + +// PARSING +// ================================================================================================================== + +struct vdf_ctx +{ + char name[2048]; + + vdf_node *root; + + u32 lines; + u32 errors; + + // State + struct + { + int wait; + int expect_decl; + + char *tokens[2]; + int i; + + char *ptr_read; + + vdf_node *pnode; + } + st; +}; + +void vdf_newln( vdf_ctx *ctx ) +{ + ctx->lines ++; + + ctx->st.tokens[0] = NULL; + ctx->st.tokens[1] = NULL; + ctx->st.i = 0; +} + +void vdf_endl( vdf_ctx *ctx ) +{ + // Final out-tokens + if( ctx->st.tokens[0] ) + { + // Keypair + if( ctx->st.tokens[1] ) + { + vdf_kv_append( ctx->st.pnode, ctx->st.tokens[0], ctx->st.tokens[1] ); + } + else + { + // Decl + strcpy( ctx->name, ctx->st.tokens[0] ); + ctx->st.expect_decl = 1; + } + } + + vdf_newln( ctx ); +} + +int vdf_line_control( vdf_ctx *ctx ) +{ + if( *ctx->st.ptr_read == '\r' ) + { + *ctx->st.ptr_read = 0x00; + return 1; + } + if( *ctx->st.ptr_read == '\n' ) + { + *ctx->st.ptr_read = 0x00; + vdf_endl( ctx ); + return 2; + } + + return 0; +} + +void vdf_wait_endl( vdf_ctx *ctx ) +{ + while( (*ctx->st.ptr_read) && (*ctx->st.ptr_read != '\n') ) + { + if( vdf_line_control( ctx ) == 2 ) + { + return; + } + + ctx->st.ptr_read ++; + } +} + +void vdf_parse_string( vdf_ctx *ctx ) +{ + while( *ctx->st.ptr_read ) + { + if( *ctx->st.ptr_read == '"' ) + { + *ctx->st.ptr_read = 0x00; + return; + } + + if( vdf_line_control( ctx ) ) + { + fprintf( stderr, "Unexpected end of line character (Line: %u)\n", ctx->lines ); + return; + } + + ctx->st.ptr_read ++; + } +} + +int vdf_parse_structure( vdf_ctx *ctx ) +{ + if( *ctx->st.ptr_read == '{' ) + { + if( ctx->st.tokens[0] || !ctx->st.expect_decl ) + { + fprintf( stderr, "Unexpected token '{' (Line: %u)\n", ctx->lines ); + ctx->errors ++; + } + + ctx->st.expect_decl = 0; + ctx->st.pnode = vdf_create_node( ctx->st.pnode, ctx->name ); + + vdf_wait_endl( ctx ); + return 1; + } + + // Closing block, jump read head back to parent + if( *ctx->st.ptr_read == '}' ) + { + if( !ctx->st.pnode->parent ) + { + fprintf( stderr, "Unexpected token '}' (Line: %u)\n", ctx->lines ); + ctx->errors ++; + } + else + { + ctx->st.pnode = ctx->st.pnode->parent; + } + + vdf_wait_endl( ctx ); + return 1; + } + + return 0; +} + +void vdf_parse_begin_token( vdf_ctx *ctx, char *ptr ) +{ + ctx->st.tokens[ ctx->st.i ] = ptr; + + if( ctx->st.expect_decl ) + { + fprintf( stderr, "Unexpected token '%s' (Line: %u)\n", ctx->name, ctx->lines ); + ctx->errors ++; + } +} + +void vdf_parse_feedbuffer( vdf_ctx *ctx, char *buf ) +{ + ctx->st.ptr_read = buf; + + while( *ctx->st.ptr_read ) + { + if( !vdf_line_control( ctx ) ) + { + if( (*ctx->st.ptr_read == '/') && (ctx->st.ptr_read[1] == '/') ) + { + *ctx->st.ptr_read = 0x00; + ctx->st.ptr_read += 2; + + vdf_endl( ctx ); + vdf_wait_endl( ctx ); + } + else + { + if( !vdf_parse_structure( ctx ) ) + { + if( *ctx->st.ptr_read == ' ' || *ctx->st.ptr_read == '\t' ) + { + *ctx->st.ptr_read = 0x00; + + if( ctx->st.tokens[ ctx->st.i ] ) + { + ctx->st.i ++; + + if( ctx->st.i == 2 ) + { + vdf_wait_endl( ctx ); + } + } + } + // Start new entry + else if( !ctx->st.tokens[ ctx->st.i ] ) + { + if( *ctx->st.ptr_read == '"' ) + { + *ctx->st.ptr_read = 0x00; + ctx->st.ptr_read ++; + + vdf_parse_begin_token( ctx, ctx->st.ptr_read ); + vdf_parse_string( ctx ); + } + else + { + if( !( *ctx->st.ptr_read == '/' && *(ctx->st.ptr_read + 1) == *ctx->st.ptr_read ) ) + { + vdf_parse_begin_token( ctx, ctx->st.ptr_read ); + } + } + } + } + } + } + + ctx->st.ptr_read ++; + } +} + +vdf_node *vdf_open_file( const char *fn ) +{ + char *text_src = csr_textasset_read( fn ); + + if( !text_src ) + { + fprintf( stderr, "vdf open failed\n" ); + return NULL; + } + + vdf_ctx ctx = {0}; + ctx.root = ctx.st.pnode = vdf_create_node( NULL, NULL ); + + vdf_newln( &ctx ); + vdf_parse_feedbuffer( &ctx, text_src ); + + free( text_src ); + return ctx.root; +} + +// OUTPUT +// ================================================================================================================== + +void vdf_out_indent( const int n, FILE *file ) +{ + for( int x = 0; x < n; x ++ ) + fprintf( file, "\t" ); +} + +void vdf_out( vdf_node *h, int lvl, int declare, FILE *file ) +{ + if( declare ) + { + vdf_out_indent( lvl, file ); fprintf( file, "\"%s\"\n", h->name ); + vdf_out_indent( lvl, file ); fprintf( file, "{\n" ); + } + + for( int i = 0; i < csr_sb_count( h->pairs ); i ++ ) + { + vdf_out_indent( lvl+1, file ); fprintf( file, "\"%s\" \"%s\"\n", h->pairs[i].key, h->pairs[i].value ); + } + + for( int i = 0; i < csr_sb_count( h->nodes ); i ++ ) + { + vdf_out( h->nodes[i], lvl + 1, 1, file ); + } + + if( declare ) + { + vdf_out_indent( lvl, file ); fprintf( file, "}\n" ); + } +} + +void vdf_save( vdf_node *node, const char *fn ) +{ + FILE* file = fopen( fn, "w" ); + + vdf_out( node, -1, 0, file ); + + fclose( file ); +} + +void vdf_print( vdf_node *node ) +{ + vdf_out( node, -1, 0, stdout ); +} diff --git a/vfilesys.h b/vfilesys.h new file mode 100644 index 0000000..2f33f83 --- /dev/null +++ b/vfilesys.h @@ -0,0 +1,180 @@ +// Abstract Valve file system +//======================================================================================================================= + +struct valve_filesystem +{ + char gamedir[ 512 ]; + char exedir[ 512 ]; + char bindir[ 512 ]; // TODO: This is currently not set? + + VPKHeader *vpk; + char **searchpaths; + + FILE *current_archive; + u16 current_idx; +} +fs_global; + +void fs_set_gameinfo( const char *path ) +{ + struct valve_filesystem *fs = &fs_global; + + vdf_node *info = vdf_open_file( path ); + if( !info ) + return; + + // Set gamedir + strcpy( fs->gamedir, path ); + csr_path_winunix( fs->gamedir ); + *csr_findext( fs->gamedir, '/' ) = 0x00; + + // Set exe dir + strcpy( fs->exedir, fs->gamedir ); + strcat( fs->exedir, "../" ); + + // Get all search paths from file + vdf_node *search_paths = vdf_next(vdf_next(vdf_next( info, "GameInfo", NULL ), "FileSystem", NULL ), "SearchPaths", NULL ); + + kv_foreach( search_paths, "Game", kv_game ) + { + if( kv_game[0] == '|' ) continue; //TODO: deal with engine replacements?? maybe?? + + char *buf; + if( csr_path_is_abs( kv_game ) ) + { + buf = csr_malloc( strlen( kv_game ) + 2 ); + strcpy( buf, kv_game ); + strcat( buf, "/" ); + } + else + { + buf = csr_malloc( strlen( fs->exedir ) + strlen( kv_game ) + 2 ); + strcpy( buf, fs->exedir ); + strcat( buf, kv_game ); + strcat( buf, "/" ); + } + + fs->searchpaths = csr_sb_reserve( fs->searchpaths, 1, sizeof( char * ) ); + fs->searchpaths[ csr_sb_count( fs->searchpaths ) ] = buf; + csr_sb_use( fs->searchpaths ); + } + + vdf_free_r( info ); + + // Look for pak01_dir + char pack_path[512]; + for( int i = 0; i < csr_sb_count( fs->searchpaths ); i ++ ) + { + strcpy( pack_path, fs->searchpaths[i] ); + strcat( pack_path, "pak01_dir.vpk" ); + + if( (fs->vpk = (VPKHeader *)csr_asset_read( path )) ) + { + break; + } + } + + if( !fs->vpk ) + { + fprintf( stderr, "Could not locate pak01_dir.vpk in %i searchpaths. Stock models will not load!", csr_sb_count( fs->searchpaths ) ); + } + + printf( "fs_info:\n\ + gamedir: %s\n\ + exedir: %s\n\ + bin: %s\n\ + pack: %s\n\ + searchpaths:\n", fs->gamedir, fs->exedir, fs->bindir, pack_path ); + + for( int i = 0; i < csr_sb_count( fs->searchpaths ); i ++ ) + { + printf( " %s\n", fs->searchpaths[i] ); + } +} + +void fs_exit(void) +{ + struct valve_filesystem *fs = &fs_global; + + for( int i = 0; i < csr_sb_count( fs->searchpaths ); i ++ ) + { + free( fs->searchpaths[ i ] ); + } + csr_sb_free( fs->searchpaths ); + fs->searchpaths = NULL; + + vpk_free( fs->vpk ); + fs->vpk = NULL; + + if( fs->current_archive ) + { + fclose( fs->current_archive ); + fs->current_archive = NULL; + } +} + +char *valve_fs_get( const char *path ) +{ + struct valve_filesystem *fs = &fs_global; + + VPKDirectoryEntry *entry; + char pak[ 533 ]; + + if( (entry = vpk_find( fs->vpk, path )) ) + { + if( entry->ArchiveIndex != fs->current_idx ) + { + if( fs->current_archive ) + { + fclose( fs->current_archive ); + fs->current_archive = NULL; + } + + fs->current_idx = entry->ArchiveIndex; + } + + if( !fs->current_archive ) + { + sprintf( pak, "%scsgo/pak01_%03hu.vpk", fs->exedir, fs->current_idx ); + fs->current_archive = fopen( pak, "rb" ); + + if( !fs->current_archive ) + { + fprintf( stderr, "Could not locate %s\n", pak ); + return NULL; + } + } + + char *filebuf = csr_malloc( entry->EntryLength ); + + fseek( fs->current_archive, entry->EntryOffset, SEEK_SET ); + if( fread( filebuf, 1, entry->EntryLength, fs->current_archive ) == entry->EntryLength ) + { + return filebuf; + } + else + { + free( filebuf ); + return NULL; + } + } + else + { + // Use physical searchpaths + char path_buf[ 512 ]; + + for( int i = 0; i < csr_sb_count( fs->searchpaths ); i ++ ) + { + strcpy( path_buf, fs->searchpaths[ i ] ); + strcat( path_buf, path ); + + char *filebuf; + if( (filebuf = csr_asset_read( path_buf )) ) + { + return filebuf; + } + } + + return NULL; + } +} diff --git a/vmdl.h b/vmdl.h new file mode 100644 index 0000000..51b8f05 --- /dev/null +++ b/vmdl.h @@ -0,0 +1,579 @@ +// VVD +//======================================================================================================================= +//StudioMDL constants + +#define MAX_NUM_LODS 8 +#define MAX_NUM_BONES_PER_VERT 3 + +#pragma pack(push, 1) +typedef struct +{ + float weight[MAX_NUM_BONES_PER_VERT]; + char bone[MAX_NUM_BONES_PER_VERT]; + char numbones; +} +boneWeight_t; + +typedef struct +{ + boneWeight_t boneweights; + float pos[3]; + float norm[3]; + float uv[2]; +} +mstudiovertex_t; + +typedef struct +{ + int id; + int version; + int checksum; + int numLods; + int numLodVertexes[MAX_NUM_LODS]; + int numFixups; + int fixupTableStart; + int vertexDataStart; + int tangentDataStart; +} +vertexFileHeader_t; + +#pragma pack(pop) + +mstudiovertex_t *GetVertexData( vertexFileHeader_t *t ) +{ + return (mstudiovertex_t *) ( (char *)t + t->vertexDataStart ); +} + +// VTX +//======================================================================================================================= + +#pragma pack(push, 1) + +typedef struct +{ + // these index into the mesh's vert[origMeshVertID]'s bones + unsigned char boneWeightIndex[3]; + unsigned char numBones; + + unsigned short origMeshVertID; + + // for sw skinned verts, these are indices into the global list of bones + // for hw skinned verts, these are hardware bone indices + char boneID[3]; +} +VTXVertex_t; + +enum StripGroupFlags +{ + STRIPGROUP_IS_FLEXED = 0x01, + STRIPGROUP_IS_HWSKINNED = 0x02, + STRIPGROUP_IS_DELTA_FLEXED = 0x04, + STRIPGROUP_SUPPRESS_HW_MORPH = 0x08, // NOTE: This is a temporary flag used at run time. +}; + +enum StripHeaderFlags_t { + STRIP_IS_TRILIST = 0x01, + STRIP_IS_TRISTRIP = 0x02 +}; + +// A strip is a piece of a stripgroup which is divided by bones +typedef struct +{ + //Indices array + int numIndices; + int indexOffset; + + //Vertices array + int numVerts; + int vertOffset; + + short numBones; + + unsigned char flags; + + int numBoneStateChanges; + int boneStateChangeOffset; +} +VTXStripHeader_t; +// Bone state change inline code ommited + +// a locking group +// a single vertex buffer +// a single index buffer +typedef struct +{ + // These are the arrays of all verts and indices for this mesh. strips index into this. + int numVerts; + int vertOffset; + + int numIndices; + int indexOffset; + + int numStrips; + int stripOffset; + + unsigned char flags; +} +VTXStripGroupHeader_t; + +VTXVertex_t *pVertexVTX( VTXStripGroupHeader_t *t, int i ) +{ + return (VTXVertex_t *)(((char *)t) + t->vertOffset) + i; +} +unsigned short *pIndexVTX( VTXStripGroupHeader_t *t, int i ) +{ + return (unsigned short *)(((char *)t) + t->indexOffset) + i; +} +VTXStripHeader_t *pStripVTX( VTXStripGroupHeader_t *t, int i ) +{ + return (VTXStripHeader_t *)(((char *)t) + t->stripOffset) + i; +} + +typedef struct +{ + int numStripGroups; + int stripGroupHeaderOffset; + + unsigned char flags; +} +VTXMeshHeader_t; +VTXStripGroupHeader_t *pStripGroupVTX( VTXMeshHeader_t *t, int i ) +{ + return (VTXStripGroupHeader_t *)(((char *)t) + t->stripGroupHeaderOffset) + i; +} + +typedef struct +{ + //Mesh array + int numMeshes; + int meshOffset; + + float switchPoint; +} +VTXModelLODHeader_t; +VTXMeshHeader_t *pMeshVTX( VTXModelLODHeader_t *t, int i ) +{ + return (VTXMeshHeader_t *)(((char *)t) + t->meshOffset) + i; +} + +// This maps one to one with models in the mdl file. +typedef struct +{ + //LOD mesh array + int numLODs; //This is also specified in FileHeader_t + int lodOffset; +} +VTXModelHeader_t; +VTXModelLODHeader_t *pLODVTX( VTXModelHeader_t *t, int i ) +{ + return (VTXModelLODHeader_t *)(((char *)t) + t->lodOffset) + i; +} + +typedef struct +{ + //Model array + int numModels; + int modelOffset; +} +VTXBodyPartHeader_t; +VTXModelHeader_t *pModelVTX( VTXBodyPartHeader_t *t, int i ) +{ + return (VTXModelHeader_t *)(((char *)t) + t->modelOffset) + i; +} + +typedef struct +{ + // file version as defined by OPTIMIZED_MODEL_FILE_VERSION (currently 7) + int version; + + // hardware params that affect how the model is to be optimized. + int vertCacheSize; + unsigned short maxBonesPerStrip; + unsigned short maxBonesPerTri; + int maxBonesPerVert; + + // must match checkSum in the .mdl + int checkSum; + + int numLODs; // Also specified in ModelHeader_t's and should match + + // Offset to materialReplacementList Array. one of these for each LOD, 8 in total + int materialReplacementListOffset; + + //Defines the size and location of the body part array + int numBodyParts; + int bodyPartOffset; +} +VTXFileHeader_t; +VTXBodyPartHeader_t *pBodyPartVTX( VTXFileHeader_t *t, int i ) +{ + return (VTXBodyPartHeader_t *)(((char *)t) + t->bodyPartOffset) + i; +} + + /* + .VTX file structure + ============================================= + + FileHeader + L BodyParts:: + L Models:: + L LODS:: + L Meshes:: + L StripGroups:: + L VerticesTable[StudioMDL.Vertex] + L IndicesTable[UINT16] + | + L Strips:: + L Vertices[UINT16] + L Indices[UINT16] + */ + +#pragma pack(pop) + +u32 vtx_count_indices( VTXFileHeader_t *t ) +{ + u32 indices = 0; + + for ( int bodyID = 0; bodyID < t->numBodyParts; ++bodyID ) + { + VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( t, bodyID ); + for ( int modelID = 0; modelID < pVtxBodyPart->numModels; ++modelID ) + { + VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID ); + + int nLod = 0; + VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod ); + + for ( int nMesh = 0; nMesh < pVtxLOD->numMeshes; ++nMesh ) + { + VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh ); + + for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup ) + { + VTXStripGroupHeader_t* pStripGroup = pStripGroupVTX( pVtxMesh, nGroup ); + + for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ ) + { + VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip ); + + if ( pStrip->flags & STRIP_IS_TRILIST ) + { + indices += pStrip->numIndices; + } + } + } + } + } + } + + return indices; +} + +// MDL +//======================================================================================================================= + +#pragma pack(push, 1) + +typedef struct +{ + // base of external vertex data stores + void *pVertexData; + void *pTangentData; +} +mstudio_modelvertexdata_t; + +typedef struct +{ + // indirection to this mesh's model's vertex data + int unused_modelvertexdata; // 64b - Moved to follow num_LOD_Vertexes. + + // used for fixup calcs when culling top level lods + // expected number of mesh verts at desired lod + int numLODVertexes[MAX_NUM_LODS]; + + mstudio_modelvertexdata_t *_the_death_ptr; +} +mstudio_meshvertexdata_t; + +typedef struct mstudiomodel_t mstudiomodel_t; + +typedef struct +{ + int material; + int modelindex; + int numvertices; // number of unique vertices/normals/texcoords + int vertexoffset; // vertex mstudiovertex_t + int numflexes; // vertex animation + int flexindex; + // special codes for material operations + int materialtype; + int materialparam; + // a unique ordinal for this mesh + int meshid; + float center[3]; + mstudio_meshvertexdata_t vertexdata; + + int unused[6]; // remove as appropriate +} +mstudiomesh_t; + +// studio models +struct mstudiomodel_t +{ + char name[64]; + int type; + float boundingradius; + + int nummeshes; + int meshindex; + + int numvertices; // number of unique vertices/normals/texcoords + int vertexindex; // vertex Vector + int tangentsindex; // tangents Vector + + int numattachments; + int attachmentindex; + + int numeyeballs; + int eyeballindex; + + mstudio_modelvertexdata_t vertexdata; + + int unused[8]; // remove as appropriate +}; +mstudiomesh_t *studiomodel_pMesh( mstudiomodel_t *t, int i ) +{ + return (mstudiomesh_t *)(((char *)t) + t->meshindex) + i; +}; + +typedef struct +{ + int sznameindex; + int nummodels; + int base; + int modelindex; // index into models array +} mstudiobodyparts_t; + +mstudiomodel_t *mstudiobodyparts_pModel( mstudiobodyparts_t *t, int i ) +{ + return (mstudiomodel_t *)(((char *)t) + t->modelindex) + i; +}; + +typedef struct { + int id; + int version; + int checksum; // this has to be the same in the phy and vtx files to load! + char name[64]; + int length; + float eyeposition[3]; // ideal eye position + float illumposition[3]; // illumination center + float hull_min[3]; // ideal movement hull size + float hull_max[3]; + float view_bbmin[3]; // clipping bounding box + float view_bbmax[3]; + int flags; + int numbones; // bones + int boneindex; + int numbonecontrollers; // bone controllers + int bonecontrollerindex; + int numhitboxsets; + int hitboxsetindex; + int numlocalanim; // animations/poses + int localanimindex; // animation descriptions + int numlocalseq; // sequences + int localseqindex; + int activitylistversion; + int eventsindexed; + // raw textures + int numtextures; + int textureindex; + // raw textures search paths + int numcdtextures; + int cdtextureindex; + // replaceable textures tables + int numskinref; + int numskinfamilies; + int skinindex; + int numbodyparts; + int bodypartindex; + // queryable attachable points + int numlocalattachments; + int localattachmentindex; + // animation node to animation node transition graph + int numlocalnodes; + int localnodeindex; + int localnodenameindex; + int numflexdesc; + int flexdescindex; + int numflexcontrollers; + int flexcontrollerindex; + int numflexrules; + int flexruleindex; + int numikchains; + int ikchainindex; + int nummouths; + int mouthindex; + int numlocalposeparameters; + int localposeparamindex; + int surfacepropindex; + // Key values + int keyvalueindex; + int keyvaluesize; + int numlocalikautoplaylocks; + int localikautoplaylockindex; + // The collision model mass that jay wanted + float mass; + int contents; + // external animations, models, etc. + int numincludemodels; + int includemodelindex; + // for demand loaded animation blocks + int szanimblocknameindex; + int numanimblocks; + int animblockindex; + int bonetablebynameindex; + char constdirectionallightdot; + char rootLOD; + char numAllowedRootLODs; + char unused[1]; + int unused4; // zero out if version < 47 + int numflexcontrollerui; + int flexcontrolleruiindex; + float flVertAnimFixedPointScale; + int unused3[1]; + int studiohdr2index; + int unused2[1]; +} +studiohdr_t; + +mstudiobodyparts_t *studiohdr_pBodypart( studiohdr_t *t, int i ) +{ + return (mstudiobodyparts_t *)(((char *)t) + t->bodypartindex) + i; +}; + +#pragma pack(pop) + +typedef struct +{ + u16 *indices; + u32 num_indices; + + float *vertices; + u32 num_vertices; +} +mdl_mesh_t; + +void mdl_free( mdl_mesh_t *m ) +{ + free( m->indices ); + free( m->vertices ); +} + +int mdl_from_find_files( const char *mdlname, mdl_mesh_t *ctx ) +{ + // Read entire files into memory (inline functions map memory) + // .DX90.VTX + char path[1024]; + strcpy( path, mdlname ); + csr_stripext( path ); + strcat( path, ".dx90.vtx" ); + VTXFileHeader_t *pVtxHdr = (VTXFileHeader_t *)csr_asset_read( path ); + + if( !pVtxHdr ) + return 0; + + // .VVD + strcpy( path, mdlname ); + csr_stripext( path ); + strcat( path, ".vvd" ); + vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)csr_asset_read( path ); + + if( !pVvdHdr ) + { + free( pVtxHdr ); + return 0; + } + + // .MDL + strcpy( path, mdlname ); + csr_stripext( path ); + strcat( path, ".mdl" ); + studiohdr_t *pMdl = (studiohdr_t *)csr_asset_read( path ); + + if( !pMdl ) + { + free( pVtxHdr ); + free( pVvdHdr ); + return 0; + } + + ctx->num_indices = vtx_count_indices( pVtxHdr ); + + // Allocate and read indices + ctx->indices = (u16 *)csr_malloc( ctx->num_indices * sizeof( u16 ) ); + ctx->num_indices = 0; + + for ( int bodyID = 0; bodyID < pMdl->numbodyparts; ++bodyID ) + { + // Body parts + VTXBodyPartHeader_t* pVtxBodyPart = pBodyPartVTX( pVtxHdr, bodyID ); + mstudiobodyparts_t *pBodyPart = studiohdr_pBodypart( pMdl, bodyID ); + + for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID ) + { + // Models + VTXModelHeader_t* pVtxModel = pModelVTX( pVtxBodyPart, modelID ); + mstudiomodel_t *pStudioModel = mstudiobodyparts_pModel( pBodyPart, modelID ); + + int nLod = 0; + VTXModelLODHeader_t *pVtxLOD = pLODVTX( pVtxModel, nLod ); + + for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh ) + { + // Meshes + VTXMeshHeader_t* pVtxMesh = pMeshVTX( pVtxLOD, nMesh ); + mstudiomesh_t* pMesh = studiomodel_pMesh( pStudioModel, nMesh ); + + for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup ) + { + // Groups + VTXStripGroupHeader_t* pStripGroup = pStripGroupVTX( pVtxMesh, nGroup ); + + for ( int nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ ) + { + // Strips + VTXStripHeader_t *pStrip = pStripVTX( pStripGroup, nStrip ); + + if ( pStrip->flags & STRIP_IS_TRILIST ) + { + // Indices + for ( int i = 0; i < pStrip->numIndices; i ++ ) + { + u16 i1 = *pIndexVTX( pStripGroup, pStrip->indexOffset + i ); + ctx->indices[ ctx->num_indices ++ ] = pVertexVTX( pStripGroup, i1 )->origMeshVertID + pMesh->vertexoffset; + } + } + } + } + } + } + } + + mstudiovertex_t *vertexData = GetVertexData( pVvdHdr ); + + // Allocate vertex blob (XYZ|NRM|UV) + ctx->num_vertices = pVvdHdr->numLodVertexes[0]; + ctx->vertices = (float *)csr_malloc( ctx->num_vertices * 8 * sizeof( float ) ); + + for( int i = 0; i < ctx->num_vertices; i ++ ) + { + mstudiovertex_t *vert = vertexData + i; + memcpy( ctx->vertices + i * 8, vert->pos, 8 * sizeof(float) ); + } + + free( pVtxHdr ); + free( pVvdHdr ); + free( pMdl ); + + return 1; +} diff --git a/vmf.h b/vmf.h new file mode 100644 index 0000000..9baa117 --- /dev/null +++ b/vmf.h @@ -0,0 +1,580 @@ +#include "csrTypes.h" +#include "csrComb.h" + +#define SOLID_MAX_SIDES 512 + +typedef struct vmf_solid vmf_solid; +typedef struct vmf_vert vmf_vert; +typedef struct vmf_mat vmf_mat; +typedef struct vmf_face vmf_face; + +typedef enum ESolidResult ESolidResult; + +enum ESolidResult +{ + k_ESolidResult_valid, + k_ESolidResult_maxsides, + k_ESolidResult_invalid, + k_ESolidResult_errnomem, + k_ESolidResult_corrupt, + k_ESolidResult_degenerate +}; + +struct vmf_vert +{ + v3f co; + v3f nrm; + v2f xy; +}; + +struct vmf_face +{ + u32 *indices; + + vdf_node *dispinfo; + + const char *material; + int blacklisted; +}; + +struct vmf_solid +{ + vmf_vert *verts; + u32 *indices; +}; + +struct vmf_mat +{ + char *str; + u32 hash; +}; + +// IMPLEMENTATION + +void solidgen_ctx_init( vmf_solid *ctx ) +{ + const u32 init_size = 128; + + ctx->verts = csr_sb_reserve( NULL, init_size, sizeof(vmf_vert) ); + ctx->indices = csr_sb_reserve( NULL, init_size, sizeof(u32) ); +} + +void solidgen_ctx_free( vmf_solid *ctx ) +{ + csr_sb_free( ctx->verts ); + csr_sb_free( ctx->indices ); +} + +// Compute bounds of solid gen ctx +void solidgen_bounds( vmf_solid *ctx, u32 start, u32 end, v3f min, v3f max ) +{ + v3f mine = { INFINITY, INFINITY, INFINITY }; + v3f maxe = {-INFINITY,-INFINITY,-INFINITY }; + + for( int i = start; i < end; i ++ ) + { + vmf_vert *vert = ctx->verts + i; + float *co = vert->co; + + mine[0] = fminf( mine[0], co[0] ); + mine[1] = fminf( mine[1], co[1] ); + mine[2] = fminf( mine[2], co[2] ); + + maxe[0] = fmaxf( maxe[0], co[0] ); + maxe[1] = fmaxf( maxe[1], co[1] ); + maxe[2] = fmaxf( maxe[2], co[2] ); + } + + v3_copy( mine, min ); + v3_copy( maxe, max ); +} + +struct +{ + vmf_mat *blacklist; + + double planes[ SOLID_MAX_SIDES*4 ]; + u32 bisectors; +} +vmf_api; + +// put an extra plane into the planes list +void vmf_addbisector( double p[4] ) +{ + double *plane = vmf_api.planes + vmf_api.bisectors * 4; + + plane[0] = p[0]; + plane[1] = p[1]; + plane[2] = p[2]; + plane[3] = p[3]; + + vmf_api.bisectors ++; +} + +void vmf_clearbisectors( void ) +{ + vmf_api.bisectors = 0; +} + +void vmf_ignore_mat( const char *material ) +{ + vmf_api.blacklist = csr_sb_reserve( vmf_api.blacklist, 1, sizeof( vmf_mat ) ); + vmf_mat *mat = (vmf_mat *)csr_sb_use( vmf_api.blacklist ); + + mat->str = csr_malloc( strlen( material ) + 1 ); + strcpy( mat->str, material ); + + mat->hash = djb2( ( const unsigned char * )material ); +} + +void vmf_clearignore( void ) +{ + for( int i = 0; i < csr_sb_count( vmf_api.blacklist ); i ++ ) + { + free( vmf_api.blacklist[ i ].str ); + } + + csr_sb_free( vmf_api.blacklist ); + vmf_api.blacklist = NULL; +} + +int mat_blacklisted( const char *material ) +{ + u32 hash = djb2((const u8 *)material); + + for( int j = 0; j < csr_sb_count( vmf_api.blacklist ); j ++ ) + { + if( vmf_api.blacklist[ j ].hash == hash ) + { + if( !strcmp( material, vmf_api.blacklist[ j ].str ) ) + { + return 1; + } + } + } + + return 0; +} + +void sort_coplanar( double p[4], vmf_vert *points, u32 *indices, u32 count ) +{ + v3f center = {0.f, 0.f, 0.f}; + v3f norm; + v3d_v3f( p, norm ); + v3_normalize( norm ); + + for( int i = 0; i < count; i ++ ) + { + v3_add( points[ indices[i] ].co, center, center ); + } + v3_divs( center, count, center ); + + v3f ref; + v3_sub( points[ indices[0] ].co, center, ref ); + + // Calc angles compared to ref + float *angles = (float*)alloca( sizeof(float)*count ); + for( int i = 0; i < count; i ++ ) + { + v3f diff; + v3f c; + + v3_sub( points[ indices[i] ].co, center, diff ); + v3_cross( diff, ref, c ); + + angles[i] = + atan2f( v3_length(c), v3_dot( diff, ref ) ) + * (v3_dot( c, norm ) < 0.f ? -1.f: 1.f); + } + + // Temporary local indexes + u32 *temp_indices = (u32 *)alloca( sizeof(u32)*count ); + for( u32 i = 0; i < count; i ++ ) temp_indices[i] = i; + + // Slow sort on large vertex counts + int it = 0; + while(1) + { + int modified = 0; + for( int i = 0; i < count-1; i ++ ) + { + int s0 = i; int s1 = i + 1; + + if( angles[temp_indices[s0]] > angles[temp_indices[s1]] ) + { + // swap indices and mirror on local + u32 temp = indices[s1]; + indices[s1] = indices[s0]; + indices[s0] = temp; + + temp = temp_indices[s1]; + temp_indices[s1] = temp_indices[s0]; + temp_indices[s0] = temp; + + modified = 1; + } + } + + it ++; + if( !modified ) break; + } +} + +int solid_has_displacement( vdf_node *node ) +{ + int it = 0; + vdf_node *pSide; + + while( (pSide = vdf_next(node, "side", &it)) ) + { + if( vdf_next( pSide, "dispinfo", NULL ) ) + { + return 1; + } + } + return 0; +} + +void solid_disp_tri( vmf_solid *ctx, u32 a, u32 b, u32 c ) +{ + *((u32 *)csr_sb_use( ctx->indices )) = a; + *((u32 *)csr_sb_use( ctx->indices )) = b; + *((u32 *)csr_sb_use( ctx->indices )) = c; +} + +void face_add_indice( vmf_face *f, u32 idx ) +{ + f->indices = csr_sb_reserve( f->indices, 1, sizeof( u32 ) ); + *((u32 *)csr_sb_use( f->indices )) = idx; +} + +ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node ) +{ + ESolidResult flag = k_ESolidResult_valid; + + vmf_face faces[ SOLID_MAX_SIDES ]; + + int is_displacement = 0; + int num_planes = 0; + + // TODO: What is this for again? surely it should be the other way around... i think... + if( solid_has_displacement( node ) ) + { + printf( "solid_has_displacement\n" ); + num_planes = vmf_api.bisectors; + + // Add dummy stuff for globals + // ??? + for( int k = 0; k < vmf_api.bisectors; k ++ ) + { + vmf_face *dummy = faces + k; + dummy->indices = NULL; + dummy->dispinfo = NULL; + dummy->material = NULL; + } + + is_displacement = 1; + } + + int it = 0; + vdf_node *pSide; + while( (pSide = vdf_next(node, "side", &it)) ) + { + if( num_planes >= SOLID_MAX_SIDES ) + { + flag = k_ESolidResult_maxsides; + fprintf( stderr, "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES ); + break; + } + + double points[3*3]; + + vmf_face *face = faces + num_planes; + face->indices = NULL; + face->dispinfo = vdf_next( pSide, "dispinfo", NULL ); + face->material = kv_get( pSide, "material", "" ); + face->blacklisted = mat_blacklisted( face->material ); + + kv_double_array( pSide, "plane", 9, points ); + + tri_to_plane( points+6, points+3, points+0, vmf_api.planes + num_planes * 4 ); + num_planes ++; + } + + // Compute plane intersections + int i[3]; + csr_comb_init( 3, i ); + + v3f center = { 0.f, 0.f, 0.f }; + int numpoints = 0; + u32 vert_start = csr_sb_count( ctx->verts ); + + do + { + // DO something with i j k + double p[3]; + + if( (faces[ i[0] ].blacklisted && faces[ i[1] ].blacklisted && faces[ i[2] ].blacklisted) ) + continue; + + if( !plane_intersect( vmf_api.planes+i[0]*4, vmf_api.planes+i[1]*4, vmf_api.planes+i[2]*4, p ) ) + continue; + + // Check for illegal verts (eg: got clipped by bisectors) + int valid = 1; + for( int m = 0; m < num_planes; m ++ ) + { + if( plane_polarity( vmf_api.planes+m*4, p ) > 1e-6f ) + { + valid = 0; + break; + } + } + + if( valid ) + { + ctx->verts = csr_sb_reserve( ctx->verts, 3, sizeof( vmf_vert ) ); + + // Take the vertex position and add it for centering base on average + numpoints ++; + v3_add( (v3f){ p[0], p[1], p[2] }, center, center ); + + // Store point / respecive normal for each plane that triggered the collision + for( int k = 0; k < 3; k ++ ) + { + if( !faces[ i[k] ].blacklisted ) + { + u32 c = csr_sb_count( ctx->verts ); + + face_add_indice( faces + i[k], c ); + + v3d_v3f( p, ctx->verts[ c ].co ); + v3d_v3f( vmf_api.planes+i[k]*4, ctx->verts[ c ].nrm ); + + csr_sb_inc( ctx->verts, 1 ); + } + } + } + } + while( csr_comb( 3, num_planes, i ) ); + + // Retrospectively set the center for each point + v3_divs( center, (float)numpoints, center ); + for( ; vert_start < csr_sb_count( ctx->verts ); vert_start ++ ) + { + v2_copy( center, ctx->verts[ vert_start ].xy ); + } + + // Sort each faces and trianglulalate them + for( int k = vmf_api.bisectors; k < num_planes; k ++ ) + { + vmf_face *face = faces + k; + + if( face->blacklisted ) continue; + + if( csr_sb_count( face->indices ) < 3 ) + { + if( !vmf_api.bisectors ) + { + flag = k_ESolidResult_degenerate; + fprintf( stderr, "Skipping degenerate face\n" ); + } + continue; + } + + // Sort only if there is no displacements, or if this side is + if( !is_displacement || ( is_displacement && face->dispinfo ) ) + { + sort_coplanar( vmf_api.planes+k*4, ctx->verts, face->indices, csr_sb_count( face->indices ) ); + } + + if( is_displacement ) + { + // Compute displacement + if( face->dispinfo ) + { + if( csr_sb_count( face->indices ) != 4 ) + { + // Mute error if we have global planes cause they + // are of course gonna fuck things up here + if( !vmf_api.bisectors ) + { + flag = k_ESolidResult_degenerate; + fprintf( stderr, "Skipping degenerate displacement\n" ); + } + continue; + } + + // Match starting position + v3f start; + int sw = 0; + float dmin = 999999.f; + + vdf_node *dispinfo = face->dispinfo; + vdf_node *vdf_normals = vdf_next( dispinfo, "normals", NULL ); + vdf_node *vdf_distances = vdf_next( dispinfo, "distances", NULL ); + + kv_float_array( dispinfo, "startposition", 3, start ); + + for( int j = 0; j < csr_sb_count( face->indices ); j ++ ) + { + float d2 = v3_dist2( start, ctx->verts[ face->indices[ j ] ].co ); + if( d2 < dmin ) + { + dmin = d2; + sw = j; + } + } + + // Get corners of displacement + float *SW = ctx->verts[ face->indices[ sw ] ].co; + float *NW = ctx->verts[ face->indices[ (sw+1) % 4] ].co; + float *NE = ctx->verts[ face->indices[ (sw+2) % 4] ].co; + float *SE = ctx->verts[ face->indices[ (sw+3) % 4] ].co; + + // Can be either 5, 9, 17 + numpoints = pow( 2, kv_get_int( dispinfo, "power", 2 ) ) + 1; + u32 reqverts = numpoints*numpoints; + u32 reqidx = (numpoints-1)*(numpoints-1)*6; + + ctx->verts = csr_sb_reserve( ctx->verts, reqverts, sizeof( vmf_vert ) ); + ctx->indices = csr_sb_reserve( ctx->indices, reqidx, sizeof( u32 ) ); + + float normals[ 17*3 ]; + float distances[ 17 ]; + + // Calculate displacement positions + for( int j = 0; j < numpoints; j ++ ) + { + char key[14]; + sprintf( key, "row%i", j ); + + kv_float_array( vdf_normals, key, 17*3, normals ); + kv_float_array( vdf_distances, key, 17, distances ); + + float dx = (float)j / (float)(numpoints - 1); //Time values for linear interpolation + + for( int m = 0; m < numpoints; m ++ ) + { + vmf_vert *vert = &ctx->verts[ csr_sb_count( ctx->verts ) + j*numpoints + m ]; + + float dy = (float)m / (float)(numpoints - 1); + + v3f lwr; v3f upr; + + v3_lerp( SW, SE, dx, lwr ); + v3_lerp( NW, NE, dx, upr ); + v3_lerp( lwr, upr, dy, vert->co ); + + v3_muladds( vert->co, normals + m * 3, distances[ m ], vert->co ); + + // Todo, put correct normal + v3_copy( (v3f){ 0.f, 0.f, 1.f }, vert->nrm ); + } + } + + // Build displacement indices + int condition = 0; + for( int row = 0; row < numpoints - 1; row ++ ) + { + for( int col = 0; col < numpoints - 1; col ++ ) + { + u32 c = csr_sb_count( ctx->verts ); + + u32 idxsw = c + ( row + 0 ) * numpoints + col + 0 ; + u32 idxse = c + ( row + 0 ) * numpoints + col + 1 ; + u32 idxnw = c + ( row + 1 ) * numpoints + col + 0 ; + u32 idxne = c + ( row + 1 ) * numpoints + col + 1 ; + + if( (condition ++) % 2 == 0 ) + { + solid_disp_tri( ctx, idxne, idxnw, idxsw ); + solid_disp_tri( ctx, idxse, idxne, idxsw ); + } + else + { + solid_disp_tri( ctx, idxse, idxnw, idxsw ); + solid_disp_tri( ctx, idxse, idxne, idxnw ); + } + } + condition ++; + } + + csr_sb_inc( ctx->verts, numpoints*numpoints ); + } + } + else + { + u32 tris = csr_sb_count( face->indices ) -2; + ctx->indices = csr_sb_reserve( ctx->indices, tris*3, sizeof( u32 ) ); + + u32 c = csr_sb_count( ctx->indices ); + + for( int j = 0; j < tris; j ++ ) + { + ctx->indices[ c +j*3 +0 ] = face->indices[ 0 ]; + ctx->indices[ c +j*3 +1 ] = face->indices[ j+1 ]; + ctx->indices[ c +j*3 +2 ] = face->indices[ j+2 ]; + + // A 0,1,2:: A,B,C + // D B 0,2,3:: A,C,D + // C + } + + csr_sb_inc( ctx->indices, tris*3 ); + } + } + + // Free temp polyon buffers + for( int j = 0; j < num_planes; j ++ ) + { + csr_sb_free( faces[ j ].indices ); + } + + return flag; +} + +void solidgen_to_obj( vmf_solid *ctx, const char *path ) +{ + FILE *fp = fopen( path, "w" ); + + if( fp ) + { + fprintf( fp, "o vmf_export\n" ); + + vmf_vert *vert; + + // Write vertex block + for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ ) + { + vert = &ctx->verts[i]; + fprintf( fp, "v %f %f %f\n", vert->co[0], vert->co[1], vert->co[2] ); + } + + // Write normals block + for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ ) + { + vert = &ctx->verts[i]; + fprintf( fp, "vn %f %f %f\n", vert->nrm[0], vert->nrm[1], vert->nrm[2] ); + } + + fprintf( fp, "s off\n" ); + + // Indices + for( int i = 0; i < csr_sb_count( ctx->indices )/3; i ++ ) + { + u32 * base = ctx->indices + i*3; + fprintf( fp, "f %u//%u %u//%u %u//%u\n", + base[2]+1, base[2]+1, + base[1]+1, base[1]+1, + base[0]+1, base[0]+1 + ); + } + + fclose( fp ); + } + else + { + fprintf( stderr, "Could not open %s for writing\n", path ); + } +} diff --git a/vpk.h b/vpk.h new file mode 100644 index 0000000..73ff7cb --- /dev/null +++ b/vpk.h @@ -0,0 +1,88 @@ +// VPK +//======================================================================================================================= + +#pragma pack(push, 1) +typedef struct +{ + u32 Signature; + u32 Version; + u32 TreeSize; + u32 FileDataSectionSize; + u32 ArchiveMD5SectionSize; + u32 OtherMD5SectionSize; + u32 SignatureSectionSize; +} +VPKHeader; + +typedef struct +{ + u32 CRC; + u16 PreloadBytes; + u16 ArchiveIndex; + u32 EntryOffset; + u32 EntryLength; + u16 Terminator; +} +VPKDirectoryEntry; +#pragma pack(pop) + +void vpk_free( VPKHeader *self ) +{ + free( self ); +} + +VPKDirectoryEntry *vpk_find( VPKHeader *self, const char *asset ) +{ + if( !self ) + return NULL; + + char wbuf[ 512 ]; + strcpy( wbuf, asset ); + + char *ext = csr_findext( wbuf, '.' ); + *(ext-1) = 0x00; + char *fn = csr_findext( wbuf, '/' ); + *(fn-1) = 0x00; + char *dir = wbuf; + + char *pCur = ((char *)self) + sizeof( VPKHeader ); + + while( 1 ) + { + if( !*pCur ) break; + + int bExt = !strcmp( ext, pCur ); + + while( *( pCur ++ ) ) {}; + while( 1 ) + { + if( !*pCur ) { pCur ++; break; } + + int bDir = !strcmp( dir, pCur ); + + while( *( pCur ++ ) ) {}; + while( 1 ) + { + if( !*pCur ) { pCur ++; break; } + + const char *vpk_fn = pCur; + + while( *( pCur ++ ) ) {}; + VPKDirectoryEntry *entry = (VPKDirectoryEntry *)pCur; + + if( !strcmp( vpk_fn, fn ) && bExt && bDir ) + { + return entry; + } + + pCur += entry->PreloadBytes + sizeof( VPKDirectoryEntry ); + } + + if( bDir && bExt ) return NULL; + } + + if( bExt ) return NULL; + } + + return NULL; +}