b744c044e9058ed379c23004bac8d05286bf41c4
1 // Copyright (C) 2021 Harry Godden (hgn)
3 // Extremely simple software renderer. Only supports orthographic
4 //=======================================================================================================================
6 typedef struct csr_target csr_target
;
7 typedef struct csr_filter csr_filter
;
8 typedef struct csr_shader csr_shader
;
9 typedef enum EMSAA EMSAA
;
11 typedef void (* csr_frag_program
)( void *, vmf_vert
[3], float, float, float );
12 typedef void (* csr_frag_clear
)( void * );
15 //=======================================================================================================================
17 // Create a render target. Resolution, msaa, and shader must be known at this point!
18 void csr_create_target( csr_target
*rt
, u32 x
, u32 y
, EMSAA aa
, csr_shader
*shader
);
19 void csr_rt_clear( csr_target
*rt
);
20 void csr_rt_free( csr_target
*rt
);
22 // Refit bounds so that it is square, and crops to center with padding
23 void csr_auto_fit( csr_target
*rt
, float padding
);
25 // Run this after bounds have been adjusted on the RT to update the size of the msaa
26 // Auto fit will call this.
27 void csr_update_subsamples( csr_target
*rt
);
29 // Write CS:GO radar txt
30 void csr_write_txt( char const *path
, const char *name
, csr_target
*rt
);
35 // Render a finalzied triangle into target. Coordinates are world space
36 void simple_raster( csr_target
*rt
, vmf_vert tri
[3] );
38 // Draw a batch of triangles with an affine world transformation
39 void csr_draw( csr_target
*rt
, vmf_vert
*triangles
, u32 triangle_count
, m4x3f transform
);
41 // Draw VMF with filtering options. Will automatically branch into instances
42 // You should call this with the last two recursive arguments (prev,inst), set to NULL
44 // Filter is optional, it can be st to NULL to just render everything.
45 void csr_vmf_render( csr_target
*rt
, vmf_map
*map
, vdf_node
*root
, csr_filter
*filter
, m4x3f prev
, m4x3f inst
);
48 void csr_rt_save_tga( csr_target
*rt
, const char *path
, u32 offset
, u32 nc
);
51 void csr_rt_save_buffers( csr_target
*rt
, const char *basename
, const char *subname
);
54 //=======================================================================================================================
59 csr_frag_program frag
;
81 const char *visgroup
; // Limit to this visgroup only
82 const char *classname
; // Limit to this exact classname. will not draw world
84 int compute_bounds_only
;
107 { 0x0.4p0f
, 0x0.4p0f
},
108 { 0x0.4p0f
, -0x0.4p0f
},
109 { -0x0.4p0f
, -0x0.4p0f
},
110 { -0x0.4p0f
, 0x0.4p0f
}
117 v2f csr_msaa_2x2rgss
[] =
119 { 0x0.2p0f
, 0x0.6p0f
},
120 { -0x0.6p0f
, 0x0.2p0f
},
121 { -0x0.2p0f
, -0x0.6p0f
},
122 { 0x0.6p0f
, -0x0.2p0f
}
133 v2f csr_msaa_8rook
[] =
135 { 0x0.1p0f
, 0x0.7p0f
},
136 { 0x0.5p0f
, 0x0.1p0f
},
137 { 0x0.7p0f
, -0x0.3p0f
},
138 { 0x0.3p0f
, -0x0.5p0f
},
139 { -0x0.1p0f
, -0x0.7p0f
},
140 { -0x0.5p0f
, -0x0.1p0f
},
141 { -0x0.7p0f
, 0x0.3p0f
},
142 { -0x0.3p0f
, 0x0.5p0f
}
146 void csr_create_target( csr_target
*rt
, u32 x
, u32 y
, EMSAA aa
, csr_shader
*shader
)
156 rt
->sample_src
= csr_msaa_1
;
161 rt
->sample_src
= csr_msaa_2x2
;
166 rt
->sample_src
= csr_msaa_2x2rgss
;
171 rt
->sample_src
= csr_msaa_8rook
;
176 rt
->depth
= (float *)csr_malloc( x
*y
*rt
->num_samples
* sizeof(float) );
177 rt
->colour
= csr_malloc( x
* y
* rt
->shader
->stride
* rt
->num_samples
);
179 v3_fill( rt
->bounds
[0], INFINITY
);
180 v3_fill( rt
->bounds
[1], -INFINITY
);
183 void csr_update_subsamples( csr_target
*rt
)
185 float range_x
= (rt
->bounds
[1][0]-rt
->bounds
[0][0]);
186 float range_y
= (rt
->bounds
[1][1]-rt
->bounds
[0][1]);
188 v2f pixel_size
= { range_x
/(float)rt
->x
, range_y
/(float)rt
->y
};
190 for( int i
= 0; i
< rt
->num_samples
; i
++ )
192 v2_mul( rt
->sample_src
[i
], pixel_size
, rt
->subsamples
[i
] );
196 void csr_rt_free( csr_target
*rt
)
202 void csr_rt_clear( csr_target
*rt
)
204 for( u32 i
= 0; i
< rt
->x
*rt
->y
*rt
->num_samples
; i
++ )
206 rt
->shader
->clear( rt
->colour
+ i
* rt
->shader
->stride
);
211 void csr_auto_fit( csr_target
*rt
, float padding
)
213 // Correct aspect ratio to be square
214 float dx
, dy
, l
, cx
, cy
;
216 dx
= rt
->bounds
[1][0] - rt
->bounds
[0][0];
217 dy
= rt
->bounds
[1][1] - rt
->bounds
[0][1];
219 l
= fmaxf( dx
, dy
) * .5f
;
221 cx
= (rt
->bounds
[1][0] + rt
->bounds
[0][0]) * .5f
;
222 cy
= (rt
->bounds
[1][1] + rt
->bounds
[0][1]) * .5f
;
224 rt
->bounds
[0][0] = cx
- l
- padding
;
225 rt
->bounds
[1][0] = cx
+ l
+ padding
;
226 rt
->bounds
[0][1] = cy
- l
- padding
;
227 rt
->bounds
[1][1] = cy
+ l
+ padding
;
229 rt
->scale
= l
+ padding
;
231 csr_update_subsamples( rt
);
234 void csr_write_txt( char const *path
, const char *name
, csr_target
*rt
)
238 write_ptr
= fopen( path
, "w" );
240 fprintf( write_ptr
, "\"%s\"\n\{\n", name
);
241 fprintf( write_ptr
, "\t\"material\" \"overviews/%s\"\n", name
);
242 fprintf( write_ptr
, "\t\"pos_x\" \"%.8f\"\n", rt
->bounds
[0][0] );
243 fprintf( write_ptr
, "\t\"pos_y\" \"%.8f\"\n", rt
->bounds
[0][1] );
244 fprintf( write_ptr
, "\t\"scale\" \"%.8f\"\n", rt
->scale
/ (float)rt
->x
);
245 fprintf( write_ptr
, "}\n" );
250 void simple_raster( csr_target
*rt
, vmf_vert tri
[3] )
252 // Very very simplified rasterizing algorithm
253 v2f bmin
= { 0.f
, 0.f
};
254 v2f bmax
= { rt
->x
, rt
->y
};
256 v2_minv( tri
[0].co
, tri
[1].co
, bmin
);
257 v2_minv( tri
[2].co
, bmin
, bmin
);
259 v2_maxv( tri
[0].co
, tri
[1].co
, bmax
);
260 v2_maxv( tri
[2].co
, bmax
, bmax
);
262 float range_x
= (rt
->bounds
[1][0]-rt
->bounds
[0][0])/(float)rt
->x
;
263 float range_y
= (rt
->bounds
[1][1]-rt
->bounds
[0][1])/(float)rt
->y
;
265 int start_x
= csr_min( rt
->x
-1, csr_max( 0, floorf( (bmin
[0]-rt
->bounds
[0][0])/range_x
)));
266 int end_x
= csr_max( 0, csr_min( rt
->x
-1, ceilf( (bmax
[0]-rt
->bounds
[0][0])/range_x
)));
267 int start_y
= csr_min( rt
->y
-1, csr_max( 0, floorf( (bmin
[1]-rt
->bounds
[0][1])/range_y
)));
268 int end_y
= csr_max( 0, csr_min( rt
->y
-1, ceilf( (bmax
[1]-rt
->bounds
[0][1])/range_y
)));
271 float d
, bca
= 0.f
, bcb
= 0.f
, bcc
= 0.f
;
273 v2_sub( tri
[1].co
, tri
[0].co
, v0
);
274 v2_sub( tri
[2].co
, tri
[0].co
, v1
);
275 v2_sub( tri
[1].co
, tri
[2].co
, v2
);
276 d
= 1.f
/ (v0
[0]*v1
[1] - v1
[0]*v0
[1]);
279 if( v2_cross( v0
, v1
) > 0.f
)
284 for( u32 py
= start_y
; py
<= end_y
; py
++ )
286 trace_origin
[1] = csr_lerpf( rt
->bounds
[0][1], rt
->bounds
[1][1], (float)py
/(float)rt
->y
);
288 for( u32 px
= start_x
; px
<= end_x
; px
++ )
290 u32 sample_index
= ((rt
->y
-py
-1)*rt
->x
+px
) * rt
->num_samples
;
292 void *frag
= rt
->colour
+ sample_index
*rt
->shader
->stride
;
293 float *depth
= &rt
->depth
[ sample_index
];
295 trace_origin
[0] = csr_lerpf( rt
->bounds
[0][0], rt
->bounds
[1][0], (float)px
/(float)rt
->x
);
297 // Determine coverage
298 for( int i
= 0; i
< rt
->num_samples
; i
++ )
302 v2_add( rt
->subsamples
[ i
], trace_origin
, sample_origin
);
303 v2_sub( sample_origin
, tri
[0].co
, vp
);
305 if( v2_cross( v0
, vp
) > 0.f
)
307 if( v2_cross( vp
, v1
) > 0.f
)
311 v2_sub( sample_origin
, tri
[2].co
, vp2
);
313 if( v2_cross( vp2
, v2
) > 0.f
)
316 bcb
= (vp
[0]*v1
[1] - v1
[0]*vp
[1]) * d
;
317 bcc
= (v0
[0]*vp
[1] - vp
[0]*v0
[1]) * d
;
318 bca
= 1.f
- bcb
- bcc
;
320 float hit
= (tri
[0].co
[2] * bca
+ tri
[1].co
[2] * bcb
+ tri
[2].co
[2] * bcc
) +16385.f
;
325 rt
->shader
->frag( frag
+i
*rt
->shader
->stride
, tri
, bca
, bcb
, bcc
);
332 void csr_draw( csr_target
*rt
, vmf_vert
*triangles
, u32 triangle_count
, m4x3f transform
)
337 // Derive normal matrix
338 m4x3_to_3x3( transform
, normal
);
339 m3x3_inv_transpose( normal
, normal
);
341 for( u32 i
= 0; i
< triangle_count
; i
++ )
343 vmf_vert
*triangle
= triangles
+ i
*3;
345 m4x3_mulv( transform
, triangle
[0].co
, new_tri
[0].co
);
346 m4x3_mulv( transform
, triangle
[1].co
, new_tri
[1].co
);
347 m4x3_mulv( transform
, triangle
[2].co
, new_tri
[2].co
);
349 m3x3_mulv( normal
, triangle
[0].nrm
, new_tri
[0].nrm
);
350 m3x3_mulv( normal
, triangle
[1].nrm
, new_tri
[1].nrm
);
351 m3x3_mulv( normal
, triangle
[2].nrm
, new_tri
[2].nrm
);
353 v3_normalize( new_tri
[0].nrm
);
354 v3_normalize( new_tri
[1].nrm
);
355 v3_normalize( new_tri
[2].nrm
);
357 m4x3_mulv( transform
, triangles
[0].origin
, new_tri
[0].origin
);
359 simple_raster( rt
, new_tri
);
363 void csr_vmf_render( csr_target
*rt
, vmf_map
*map
, vdf_node
*root
, csr_filter
*filter
, m4x3f prev
, m4x3f inst
)
365 m4x3f transform
= M4X3_IDENTITY
;
371 int filter_visgroups
= 0, filter_classname
= 0, compute_bounds_only
= 0;
375 if( filter
->visgroup
)
377 filter_visgroups
= 1;
378 group_id
= vmf_visgroup_id( root
, filter
->visgroup
);
381 if( filter
->classname
)
383 filter_classname
= 1;
386 compute_bounds_only
= filter
->compute_bounds_only
;
389 // Multiply previous transform with instance transform to create basis
392 m4x3_mul( prev
, inst
, transform
);
395 // Gather world brushes
396 solidgen_ctx_init( &solid
);
398 if( !filter_classname
)
400 vdf_node
*world
= vdf_next( root
, "world", NULL
);
402 vdf_foreach( world
, "solid", brush
)
404 if( filter_visgroups
&& !vmf_visgroup_match( brush
, group_id
) )
407 // TODO: heap-use-after-free
408 solidgen_push( &solid
, brush
);
412 // Actual entity loop
415 vdf_foreach( root
, "entity", ent
)
417 if( filter_visgroups
&& !vmf_visgroup_match( ent
, group_id
) )
420 if( filter_classname
)
421 if( strcmp( kv_get( ent
, "classname", "" ), filter
->classname
) )
424 if( ent
->user
& VMF_FLAG_IS_PROP
)
426 // Create model transform
427 m4x3_identity( model
);
429 vmf_entity_transform( ent
, model
);
430 m4x3_mul( transform
, model
, model
);
433 mdl_mesh_t
*mdl
= &map
->models
[ ent
->user1
].mdl
;
435 if( compute_bounds_only
)
437 map
->models
[ ent
->user1
].need_load
= 1;
438 m4x3_expand_aabb_point( model
, rt
->bounds
, (v3f
){0.f
,0.f
,0.f
} );
442 for( int i
= 0; i
< mdl
->num_indices
/3; i
++ )
444 for( int j
= 0; j
< 3; j
++ )
446 v3_copy( &mdl
->vertices
[ mdl
->indices
[ i
*3+j
] *8 ], tri
[j
].co
);
447 v3_copy( &mdl
->vertices
[ mdl
->indices
[ i
*3+j
] *8+3 ], tri
[j
].nrm
);
448 v3_zero( tri
[j
].origin
);
451 csr_draw( rt
, tri
, 1, model
);
455 else if( ent
->user
& VMF_FLAG_IS_INSTANCE
)
457 m4x3_identity( model
);
458 vmf_entity_transform( ent
, model
);
460 csr_vmf_render( rt
, map
, map
->cache
[ ent
->user1
].root
, filter
, transform
, model
);
465 vdf_foreach( ent
, "solid", ent_solid
)
467 solidgen_push( &solid
, ent_solid
);
472 if( compute_bounds_only
)
474 solidgen_bounds( &solid
, trf_bounds
);
475 m4x3_transform_aabb( transform
, trf_bounds
);
476 box_concat( rt
->bounds
, trf_bounds
);
481 for( int i
= 0; i
< csr_sb_count( solid
.indices
)/3; i
++ )
483 u32
* base
= solid
.indices
+ i
*3;
485 tri
[0] = solid
.verts
[ base
[0] ];
486 tri
[1] = solid
.verts
[ base
[1] ];
487 tri
[2] = solid
.verts
[ base
[2] ];
489 csr_draw( rt
, tri
, 1, transform
);
493 solidgen_ctx_reset( &solid
);
494 solidgen_ctx_free( &solid
);
498 void csr_rt_save_buffers( csr_target
*rt
, const char *basename
, const char *subname
)
502 float *image
= (float *)csr_malloc( 1024*1024*sizeof(float)*3 );
504 float contrib
= 1.f
/(float)rt
->num_samples
;
506 for( int l
= 0; l
< rt
->x
; l
++ )
508 for( int x
= 0; x
< rt
->y
; x
++ )
510 float *dst
= &image
[ (l
*1024+x
)*3 ];
511 void *src
= rt
->colour
+ ((1023-l
)*1024+x
) * rt
->num_samples
* rt
->shader
->stride
;
513 v3_muls( (float *)src
, contrib
, dst
);
515 for( int j
= 1; j
< rt
->num_samples
; j
++ )
517 v3_muladds( dst
, (float *)(src
+ j
*rt
->shader
->stride
), contrib
, dst
);
522 // Save position buffer
523 strcpy( output
, basename
);
524 strcat( output
, "." );
525 strcat( output
, subname
);
526 strcat( output
, "_position.pfm" );
527 csr_32f_write( output
, rt
->x
, rt
->y
, image
);
532 // Save floating point buffer to tga. Must be in range (0-1)
533 // Offset and stride are in bytes
534 void csr_rt_save_tga( csr_target
*rt
, const char *path
, u32 offset
, u32 nc
)
536 u8
*image
= (u8
*)csr_malloc( rt
->x
*rt
->y
* 4 );
538 float contrib
= 255.f
/(float)rt
->num_samples
;
540 for( int y
= 0; y
< rt
->y
; y
++ )
542 for( int x
= 0; x
< rt
->x
; x
++ )
544 u32 pixel_index
= (y
*rt
->x
+ x
);
546 void *src
= rt
->colour
+ offset
+ pixel_index
* rt
->num_samples
* rt
->shader
->stride
;
547 u8
*dst
= image
+ pixel_index
*4;
549 v4f accum
= { 0.f
, 0.f
, 0.f
, 0.f
};
551 for( int k
= 0; k
< rt
->num_samples
; k
++ )
553 float *src_sample
= (float *)(src
+ k
*rt
->shader
->stride
);
555 for( int j
= 0; j
< nc
; j
++ )
557 accum
[ j
] += src_sample
[ j
] * contrib
;
561 // TODO: Clamp this value
569 csr_tga_write( path
, rt
->x
, rt
->y
, nc
, image
);