#include <string.h>
#include <math.h>
#include <time.h>
+#include <stdarg.h>
// CSR lib
+#include "csrLog.h"
#include "csrOpt.h"
#include "csrTypes.h"
#include "csrMath.h"
char output_path[ 512 ]; // Full path eg. /home/harry/my_map.vmf
char vmf_name[ 128 ]; // Just the base name eg. my_map
int output_set = 0;
+ EMSAA sampling_mode = k_EMSAA_RGSS;
while( csr_argp( argc, argv ) )
{
{
if( num_strings == 20 )
{
- fprintf( stderr, "Too many arguments! Max 20\n" );
- fs_exit();
- exit(0);
+ log_error( "Too many arguments! Max 20\n" );
+ goto IL_CSR_EXIT;
}
- printf( "got: %s\n", arg );
-
strings[ num_strings ++ ] = arg;
}
{
padding = atof( arg );
}
+
+ if( (arg = csr_long_opt_arg( "multi-sample" )) )
+ {
+ if( !strcmp( arg, "none" ))
+ {
+ sampling_mode = k_EMSAA_none;
+ }
+ else if( !strcmp( arg, "rgss" ))
+ {
+ sampling_mode = k_EMSAA_RGSS;
+ }
+ else if( !strcmp( arg, "2x" ))
+ {
+ sampling_mode = k_EMSAA_2x2;
+ }
+ else if( !strcmp( arg, "8r" ))
+ {
+ sampling_mode = k_EMSAA_8R;
+ }
+ else
+ {
+ log_error( "Invalid sampling pattern '%s'\n", arg );
+ goto IL_CSR_EXIT;
+ }
+ }
if( csr_opt( 'v' ) || csr_long_opt( "version" ) )
{
printf( "csRadar version: " CSR_VERSION "\n" );
- return 0;
+ goto IL_CSR_EXIT;
}
if( csr_opt( 'h' ) || csr_long_opt( "help" ) )
" -r 1024 Output resolution\n"
" -o <output> Specify output name/path\n"
" --padding=128 When cropping radar, add padding units to border\n"
- " --standard-layers Use standard TAR layers/groups\n"
+ //" --standard-layers Use standard TAR layers/groups\n"
" --no-txt Don't create matching radar txt\n"
- " --multi-sample= [ none, 2, 4, 4r, 8kn (default), 16c ]\n"
+ " --multi-sample=RGSS [ none, 2x, rgss, 8r ]\n"
+ " --extension=TAR Use an extension binary instead\n"
"\n"
" -v --version Display program version\n"
" -h --help Display this help text\n"
);
- return 0;
+ goto IL_CSR_EXIT;
}
}
if( num_strings )
{
- vmf_map *map = vmf_init( strings[0], 1 );
+ vmf_map *map = vmf_init( strings[0] );
if( map )
{
strcpy( vmf_name, base_name );
- printf( "output_path: '%s'\nvmf_name: '%s'\n", output_path, vmf_name );
-
+ log_info( "output_path: '%s'\n", output_path );
+ log_info( "vmf_name: '%s'\n", vmf_name );
- // Main
-
+ // Main
csr_target target;
- csr_create_target( &target, resolution, resolution );
+ csr_create_target( &target, resolution, resolution, sampling_mode );
csr_rt_clear( &target );
+ csr_use_program( &target, frag_gbuffer );
+
// Compute bounds
csr_filter filter =
{
}
}
- filter.compute_bounds_only = 0;
- csr_auto_fit( &target, padding );
+ if( i == 0 )
+ {
+ filter.compute_bounds_only = 0;
+ csr_auto_fit( &target, padding );
+ vmf_load_models( map );
+ }
}
if( write_txt )
}
else
{
- fprintf( stderr, "Could not load VMF\n" );
+ log_error( "Could not load VMF\n" );
}
}
else
{
- fprintf( stderr, "Missing required argument: mapfile\n" );
+ log_error( "Missing required argument: mapfile\n" );
}
+IL_CSR_EXIT:
fs_exit();
-
return 0;
}
-#ifndef CSR_COMB_H
-#define CSR_COMB_H
-
void csr_comb_init( int const M, int p[] )
{
for( int i = 0; i < M; i ++ )
}
return 0;
}
-
-void csr_comb_print( int M, int p[] )
-{
- for( int i = 0; i < M; i ++ )
- {
- printf( "%d ", p[i] );
- }
-
- printf( "\n" );
-}
-
-#endif
typedef struct csr_frag csr_frag;
typedef struct csr_target csr_target;
typedef struct csr_filter csr_filter;
+typedef enum EMSAA EMSAA;
+
+typedef void (* csr_frag_shader)( v4f, vmf_vert[3], float, float, float );
// MSAA patterns
v2f csr_msaa_1[] =
{0.f, 0.f}
};
+// XX
+// XX
v2f csr_msaa_2x2[] =
{
- { 0.25f, 0.25f },
- { 0.25f, -0.25f },
- { -0.25f, -0.25f },
- { -0.25f, 0.25f }
+ { 0x0.4p0f, 0x0.4p0f },
+ { 0x0.4p0f, -0x0.4p0f },
+ { -0x0.4p0f, -0x0.4p0f },
+ { -0x0.4p0f, 0x0.4p0f }
};
+// X
+// X
+// X
+// X
v2f csr_msaa_2x2rgss[] =
{
+ { 0x0.2p0f, 0x0.6p0f },
+ { -0x0.6p0f, 0x0.2p0f },
+ { -0x0.2p0f, -0x0.6p0f },
+ { 0x0.6p0f, -0x0.2p0f }
+};
+// X
+// X
+// X
+// X
+// X
+// X
+// X
+// X
+v2f csr_msaa_8rook[] =
+{
+ { 0x0.1p0f, 0x0.7p0f },
+ { 0x0.5p0f, 0x0.1p0f },
+ { 0x0.7p0f, -0x0.3p0f },
+ { 0x0.3p0f, -0x0.5p0f },
+ { -0x0.1p0f, -0x0.7p0f },
+ { -0x0.5p0f, -0x0.1p0f },
+ { -0x0.7p0f, 0x0.3p0f },
+ { -0x0.3p0f, 0x0.5p0f }
};
struct csr_frag
{
- v3f co;
- v3f nrm;
-
+ v4f colour;
float depth;
};
boxf bounds;
float scale;
- v2f subsamples[ 16 ];
+ v2f subsamples[ 8 ];
int num_samples;
+ v2f *sample_src;
+
+ csr_frag_shader shader;
};
+void csr_use_program( csr_target *rt, csr_frag_shader shader )
+{
+ rt->shader = shader;
+}
+
struct csr_filter
{
const char *visgroup; // Limit to this visgroup only
int compute_bounds_only;
};
-void csr_create_target( csr_target *rt, u32 x, u32 y )
+enum EMSAA
+{
+ k_EMSAA_none,
+ k_EMSAA_2x2,
+ k_EMSAA_RGSS,
+ k_EMSAA_8R
+};
+
+void csr_create_target( csr_target *rt, u32 x, u32 y, EMSAA aa )
{
rt->x = x;
rt->y = y;
- rt->num_samples = 4;
+
+ switch( aa )
+ {
+ default:
+ case k_EMSAA_none:
+ rt->num_samples = 1;
+ rt->sample_src = csr_msaa_1;
+ break;
+
+ case k_EMSAA_2x2:
+ rt->num_samples = 4;
+ rt->sample_src = csr_msaa_2x2;
+ break;
+
+ case k_EMSAA_RGSS:
+ rt->num_samples = 4;
+ rt->sample_src = csr_msaa_2x2rgss;
+ break;
+
+ case k_EMSAA_8R:
+ rt->num_samples = 8;
+ rt->sample_src = csr_msaa_8rook;
+ break;
+ }
rt->fragments = (csr_frag *)csr_malloc( x*y*sizeof(csr_frag)*rt->num_samples );
v2f pixel_size = { range_x/(float)rt->x, range_y/(float)rt->y };
- rt->subsamples[0][0] = pixel_size[0] * -0.25f;
- rt->subsamples[0][1] = 0.f;
- rt->subsamples[1][0] = pixel_size[0] * 0.75f;
- rt->subsamples[1][1] = pixel_size[1] * 0.25f;
- rt->subsamples[2][0] = 0.f;
- rt->subsamples[2][1] = pixel_size[1] * 0.5f;
- rt->subsamples[3][0] = pixel_size[0] * 0.5f;
- rt->subsamples[3][1] = pixel_size[1] * 0.75f;
+ for( int i = 0; i < rt->num_samples; i ++ )
+ {
+ v2_mul( rt->sample_src[i], pixel_size, rt->subsamples[i] );
+ }
}
void csr_rt_free( csr_target *rt )
{
for( u32 i = 0; i < rt->x*rt->y*rt->num_samples; i ++ )
{
- v3_zero( rt->fragments[ i ].co );
- v3_zero( rt->fragments[ i ].nrm );
+ v4_zero( rt->fragments[ i ].colour );
rt->fragments[i].depth = 0.f;
}
}
void csr_auto_fit( csr_target *rt, float padding )
{
// Correct aspect ratio to be square
- float dx, dy, d, l, cx, cy;
+ float dx, dy, l, cx, cy;
dx = rt->bounds[1][0] - rt->bounds[0][0];
dy = rt->bounds[1][1] - rt->bounds[0][1];
fclose( write_ptr );
}
+void frag_gbuffer( v4f frag_colour, vmf_vert tri[3], float bca, float bcb, float bcc )
+{
+ v3_muls( tri[0].co, bca, frag_colour );
+ v3_muladds( frag_colour, tri[1].co, bcb, frag_colour );
+ v3_muladds( frag_colour, tri[2].co, bcc, frag_colour );
+}
+
void simple_raster( csr_target *rt, vmf_vert tri[3] )
{
// Very very simplified rasterizing algorithm
if( hit > frag[i].depth )
{
frag[i].depth = hit;
- v3_muls( tri[0].co, bca, frag[i].co );
- v3_muladds( frag[i].co, tri[1].co, bcb, frag[i].co );
- v3_muladds( frag[i].co, tri[2].co, bcc, frag[i].co );
-
- // TODO: Same for normal map
+ rt->shader( frag[i].colour, tri, bca, bcb, bcc );
}
}
}
if( compute_bounds_only )
{
- box_copy( mdl->bounds, trf_bounds );
- m4x3_transform_aabb( model, trf_bounds );
-
- // Join
- box_concat( rt->bounds, trf_bounds );
+ map->models[ ent->user1 ].need_load = 1;
+ m4x3_expand_aabb_point( model, rt->bounds, (v3f){0.f,0.f,0.f} );
}
else
{
float *image = (float *)csr_malloc( 1024*1024*sizeof(float)*3 );
+ float contrib = 1.f/(float)rt->num_samples;
+
for( int l = 0; l < rt->x; l ++ )
{
for( int x = 0; x < rt->y; x ++ )
csr_frag *src = &rt->fragments[ ((1023-l)*1024+x)*rt->num_samples ];
v3_zero( dst );
- v3_muls( src[0].co, 1.f/(float)rt->num_samples, dst );
- v3_muladds( dst, src[1].co, 1.f/(float)rt->num_samples, dst );
- v3_muladds( dst, src[2].co, 1.f/(float)rt->num_samples, dst );
- v3_muladds( dst, src[3].co, 1.f/(float)rt->num_samples, dst );
+ v3_muls( src[0].colour, contrib, dst );
+
+ for( int j = 1; j < rt->num_samples; j ++ )
+ {
+ v3_muladds( dst, src[j].colour, contrib, dst );
+ }
}
}
--- /dev/null
+// Logging with colour
+// ======================================================================================
+
+#define KNRM "\x1B[0m"
+#define KRED "\x1B[31m"
+#define KGRN "\x1B[32m"
+#define KYEL "\x1B[33m"
+#define KBLU "\x1B[34m"
+#define KMAG "\x1B[35m"
+#define KCYN "\x1B[36m"
+#define KWHT "\x1B[37m"
+
+#define LOG_TYPE_DEV 0
+#define LOG_TYPE_INFO 1
+#define LOG_TYPE_ALLOC 2
+#define LOG_TYPE_FREE 3
+#define LOG_TYPE_SUCCESS 4
+#define LOG_TYPE_ERROR 5
+#define LOG_TYPE_WARN 6
+
+void(*CSR_LOG_REDIR_STDOUT)(int, const char*) = NULL;
+int CSR_LOG_ENABLE_STDOUT = 1;
+
+void csr_log_out( FILE *f, int type, const char* prefix, const char* fmt, ... )
+{
+ if( CSR_LOG_ENABLE_STDOUT )
+ {
+ fprintf( f, "%s", prefix );
+ va_list args;
+ va_start( args, fmt );
+ vfprintf( f, fmt, args );
+ va_end( args );
+ fprintf( f, KNRM ); // reset
+ }
+
+ // Send to a seperate logging function
+ if( CSR_LOG_REDIR_STDOUT )
+ {
+ char buffer[ 512 ];
+ va_list args;
+ va_start( args, fmt );
+ vsnprintf( buffer, 512, fmt, args );
+ va_end( args );
+ CSR_LOG_REDIR_STDOUT( type, buffer );
+ }
+}
+
+#define log_dev(FMT, ...) csr_log_out( stdout, LOG_TYPE_DEV, (KNRM " dev" KWHT "| " KNRM), FMT, ##__VA_ARGS__ )
+#define log_info(FMT, ...) csr_log_out( stdout, LOG_TYPE_INFO, (KNRM " info" KWHT "| " KNRM), FMT, ##__VA_ARGS__ )
+#define log_alloc(FMT, ...) csr_log_out( stdout, LOG_TYPE_ALLOC, (KCYN " alloc" KWHT "| " KNRM), FMT, ##__VA_ARGS__ )
+#define log_free(FMT, ...) csr_log_out( stdout, LOG_TYPE_FREE, (KMAG " free" KWHT "| " KNRM), FMT, ##__VA_ARGS__ )
+#define log_success(FMT, ...) csr_log_out( stdout, LOG_TYPE_SUCCESS, (KGRN "success" KWHT "| " KGRN), FMT, ##__VA_ARGS__ )
+#define log_error(FMT, ...) csr_log_out( stderr, LOG_TYPE_ERROR, (KRED " error" KWHT "| " KRED), FMT, ##__VA_ARGS__ )
+#define log_warn(FMT, ...) csr_log_out( stdout, LOG_TYPE_WARN, (KYEL " warn" KWHT "| " KYEL), FMT, ##__VA_ARGS__ )
+
+#define log_init log_alloc
+#define log_dealloc log_free
d[0] = a[0]+b[0]; d[1] = a[1]+b[1];
}
+void v2_muls( v2f a, float s, v2f d )
+{
+ d[0] = a[0]*s; d[1] = a[1]*s;
+}
+
+void v2_mul( v2f a, v2f b, v2f d )
+{
+ d[0] = a[0]*b[0]; d[1] = a[1]*b[1];
+}
+
// Vector 3
// ==================================================================================================================
}
// Vector 4
+// ==================================================================================================================
+
void v4_copy( v4f a, v4f b )
{
b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];
}
+void v4_zero( v4f a )
+{
+ a[0] = 0.f; a[1] = 0.f; a[2] = 0.f; a[3] = 0.f;
+}
+
// Matrix 3x3
//======================================================================================================
if( *cur != '-' )
{
- fprintf( stderr, "Unknown opt '-%c'\n", *cur );
+ log_error( "Unknown opt '-%c'\n", *cur );
}
else
{
- fprintf( stderr, "Unknown opt '--%s'\n", cur + 1 );
+ log_error( "Unknown opt '--%s'\n", cur + 1 );
}
exit(0);
}
}
- fprintf( stderr, "Option '%c' requires argument!\n", c );
+ log_error( "Option '%c' requires argument!\n", c );
exit(0);
}
}
else
{
- fprintf( stderr, "Long option '%s' requires argument\n", name );
+ log_error( "Long option '%s' requires argument\n", name );
}
}
}
-./csRadar cs_apollo.vmf -g "/home/harry/SteamLibrary/steamapps/common/Counter-Strike Global Offensive/csgo/gameinfo.txt"
+./csRadar cs_apollo.vmf -g "/home/harry/SteamLibrary/steamapps/common/Counter-Strike Global Offensive/csgo/gameinfo.txt" tar_layout --multi-sample=rgss
if( vdf_line_control( ctx ) )
{
- fprintf( stderr, "Unexpected end of line character (Line: %u)\n", ctx->lines );
+ log_error( "Unexpected end of line character (Line: %u)\n", ctx->lines );
return;
}
{
if( ctx->st.tokens[0] || !ctx->st.expect_decl )
{
- fprintf( stderr, "Unexpected token '{' (Line: %u)\n", ctx->lines );
+ log_error( "Unexpected token '{' (Line: %u)\n", ctx->lines );
ctx->errors ++;
}
{
if( !ctx->st.pnode->parent )
{
- fprintf( stderr, "Unexpected token '}' (Line: %u)\n", ctx->lines );
+ log_error( "Unexpected token '}' (Line: %u)\n", ctx->lines );
ctx->errors ++;
}
else
if( ctx->st.expect_decl )
{
- fprintf( stderr, "Unexpected token '%s' (Line: %u)\n", ctx->name, ctx->lines );
+ log_error( "Unexpected token '%s' (Line: %u)\n", ctx->name, ctx->lines );
ctx->errors ++;
}
}
if( !text_src )
{
- fprintf( stderr, "vdf open failed\n" );
return 0;
}
if( !fs->vpk )
{
- fprintf( stderr, "Could not locate pak01_dir.vpk in %i searchpaths. Stock models will not load!", csr_sb_count( fs->searchpaths ) );
+ log_error( "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 );
+ log_info( "fs_info:\n" );
+ log_info( " gamedir: %s\n", fs->gamedir );
+ log_info( " exedir: %s\n", fs->exedir );
+ log_info( " bin: %s\n", fs->bindir );
+ log_info( " pack: %s\n", pack_path );
+ log_info( " searchpaths:\n" );
for( int i = 0; i < csr_sb_count( fs->searchpaths ); i ++ )
{
- printf( " %s\n", fs->searchpaths[i] );
+ log_info( " '%s'\n", fs->searchpaths[i] );
}
}
if( !fs->current_archive )
{
- fprintf( stderr, "Could not locate %s\n", pak );
+ log_error( "Could not locate %s\n", pak );
return NULL;
}
}
u32 hash;
mdl_mesh_t mdl;
+
+ int need_load;
}
*models;
if( num_planes >= SOLID_MAX_SIDES )
{
flag = k_ESolidResult_maxsides;
- fprintf( stderr, "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES );
+ log_error( "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES );
break;
}
if( csr_sb_count( face->indices ) < 3 )
{
flag = k_ESolidResult_degenerate;
- fprintf( stderr, "Skipping degenerate face\n" );
+ log_error( "Skipping degenerate face\n" );
continue;
}
if( csr_sb_count( face->indices ) != 4 )
{
flag = k_ESolidResult_degenerate;
- fprintf( stderr, "Skipping degenerate displacement\n" );
+ log_error( "Skipping degenerate displacement\n" );
continue;
}
map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
struct vmf_model *entry = &map->models[ csr_sb_count( map->models ) ];
+ entry->need_load = 0;
+ mdl_error( &entry->mdl ); // (just setting arrays to 0)
+
entry->str = csr_malloc( strlen( model_path ) +1 );
strcpy( entry->str, model_path );
entry->hash = djb2( (const unsigned char *)model_path );
}
// Load all models
-void vmf_load_models( vmf_map *map )
+void vmf_index_models( vmf_map *map )
{
- printf( "Loading all models\n" );
+ log_info( "Indexing all models\n" );
- // Error model. TODO: Maybe don't have this be junk data.
+ // Error model == blank
map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
csr_sb_use( map->models );
mdl_error( &map->models[0].mdl );
vmf_populate_models( map->cache[i].root, map );
}
- printf( "Indexed (%u) models\n", csr_sb_count( map->models )-1 );
-
+ log_info( "Indexed (%u) models\n", csr_sb_count( map->models )-1 );
+}
+
+void vmf_load_models( vmf_map *map )
+{
u32 num_success = 0;
+ u32 num_reqs = 0;
- // Load model data
- // TODO: Make nice loading bar
for( int i = 1; i < csr_sb_count( map->models ); i ++ )
{
struct vmf_model *mdl = &map->models[i];
- if( mdl_from_find_files( mdl->str, &mdl->mdl ) )
- {
- num_success ++;
- }
- else
+ if( mdl->need_load )
{
- fprintf( stderr, "Failed to load model: %s\n", mdl->str );
+ num_reqs ++;
+
+ if( mdl_from_find_files( mdl->str, &mdl->mdl ) )
+ {
+ num_success ++;
+ }
+ else
+ {
+ log_warn( "Failed to load model: %s\n", mdl->str );
+ }
}
}
- printf( "Done (%u of %u loaded)\n", num_success, csr_sb_count( map->models )-1 );
+ log_info( "Done (%u of %u loaded)\n", num_success, num_reqs );
}
u32 vmf_init_subvmf( vmf_map *map, const char *subvmf );
{
if( !strcmp( map->cache[i].name, subvmf ) )
{
- return i+1;
+ if( map->cache[i].root )
+ return i+1;
+ else
+ return 0;
}
}
}
- printf( "Loading subvmf: %s\n", subvmf );
-
id = csr_sb_count( map->cache );
map->cache = csr_sb_reserve( map->cache, 1, sizeof( struct vmf_instance ));
struct vmf_instance *inst = &map->cache[ id ];
+ csr_sb_use( map->cache );
+ inst->hash = hash;
+ inst->name = csr_malloc( strlen( subvmf )+1 );
+ strcpy( inst->name, subvmf );
+
if( (inst->root = vdf_open_file( subvmf )) )
- {
- csr_sb_use( map->cache );
-
- inst->hash = hash;
- inst->name = csr_malloc( strlen( subvmf )+1 );
- strcpy( inst->name, subvmf );
-
+ {
// Recursive load other instances
- vmf_load_all_instances( map, inst->root );
-
+ vmf_load_all_instances( map, inst->root );
return id+1;
}
else
{
- fprintf( stderr, "Failed to load instance file\n" );
+ log_error( "Failed to load instance file: %s\n", subvmf );
return 0;
}
}
-vmf_map *vmf_init( const char *path, int load_models )
+vmf_map *vmf_init( const char *path )
{
vmf_map *map = csr_calloc( sizeof( vmf_map ) );
map->root = vdf_open_file( path );
vmf_load_all_instances( map, map->root );
// Other resources
- if( load_models )
- {
- vmf_load_models( map );
- }
-
+ vmf_index_models( map );
return map;
}
{
for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
{
- vdf_free_r( map->cache[i].root );
+ if( map->cache[i].root )
+ vdf_free_r( map->cache[i].root );
+
free( map->cache[i].name );
}
free( map );
}
-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 );
- }
-}
-
void vmf_entity_transform( vdf_node *ent, m4x3f mat )
{
v3f angles = {0.f,0.f,0.f};