init
authorhgn <hgodden00@gmail.com>
Fri, 9 Jul 2021 11:28:39 +0000 (12:28 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 9 Jul 2021 11:28:39 +0000 (12:28 +0100)
17 files changed:
.gitattributes [new file with mode: 0644]
.gitignore [new file with mode: 0755]
csRadar.c [new file with mode: 0644]
csr32f.h [new file with mode: 0644]
csrComb.h [new file with mode: 0644]
csrDraw.h [new file with mode: 0644]
csrIO.h [new file with mode: 0644]
csrMath.h [new file with mode: 0644]
csrMem.h [new file with mode: 0644]
csrTypes.h [new file with mode: 0644]
geometry_buffers.c [new file with mode: 0644]
plugins/geometry_buffers.c [new file with mode: 0644]
vdf.h [new file with mode: 0644]
vfilesys.h [new file with mode: 0644]
vmdl.h [new file with mode: 0644]
vmf.h [new file with mode: 0644]
vpk.h [new file with mode: 0644]

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..c70091b
--- /dev/null
@@ -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 (executable)
index 0000000..5431199
--- /dev/null
@@ -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 (file)
index 0000000..ae383f7
--- /dev/null
+++ b/csRadar.c
@@ -0,0 +1,105 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+// 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 (file)
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 (file)
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 (file)
index 0000000..e0635bb
--- /dev/null
+++ b/csrDraw.h
@@ -0,0 +1,88 @@
+#include <time.h>
+
+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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..e65ad99
--- /dev/null
@@ -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 (file)
index 0000000..d3b18b6
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/vdf.h b/vdf.h
new file mode 100644 (file)
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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+
+// 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 (file)
index 0000000..2f33f83
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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;
+}