+
+ 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 );
+ }
+}
+
+void csr_vmf_render( csr_target *rt, vmf_map *map, vdf_node *root, csr_filter *filter, m4x3f prev, m4x3f inst )
+{
+ m4x3f transform = M4X3_IDENTITY;
+ vmf_solid solid;
+ vmf_vert tri[3];
+ boxf trf_bounds;
+
+ u32 group_id = 0;
+ int filter_visgroups = 0, filter_classname = 0, compute_bounds_only = 0;
+
+ if( filter )
+ {
+ if( filter->visgroup )
+ {
+ filter_visgroups = 1;
+ group_id = vmf_visgroup_id( root, filter->visgroup );
+ }
+
+ if( filter->classname )
+ {
+ filter_classname = 1;
+ }
+
+ compute_bounds_only = filter->compute_bounds_only;
+ }
+
+ // Multiply previous transform with instance transform to create basis
+ if( prev )
+ {
+ m4x3_mul( prev, inst, transform );
+ }
+
+ // Gather world brushes
+ solidgen_ctx_init( &solid );
+
+ if( !filter_classname )
+ {
+ vdf_node *world = vdf_next( root, "world", NULL );
+
+ vdf_foreach( world, "solid", brush )
+ {
+ if( filter_visgroups && !vmf_visgroup_match( brush, group_id ) )
+ continue;
+
+ // TODO: heap-use-after-free
+ solidgen_push( &solid, brush );
+ }
+ }
+
+ // Actual entity loop
+ m4x3f model;
+
+ vdf_foreach( root, "entity", ent )
+ {
+ if( filter_visgroups && !vmf_visgroup_match( ent, group_id ) )
+ continue;
+
+ if( filter_classname )
+ if( strcmp( kv_get( ent, "classname", "" ), filter->classname ) )
+ continue;
+
+ if( ent->user & VMF_FLAG_IS_PROP )
+ {
+ // Create model transform
+ m4x3_identity( model );
+
+ vmf_entity_transform( ent, model );
+ m4x3_mul( transform, model, model );
+
+ // Draw model
+ mdl_mesh_t *mdl = &map->models[ ent->user1 ].mdl;
+
+ if( compute_bounds_only )
+ {
+ map->models[ ent->user1 ].need_load = 1;
+ m4x3_expand_aabb_point( model, rt->bounds, (v3f){0.f,0.f,0.f} );
+ }
+ else
+ {
+ for( int i = 0; i < mdl->num_indices/3; i ++ )
+ {
+ for( int j = 0; j < 3; j ++ )
+ {
+ 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 );
+ v3_zero( tri[j].origin );
+ }
+
+ csr_draw( rt, tri, 1, model );
+ }
+ }
+ }
+ else if( ent->user & VMF_FLAG_IS_INSTANCE )
+ {
+ m4x3_identity( model );
+ vmf_entity_transform( ent, model );
+
+ csr_vmf_render( rt, map, map->cache[ ent->user1 ].root, filter, transform, model );
+ }
+ else
+ {
+ // Brush entity
+ vdf_foreach( ent, "solid", ent_solid )
+ {
+ solidgen_push( &solid, ent_solid );
+ }
+ }