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;
+}