--- /dev/null
+*.h linguist-language=C
+*.vs linguist-language=GLSL
+*.fs linguist-language=GLSL
+*.gls linguist-language=GLSL
--- /dev/null
+# 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
--- /dev/null
+#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 );
+ }
+}
--- /dev/null
+ // 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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+#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
--- /dev/null
+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];
--- /dev/null
+int csr_run( csr_opts *context )
+{
+ context->x;
+ context->y;
+
+ context->etc;
+}
--- /dev/null
+#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 );
+}
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+#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 );
+ }
+}
--- /dev/null
+// 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;
+}