tga & normals
authorhgn <hgodden00@gmail.com>
Fri, 16 Jul 2021 20:10:28 +0000 (21:10 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 16 Jul 2021 20:10:28 +0000 (21:10 +0100)
build.sh
csRadar.c
csRadar.h
csrDraw.h
csrMath.h
csrTga.h [new file with mode: 0644]
ext_csr_free.c
vmf.h

index 51594999c49e5c125ec3ad0bc995405ea096b801..a898a6872cb8a2f462cbd2ea26d08e7c45eef2f4 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -1,4 +1,3 @@
-
 flags="-ggdb3 -Wall -fsanitize=address"
 
 while (( "$#" )); do
index 11253739c13bab5f3b4ec1ea829f63120420e625..9f05c2b02aaafd6fc105a6182dd82e5b1e13230f 100644 (file)
--- a/csRadar.c
+++ b/csRadar.c
@@ -1,9 +1,15 @@
+// Copyright (C) 2021 Harry Godden (hgn)
+
+// Building:
+//  gcc -rdynamic csRadar.c -o csRadar -lm -ldl
+//
+// Plugins:
+//  gcc -fpic -shared -o ext/my_plugin.so my_plugin.c -lm
+
 #define VALVE_IMPLEMENTATION
 #define CSR_EXECUTABLE
 #include "csRadar.h"
 
-// gcc -Wall -fsanitize=address csRadar.c -o csRadar -lm
-
 int main( int argc, char *argv[] )
 {
        csr_api api = 
@@ -17,6 +23,7 @@ int main( int argc, char *argv[] )
 
        int output_set = 0;
        char *extension = NULL;
+       char *gameinfo = NULL;
 
        char *arg;
        while( csr_argp( argc, argv ) )
@@ -29,7 +36,7 @@ int main( int argc, char *argv[] )
                                goto IL_CSR_EXIT;
                        }
                        
-                       api.strings[ api.num_strings ++ ] = arg;
+                       api.strings[ api.num_strings ++ ].str = arg;
                }
                
                if( (arg = csr_opt_arg( 'o' )) )
@@ -42,7 +49,7 @@ int main( int argc, char *argv[] )
                
                if( (arg = csr_opt_arg( 'g' )) )
                {
-                       fs_set_gameinfo( arg );
+                       gameinfo = arg;
                }
                
                if( (arg = csr_opt_arg( 'r' )) )
@@ -108,10 +115,9 @@ int main( int argc, char *argv[] )
                                "   -r 1024                        Output resolution\n"
                                "   -o <output>                    Specify output name/path\n"
                                "   -e <classname>                 Same as default arg, but instead filters for entity class\n"
+                               //"   -s <height>                    Add a vertical split at this height\n"
                                "   --padding=128                  When cropping radar, add padding units to border\n"
                                //"   --standard-layers              Use standard TAR layers/groups\n"
-                               "   --write-normals                Enable normals as an output stream\n"
-                               "   --write-origins                Enable entity origins as an output stream\n"
                                "   --no-txt                       Don't create matching radar txt\n"
                                "   --multi-sample=RGSS            [ none, 2x, rgss, 8r ]\n"
                                "   --extension=TAR                Use an extension binary instead\n"
@@ -126,10 +132,15 @@ int main( int argc, char *argv[] )
        
        if( api.num_strings )
        {
+               if( gameinfo )
+               {
+                       fs_set_gameinfo( gameinfo );
+               }
+       
                // Path handling
                if( !output_set )
                {
-                       strcpy( api.output_path, api.strings[0] );
+                       strcpy( api.output_path, api.strings[0].str );
                        csr_stripext( api.output_path );
                }
                
@@ -144,9 +155,19 @@ int main( int argc, char *argv[] )
                log_info( "output_path: '%s'\n", api.output_path );
                log_info( "vmf_name: '%s'\n", api.vmf_name );
 
-               api.map = vmf_init( api.strings[0] );
+               api.map = vmf_init( api.strings[0].str );
                if( api.map )
                {
+                       // Update arg inferred types
+                       api.strings[0].type = k_iftype_vmf;
+                       for( int i = 1; i < api.num_strings; i ++ )
+                       {
+                               if( vmf_visgroup_id( api.map->root, api.strings[i].str ) != -1 )
+                                       api.strings[i].type = k_iftype_visgroup;
+                               else
+                                       api.strings[i].type = k_iftype_classname;
+                       }
+               
                        if( !extension )
                                extension = "csRadarFree";
                
index 36c00f86412a5c24302d8277eddd3f95eea4ec83..4192e555ff13a5f9343d11a210099e9d59c42459 100644 (file)
--- a/csRadar.h
+++ b/csRadar.h
@@ -27,7 +27,7 @@
 // Drawing
 #ifdef CSR_EXECUTABLE
  #include "csr32f.h"
-//#include "csrTga.h"
+ #include "csrTga.h"
 #endif
 
 #include "csrDraw.h"                   // Y
@@ -36,10 +36,25 @@ static const u32 csr_build = 3;
 static const u32 csr_api_version = 1;
 
 typedef struct csr_api csr_api;
+typedef struct csr_opt_str csr_opt_str;
+
 struct csr_api
 {
        // Floating arguments from main's argv
-       char *strings[ 20 ];
+       struct csr_opt_str
+       {
+               char *str;
+               
+               enum inferred_type
+               {
+                       k_iftype_vmf,
+                       k_iftype_visgroup,
+                       k_iftype_classname
+               }
+               type;
+       }
+       strings[ 20 ];
+       
        int num_strings;
        
        float padding;
@@ -49,9 +64,6 @@ struct csr_api
        char vmf_name[ 128 ];                   // Just the base name eg. my_map
        EMSAA sampling_mode;
        
-       int write_normals;
-       int write_origins;
-       
        // Main API interface
        vmf_map *map;
        csr_target target;
@@ -59,6 +71,24 @@ struct csr_api
        u32 api_version;
 };
 
+void csr_filter_update_from_strings( csr_filter *filter, csr_opt_str *opt );
+
+#ifdef CSR_EXECUTABLE
+void csr_filter_update_from_strings( csr_filter *filter, csr_opt_str *opt )
+{
+       if( opt->type == k_iftype_visgroup )
+       {
+               filter->visgroup = opt->str;
+               filter->classname = NULL;
+       }
+       else
+       {
+               filter->classname = opt->str;
+               filter->visgroup = NULL;
+       }
+}
+#endif
+
 #ifndef CSR_EXECUTABLE
 static int csr_init( csr_api *api )
 {
index 97fd8a54553966b80b7f94ac210c6e4001ef5857..b744c044e9058ed379c23004bac8d05286bf41c4 100644 (file)
--- a/csrDraw.h
+++ b/csrDraw.h
@@ -44,6 +44,9 @@ void csr_draw( csr_target *rt, vmf_vert *triangles, u32 triangle_count, m4x3f tr
 // Filter is optional, it can be st to NULL to just render everything.
 void csr_vmf_render( csr_target *rt, vmf_map *map, vdf_node *root, csr_filter *filter, m4x3f prev, m4x3f inst );
 
+
+void csr_rt_save_tga( csr_target *rt, const char *path, u32 offset, u32 nc );
+
 // Obsolete
 void csr_rt_save_buffers( csr_target *rt, const char *basename, const char *subname );
 
@@ -284,7 +287,7 @@ void simple_raster( csr_target *rt, vmf_vert tri[3] )
                
                for( u32 px = start_x; px <= end_x; px ++ )
                {
-                       u32 sample_index = (py * rt->y + px) * rt->num_samples;
+                       u32 sample_index = ((rt->y-py-1)*rt->x+px) * rt->num_samples;
                
                        void *frag = rt->colour + sample_index*rt->shader->stride;
                        float *depth = &rt->depth[ sample_index ];
@@ -333,8 +336,6 @@ void csr_draw( csr_target *rt, vmf_vert *triangles, u32 triangle_count, m4x3f tr
 
        // Derive normal matrix
        m4x3_to_3x3( transform, normal );
-       
-       // NOTE: This isn't strictly necessary since CS:GO only uses uniform scaling.
        m3x3_inv_transpose( normal, normal );
 
        for( u32 i = 0; i < triangle_count; i ++ )
@@ -344,10 +345,17 @@ void csr_draw( csr_target *rt, vmf_vert *triangles, u32 triangle_count, m4x3f tr
                m4x3_mulv( transform, triangle[0].co, new_tri[0].co );
                m4x3_mulv( transform, triangle[1].co, new_tri[1].co );
                m4x3_mulv( transform, triangle[2].co, new_tri[2].co );
+
                m3x3_mulv( normal, triangle[0].nrm, new_tri[0].nrm );
                m3x3_mulv( normal, triangle[1].nrm, new_tri[1].nrm );
                m3x3_mulv( normal, triangle[2].nrm, new_tri[2].nrm );
                
+               v3_normalize( new_tri[0].nrm );
+               v3_normalize( new_tri[1].nrm );
+               v3_normalize( new_tri[2].nrm );
+               
+               m4x3_mulv( transform, triangles[0].origin, new_tri[0].origin );
+               
                simple_raster( rt, new_tri );
        }
 }
@@ -437,8 +445,7 @@ void csr_vmf_render( csr_target *rt, vmf_map *map, vdf_node *root, csr_filter *f
                                        {
                                                v3_copy( &mdl->vertices[ mdl->indices[ i*3+j ] *8 ],   tri[j].co );
                                                v3_copy( &mdl->vertices[ mdl->indices[ i*3+j ] *8+3 ], tri[j].nrm );
-                                               tri[j].xy[0] = 0.f;
-                                               tri[j].xy[1] = 0.f;
+                                               v3_zero( tri[j].origin );
                                        }
                                        
                                        csr_draw( rt, tri, 1, model );
@@ -522,4 +529,45 @@ void csr_rt_save_buffers( csr_target *rt, const char *basename, const char *subn
        free( image );
 }
 
+// Save floating point buffer to tga. Must be in range (0-1)
+// Offset and stride are in bytes
+void csr_rt_save_tga( csr_target *rt, const char *path, u32 offset, u32 nc )
+{
+       u8 *image = (u8 *)csr_malloc( rt->x*rt->y * 4 );
+       
+       float contrib = 255.f/(float)rt->num_samples;
+       
+       for( int y = 0; y < rt->y; y ++ )
+       {
+               for( int x = 0; x < rt->x; x ++ )
+               {
+                       u32 pixel_index = (y*rt->x + x);
+               
+                       void *src = rt->colour + offset + pixel_index * rt->num_samples * rt->shader->stride;
+                       u8 *dst = image + pixel_index*4;
+                       
+                       v4f accum = { 0.f, 0.f, 0.f, 0.f };
+                       
+                       for( int k = 0; k < rt->num_samples; k ++ )
+                       {
+                               float *src_sample = (float *)(src + k*rt->shader->stride);
+                       
+                               for( int j = 0; j < nc; j ++ )
+                               {
+                                       accum[ j ] += src_sample[ j ] * contrib;
+                               }
+                       }
+                       
+                       // TODO: Clamp this value
+                       dst[0] = accum[0];
+                       dst[1] = accum[1];
+                       dst[2] = accum[2];
+                       dst[3] = accum[3];
+               }
+       }
+       
+       csr_tga_write( path, rt->x, rt->y, nc, image );
+       free( image );
+}
+
 #endif
index 1555361cab68be25f694c6e73dfd27bf8f9a33a3..36ad62fe4583bb848e5d7ef90929594982ed3d23 100644 (file)
--- a/csrMath.h
+++ b/csrMath.h
@@ -86,6 +86,11 @@ CSR_INLINE void v2_mul( v2f a, v2f b, v2f d )
        d[0] = a[0]*b[0]; d[1] = a[1]*b[1];
 }
 
+CSR_INLINE void v2_div( v2f a, v2f b, v2f d )
+{
+       d[0] = a[0]/b[0]; d[1] = a[1]/b[1];
+}
+
 // Vector 3
 // ==================================================================================================================
 
diff --git a/csrTga.h b/csrTga.h
new file mode 100644 (file)
index 0000000..c3fbd5b
--- /dev/null
+++ b/csrTga.h
@@ -0,0 +1,62 @@
+#pragma pack(push, 1)
+struct tga_header 
+{
+       char  idlength;
+       char  colourmaptype;
+       char  datatypecode;
+       short int colourmaporigin;
+       short int colourmaplength;
+       char  colourmapdepth;
+       short int x_origin;
+       short int y_origin;
+       short width;
+       short height;
+       char  bitsperpixel;
+       char  imagedescriptor;
+};
+#pragma pack(pop)
+
+// Requires RGBA data. Can write grayscale (comp=1), RGB (comp=3) or RGBA (comp=4)
+int csr_tga_write( const char *path, u32 width, u32 height, int comp, u8 *rgba )
+{
+       FILE *fp = fopen( path, "wb" );
+
+       if( fp )
+       {
+               struct tga_header header =
+               {
+                       .datatypecode = 2,
+                       .width = width,
+                       .height = height,
+                       .bitsperpixel = comp * 8,
+                       .imagedescriptor = 0x20
+               };
+
+               fwrite( &header, sizeof( struct tga_header ), 1, fp );
+
+               for( u32 i = 0; i < width * height; i ++ )
+               {
+                       u8 *colour = rgba + i*4;                        
+                       u8 flipped[4];
+
+                       if( comp >= 3 )
+                       {                               
+                               flipped[0] = colour[2];
+                               flipped[1] = colour[1];
+                               flipped[2] = colour[0];
+                               flipped[3] = colour[3];
+                               
+                               colour = flipped;
+                       }
+                       
+                       fwrite( colour, 1, comp, fp );
+               }
+
+               fclose( fp );
+               return 1;
+       }
+       else
+       {
+               return 0;
+       }
+}
index f6b0b85c1044886899aceff646f6686e2e25a607..ffec370b0a02cb76c21e4e0b4e7a1a47ec68e544 100644 (file)
@@ -5,6 +5,9 @@
 
 #include "csRadar.h"
 
+float min_height = 0.f;
+v4f remapping_range;
+
 // GBuffer shader
 void frag_gbuffer( void *dest, vmf_vert tri[3], float bca, float bcb, float bcc );
 void frag_gbuffer_clear( void *dest );
@@ -27,17 +30,6 @@ void csr_ext_main( csr_api *api )
 {
        if( !csr_init( api ) )
                return;
-               
-       // Setup shader variants
-       if( api->write_origins )
-       {
-               // Use origin fragment variant
-       }
-       
-       if( api->write_normals )
-       {
-               // Increase stride 
-       }
        
        csr_create_target( &api->target, api->resolution, api->resolution, api->sampling_mode, &shader_gbuffer );
        csr_rt_clear( &api->target );
@@ -47,6 +39,10 @@ void csr_ext_main( csr_api *api )
        csr_auto_fit( &api->target, api->padding );
        vmf_load_models( api->map );
        
+       min_height = api->target.bounds[0][2];
+       v2_copy( api->target.bounds[0], remapping_range );
+       v2_sub( api->target.bounds[1], api->target.bounds[0], &remapping_range[2] );
+       
        // Draw everything
        draw_buffers( api, 0 ); 
 }
@@ -69,6 +65,7 @@ void draw_buffers( csr_api *api, int bounds_only )
                if( !bounds_only )
                {
                        csr_rt_save_buffers( &api->target, api->output_path, "all" );
+                       csr_rt_save_tga( &api->target, "test_test_test.tga", 3*sizeof(float), 4 );
                }
        }
        else
@@ -76,16 +73,18 @@ void draw_buffers( csr_api *api, int bounds_only )
                // Draw groups
                for( int i = 1; i < api->num_strings; i ++ )
                {
-                       filter.visgroup = api->strings[ i ];
+                       csr_filter_update_from_strings( &filter, &api->strings[i] );
+                       
                        csr_vmf_render( &api->target, map, map->root, &filter, NULL, NULL );
 
                        if( !bounds_only )
                        {
-                               csr_rt_save_buffers( &api->target, api->output_path, api->strings[i] );                 
+                               csr_rt_save_buffers( &api->target, api->output_path, api->strings[i].str );                     
                                //csr_rt_save_c32f( ... );
                                //csr_rt_save_tga( ... );
                                
                                // tar_write_dds( ... );
+                               csr_rt_save_tga( &api->target, "test_test_test.tga", 3*sizeof(float), 4 );
                                
                                csr_rt_clear( &api->target );
                        }
@@ -97,15 +96,34 @@ void frag_gbuffer( void *dest, vmf_vert tri[3], float bca, float bcb, float bcc
 {
        float *dest_colour = (float *)dest;
 
-       v3_muls( tri[0].co, bca, dest_colour );
-       v3_muladds( dest_colour, tri[1].co, bcb, dest_colour );
-       v3_muladds( dest_colour, tri[2].co, bcc, dest_colour );
+       // Position
+       v2_sub( tri[0].origin, remapping_range, dest_colour );
+       v2_div( dest_colour, &remapping_range[2], dest_colour );
+
+       dest_colour[2] = tri[0].co[2]*bca + tri[1].co[2]*bcb + tri[2].co[2]*bcc;
+       
+       // Normals
+       v3_muls( tri[0].nrm, bca, dest_colour+3 );
+       v3_muladds( dest_colour+3, tri[1].nrm, bcb, dest_colour+3 );
+       v3_muladds( dest_colour+3, tri[2].nrm, bcc, dest_colour+3 );
        
-       // TODO: Normal map
+       v3_muls( dest_colour+3, 0.5f, dest_colour+3 );
+       v3_add( (v3f){0.5f,0.5f,0.5f}, dest_colour+3, dest_colour+3 );
+       
+       // Mask
+       dest_colour[6] = 1.f;
 }
 
 void frag_gbuffer_clear( void *dest )
 {
        float *dest_colour = (float *)dest;
-       v3_zero( dest_colour );
+       dest_colour[0] = 0.f;
+       dest_colour[1] = 0.f;
+       dest_colour[2] = min_height;
+       
+       dest_colour[3] = 0.5f;
+       dest_colour[4] = 0.5f;
+       dest_colour[5] = 1.0f;
+       
+       dest_colour[6] = 0.f;
 }
diff --git a/vmf.h b/vmf.h
index c5f736b4474977214d8ef543dbb79ba1029c4a0d..4fbe023c568867a5ba5cb6ab0a4ae90c581dbe99 100644 (file)
--- a/vmf.h
+++ b/vmf.h
@@ -43,7 +43,7 @@ void vmf_load_models( vmf_map *map );
 // Create matrix describing this entities transform
 void vmf_entity_transform( vdf_node *ent, m4x3f mat );
 
-u32 vmf_visgroup_id( vdf_node *root, const char *name );
+int vmf_visgroup_id( vdf_node *root, const char *name );
 int vmf_visgroup_match( vdf_node *ent, u32 target );
 
 // Currently unused
@@ -74,7 +74,7 @@ struct vmf_vert
 {
        v3f     co;
        v3f     nrm;
-       v2f     xy;
+       v3f     origin;
 };
 
 struct vmf_face
@@ -422,7 +422,7 @@ ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node )
        v3_divs( center, (float)numpoints, center );
        for( ; vert_start < csr_sb_count( ctx->verts ); vert_start ++ )
        {
-               v2_copy( center, ctx->verts[ vert_start ].xy );
+               v3_copy( center, ctx->verts[ vert_start ].origin );
        }
        
        // Sort each faces and trianglulalate them
@@ -522,6 +522,9 @@ ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node )
                                                
                                                // Todo, put correct normal
                                                v3_copy( (v3f){ 0.f, 0.f, 1.f }, vert->nrm );
+                                               
+                                               // Todo: use real bounds of displaced vertices
+                                               v3_copy( center, vert->origin );
                                        }
                                }
                                
@@ -817,7 +820,7 @@ void vmf_entity_transform( vdf_node *ent, m4x3f mat )
        m4x3_scale( mat, scale );
 }
 
-u32 vmf_visgroup_id( vdf_node *root, const char *name )
+int vmf_visgroup_id( vdf_node *root, const char *name )
 {
        vdf_node *dict = vdf_next( root, "visgroups", NULL );
        
@@ -832,7 +835,7 @@ u32 vmf_visgroup_id( vdf_node *root, const char *name )
                }
        }
        
-       return 0;
+       return -1;
 }
 
 int vmf_visgroup_match( vdf_node *ent, u32 target )