18580477ff78ca93ec965e8cef981b22e52e9a44
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
);
49 void csr_rt_save_c32f( csr_target
*rt
, const char *path
, u32 offset
);
52 __attribute__ ((deprecated
))
53 void csr_rt_save_buffers( csr_target
*rt
, const char *basename
, const char *subname
);
56 //=======================================================================================================================
61 csr_frag_program frag
;
83 const char *visgroup
; // Limit to this visgroup only
84 const char *classname
; // Limit to this exact classname. will not draw world
86 int compute_bounds_only
;
109 { 0x0.4p0f
, 0x0.4p0f
},
110 { 0x0.4p0f
, -0x0.4p0f
},
111 { -0x0.4p0f
, -0x0.4p0f
},
112 { -0x0.4p0f
, 0x0.4p0f
}
119 v2f csr_msaa_2x2rgss
[] =
121 { 0x0.2p0f
, 0x0.6p0f
},
122 { -0x0.6p0f
, 0x0.2p0f
},
123 { -0x0.2p0f
, -0x0.6p0f
},
124 { 0x0.6p0f
, -0x0.2p0f
}
135 v2f csr_msaa_8rook
[] =
137 { 0x0.1p0f
, 0x0.7p0f
},
138 { 0x0.5p0f
, 0x0.1p0f
},
139 { 0x0.7p0f
, -0x0.3p0f
},
140 { 0x0.3p0f
, -0x0.5p0f
},
141 { -0x0.1p0f
, -0x0.7p0f
},
142 { -0x0.5p0f
, -0x0.1p0f
},
143 { -0x0.7p0f
, 0x0.3p0f
},
144 { -0x0.3p0f
, 0x0.5p0f
}
148 void csr_create_target( csr_target
*rt
, u32 x
, u32 y
, EMSAA aa
, csr_shader
*shader
)
158 rt
->sample_src
= csr_msaa_1
;
163 rt
->sample_src
= csr_msaa_2x2
;
168 rt
->sample_src
= csr_msaa_2x2rgss
;
173 rt
->sample_src
= csr_msaa_8rook
;
178 rt
->depth
= (float *)csr_malloc( x
*y
*rt
->num_samples
* sizeof(float) );
179 rt
->colour
= csr_malloc( x
* y
* rt
->shader
->stride
* rt
->num_samples
);
181 v3_fill( rt
->bounds
[0], INFINITY
);
182 v3_fill( rt
->bounds
[1], -INFINITY
);
185 void csr_update_subsamples( csr_target
*rt
)
187 float range_x
= (rt
->bounds
[1][0]-rt
->bounds
[0][0]);
188 float range_y
= (rt
->bounds
[1][1]-rt
->bounds
[0][1]);
190 v2f pixel_size
= { range_x
/(float)rt
->x
, range_y
/(float)rt
->y
};
192 for( int i
= 0; i
< rt
->num_samples
; i
++ )
194 v2_mul( rt
->sample_src
[i
], pixel_size
, rt
->subsamples
[i
] );
198 void csr_rt_free( csr_target
*rt
)
204 void csr_rt_clear( csr_target
*rt
)
206 for( u32 i
= 0; i
< rt
->x
*rt
->y
*rt
->num_samples
; i
++ )
208 rt
->shader
->clear( rt
->colour
+ i
* rt
->shader
->stride
);
213 void csr_auto_fit( csr_target
*rt
, float padding
)
215 // Correct aspect ratio to be square
216 float dx
, dy
, l
, cx
, cy
;
218 dx
= rt
->bounds
[1][0] - rt
->bounds
[0][0];
219 dy
= rt
->bounds
[1][1] - rt
->bounds
[0][1];
221 l
= fmaxf( dx
, dy
) * .5f
;
223 cx
= (rt
->bounds
[1][0] + rt
->bounds
[0][0]) * .5f
;
224 cy
= (rt
->bounds
[1][1] + rt
->bounds
[0][1]) * .5f
;
226 rt
->bounds
[0][0] = cx
- l
- padding
;
227 rt
->bounds
[1][0] = cx
+ l
+ padding
;
228 rt
->bounds
[0][1] = cy
- l
- padding
;
229 rt
->bounds
[1][1] = cy
+ l
+ padding
;
231 rt
->scale
= l
+ padding
;
233 csr_update_subsamples( rt
);
236 void csr_write_txt( char const *path
, const char *name
, csr_target
*rt
)
240 write_ptr
= fopen( path
, "w" );
242 fprintf( write_ptr
, "\"%s\"\n\{\n", name
);
243 fprintf( write_ptr
, "\t\"material\" \"overviews/%s\"\n", name
);
244 fprintf( write_ptr
, "\t\"pos_x\" \"%.8f\"\n", rt
->bounds
[0][0] );
245 fprintf( write_ptr
, "\t\"pos_y\" \"%.8f\"\n", rt
->bounds
[0][1] );
246 fprintf( write_ptr
, "\t\"scale\" \"%.8f\"\n", rt
->scale
/ (float)rt
->x
);
247 fprintf( write_ptr
, "}\n" );
252 void simple_raster( csr_target
*rt
, vmf_vert tri
[3] )
254 // Very very simplified rasterizing algorithm
255 v2f bmin
= { 0.f
, 0.f
};
256 v2f bmax
= { rt
->x
, rt
->y
};
258 v2_minv( tri
[0].co
, tri
[1].co
, bmin
);
259 v2_minv( tri
[2].co
, bmin
, bmin
);
261 v2_maxv( tri
[0].co
, tri
[1].co
, bmax
);
262 v2_maxv( tri
[2].co
, bmax
, bmax
);
264 float range_x
= (rt
->bounds
[1][0]-rt
->bounds
[0][0])/(float)rt
->x
;
265 float range_y
= (rt
->bounds
[1][1]-rt
->bounds
[0][1])/(float)rt
->y
;
267 int start_x
= csr_min( rt
->x
-1, csr_max( 0, floorf( (bmin
[0]-rt
->bounds
[0][0])/range_x
)));
268 int end_x
= csr_max( 0, csr_min( rt
->x
-1, ceilf( (bmax
[0]-rt
->bounds
[0][0])/range_x
)));
269 int start_y
= csr_min( rt
->y
-1, csr_max( 0, floorf( (bmin
[1]-rt
->bounds
[0][1])/range_y
)));
270 int end_y
= csr_max( 0, csr_min( rt
->y
-1, ceilf( (bmax
[1]-rt
->bounds
[0][1])/range_y
)));
273 float d
, bca
= 0.f
, bcb
= 0.f
, bcc
= 0.f
;
275 v2_sub( tri
[1].co
, tri
[0].co
, v0
);
276 v2_sub( tri
[2].co
, tri
[0].co
, v1
);
277 v2_sub( tri
[1].co
, tri
[2].co
, v2
);
278 d
= 1.f
/ (v0
[0]*v1
[1] - v1
[0]*v0
[1]);
281 if( v2_cross( v0
, v1
) > 0.f
)
286 for( u32 py
= start_y
; py
<= end_y
; py
++ )
288 trace_origin
[1] = csr_lerpf( rt
->bounds
[0][1], rt
->bounds
[1][1], (float)py
/(float)rt
->y
);
290 for( u32 px
= start_x
; px
<= end_x
; px
++ )
292 u32 sample_index
= ((rt
->y
-py
-1)*rt
->x
+px
) * rt
->num_samples
;
294 void *frag
= rt
->colour
+ sample_index
*rt
->shader
->stride
;
295 float *depth
= &rt
->depth
[ sample_index
];
297 trace_origin
[0] = csr_lerpf( rt
->bounds
[0][0], rt
->bounds
[1][0], (float)px
/(float)rt
->x
);
299 // Determine coverage
300 for( int i
= 0; i
< rt
->num_samples
; i
++ )
304 v2_add( rt
->subsamples
[ i
], trace_origin
, sample_origin
);
305 v2_sub( sample_origin
, tri
[0].co
, vp
);
307 if( v2_cross( v0
, vp
) > 0.f
)
309 if( v2_cross( vp
, v1
) > 0.f
)
313 v2_sub( sample_origin
, tri
[2].co
, vp2
);
315 if( v2_cross( vp2
, v2
) > 0.f
)
318 bcb
= (vp
[0]*v1
[1] - v1
[0]*vp
[1]) * d
;
319 bcc
= (v0
[0]*vp
[1] - vp
[0]*v0
[1]) * d
;
320 bca
= 1.f
- bcb
- bcc
;
322 float hit
= tri
[0].co
[2] * bca
+ tri
[1].co
[2] * bcb
+ tri
[2].co
[2] * bcc
;
323 float hit_depth
= hit
+ 16385.f
;
325 if( hit_depth
> depth
[i
] && hit
>= rt
->bounds
[0][2] && hit
<= rt
->bounds
[1][2] )
327 depth
[i
] = hit_depth
;
328 rt
->shader
->frag( frag
+i
*rt
->shader
->stride
, tri
, bca
, bcb
, bcc
);
335 void csr_draw( csr_target
*rt
, vmf_vert
*triangles
, u32 triangle_count
, m4x3f transform
)
340 // Derive normal matrix
341 m4x3_to_3x3( transform
, normal
);
342 m3x3_inv_transpose( normal
, normal
);
344 for( u32 i
= 0; i
< triangle_count
; i
++ )
346 vmf_vert
*triangle
= triangles
+ i
*3;
348 m4x3_mulv( transform
, triangle
[0].co
, new_tri
[0].co
);
349 m4x3_mulv( transform
, triangle
[1].co
, new_tri
[1].co
);
350 m4x3_mulv( transform
, triangle
[2].co
, new_tri
[2].co
);
352 m3x3_mulv( normal
, triangle
[0].nrm
, new_tri
[0].nrm
);
353 m3x3_mulv( normal
, triangle
[1].nrm
, new_tri
[1].nrm
);
354 m3x3_mulv( normal
, triangle
[2].nrm
, new_tri
[2].nrm
);
356 v3_normalize( new_tri
[0].nrm
);
357 v3_normalize( new_tri
[1].nrm
);
358 v3_normalize( new_tri
[2].nrm
);
360 m4x3_mulv( transform
, triangles
[0].origin
, new_tri
[0].origin
);
362 simple_raster( rt
, new_tri
);
366 void csr_vmf_render( csr_target
*rt
, vmf_map
*map
, vdf_node
*root
, csr_filter
*filter
, m4x3f prev
, m4x3f inst
)
368 m4x3f transform
= M4X3_IDENTITY
;
374 int filter_visgroups
= 0, filter_classname
= 0, compute_bounds_only
= 0;
378 if( filter
->visgroup
)
380 filter_visgroups
= 1;
381 group_id
= vmf_visgroup_id( root
, filter
->visgroup
);
384 if( filter
->classname
)
386 filter_classname
= 1;
389 compute_bounds_only
= filter
->compute_bounds_only
;
392 // Multiply previous transform with instance transform to create basis
395 m4x3_mul( prev
, inst
, transform
);
398 // Gather world brushes
399 solidgen_ctx_init( &solid
);
401 if( !filter_classname
)
403 vdf_node
*world
= vdf_next( root
, "world", NULL
);
405 vdf_foreach( world
, "solid", brush
)
407 if( filter_visgroups
&& !vmf_visgroup_match( brush
, group_id
) )
410 // TODO: heap-use-after-free
411 solidgen_push( &solid
, brush
);
415 // Actual entity loop
418 vdf_foreach( root
, "entity", ent
)
420 if( filter_visgroups
&& !vmf_visgroup_match( ent
, group_id
) )
423 if( filter_classname
)
424 if( strcmp( kv_get( ent
, "classname", "" ), filter
->classname
) )
427 if( ent
->user
& VMF_FLAG_IS_PROP
)
429 // Create model transform
430 m4x3_identity( model
);
432 vmf_entity_transform( ent
, model
);
433 m4x3_mul( transform
, model
, model
);
436 mdl_mesh_t
*mdl
= &map
->models
[ ent
->user1
].mdl
;
438 if( compute_bounds_only
)
440 map
->models
[ ent
->user1
].need_load
= 1;
441 m4x3_expand_aabb_point( model
, rt
->bounds
, (v3f
){0.f
,0.f
,0.f
} );
445 for( int i
= 0; i
< mdl
->num_indices
/3; i
++ )
447 for( int j
= 0; j
< 3; j
++ )
449 v3_copy( &mdl
->vertices
[ mdl
->indices
[ i
*3+j
] *8 ], tri
[j
].co
);
450 v3_copy( &mdl
->vertices
[ mdl
->indices
[ i
*3+j
] *8+3 ], tri
[j
].nrm
);
451 v3_zero( tri
[j
].origin
);
454 csr_draw( rt
, tri
, 1, model
);
458 else if( ent
->user
& VMF_FLAG_IS_INSTANCE
)
460 m4x3_identity( model
);
461 vmf_entity_transform( ent
, model
);
463 csr_vmf_render( rt
, map
, map
->cache
[ ent
->user1
].root
, filter
, transform
, model
);
468 vdf_foreach( ent
, "solid", ent_solid
)
470 solidgen_push( &solid
, ent_solid
);
475 if( compute_bounds_only
)
477 solidgen_bounds( &solid
, trf_bounds
);
478 m4x3_transform_aabb( transform
, trf_bounds
);
479 box_concat( rt
->bounds
, trf_bounds
);
484 for( int i
= 0; i
< csr_sb_count( solid
.indices
)/3; i
++ )
486 u32
* base
= solid
.indices
+ i
*3;
488 tri
[0] = solid
.verts
[ base
[0] ];
489 tri
[1] = solid
.verts
[ base
[1] ];
490 tri
[2] = solid
.verts
[ base
[2] ];
492 csr_draw( rt
, tri
, 1, transform
);
496 solidgen_ctx_reset( &solid
);
497 solidgen_ctx_free( &solid
);
501 void csr_rt_save_buffers( csr_target
*rt
, const char *basename
, const char *subname
)
505 float *image
= (float *)csr_malloc( 1024*1024*sizeof(float)*3 );
507 float contrib
= 1.f
/(float)rt
->num_samples
;
509 for( int l
= 0; l
< rt
->x
; l
++ )
511 for( int x
= 0; x
< rt
->y
; x
++ )
513 float *dst
= &image
[ (l
*1024+x
)*3 ];
514 void *src
= rt
->colour
+ ((1023-l
)*1024+x
) * rt
->num_samples
* rt
->shader
->stride
;
516 v3_muls( (float *)src
, contrib
, dst
);
518 for( int j
= 1; j
< rt
->num_samples
; j
++ )
520 v3_muladds( dst
, (float *)(src
+ j
*rt
->shader
->stride
), contrib
, dst
);
525 // Save position buffer
526 strcpy( output
, basename
);
527 strcat( output
, "." );
528 strcat( output
, subname
);
529 strcat( output
, "_position.pfm" );
530 csr_32f_write( output
, rt
->x
, rt
->y
, image
);
536 void csr_rt_save_c32f( csr_target
*rt
, const char *path
, u32 offset
)
538 float *image
= (float *)csr_malloc( rt
->x
*rt
->y
*3*sizeof(float) );
540 float contrib
= 255.f
/(float)rt
->num_samples
;
542 for( int i
= 0; i
< rt
->x
*rt
->y
; i
++ )
544 void *src
= rt
->colour
+ offset
+ i
* rt
->num_samples
* rt
->shader
->stride
;
545 float *dst
= image
+ i
*3;
548 for( int k
= 0; k
< rt
->num_samples
; k
++ )
550 v3_muladds( dst
, (float *)(src
+ k
*rt
->shader
->stride
), contrib
, dst
);
554 csr_32f_write( path
, rt
->x
, rt
->y
, image
);
558 // Save floating point buffer to tga. Must be in range (0-1)
559 // Offset and stride are in bytes
560 void csr_rt_save_tga( csr_target
*rt
, const char *path
, u32 offset
, u32 nc
)
562 u8
*image
= (u8
*)csr_malloc( rt
->x
*rt
->y
* 4 );
564 float contrib
= 255.f
/(float)rt
->num_samples
;
566 for( int i
= 0; i
< rt
->x
*rt
->y
; i
++ )
568 void *src
= rt
->colour
+ offset
+ i
* rt
->num_samples
* rt
->shader
->stride
;
569 u8
*dst
= image
+ i
*4;
571 v4f accum
= { 0.f
, 0.f
, 0.f
, 0.f
};
573 for( int k
= 0; k
< rt
->num_samples
; k
++ )
575 float *src_sample
= (float *)(src
+ k
*rt
->shader
->stride
);
577 for( int j
= 0; j
< nc
; j
++ )
579 accum
[ j
] += src_sample
[ j
] * contrib
;
583 // TODO: Clamp this value
590 csr_tga_write( path
, rt
->x
, rt
->y
, nc
, image
);