From a97099abba0a239e20929f04ece9d6839c96ac14 Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 9 Jul 2021 12:28:39 +0100 Subject: [PATCH] init --- .gitattributes | 4 + .gitignore | 23 ++ csRadar.c | 105 +++++++ csr32f.h | 27 ++ csrComb.h | 39 +++ csrDraw.h | 88 ++++++ csrIO.h | 162 ++++++++++ csrMath.h | 418 +++++++++++++++++++++++++ csrMem.h | 90 ++++++ csrTypes.h | 15 + geometry_buffers.c | 7 + plugins/geometry_buffers.c | 0 vdf.h | 602 +++++++++++++++++++++++++++++++++++++ vfilesys.h | 180 +++++++++++ vmdl.h | 579 +++++++++++++++++++++++++++++++++++ vmf.h | 580 +++++++++++++++++++++++++++++++++++ vpk.h | 88 ++++++ 17 files changed, 3007 insertions(+) create mode 100644 .gitattributes create mode 100755 .gitignore create mode 100644 csRadar.c create mode 100644 csr32f.h create mode 100644 csrComb.h create mode 100644 csrDraw.h create mode 100644 csrIO.h create mode 100644 csrMath.h create mode 100644 csrMem.h create mode 100644 csrTypes.h create mode 100644 geometry_buffers.c create mode 100644 plugins/geometry_buffers.c create mode 100644 vdf.h create mode 100644 vfilesys.h create mode 100644 vmdl.h create mode 100644 vmf.h create mode 100644 vpk.h 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; +} -- 2.25.1