revised filtering & api
[csRadar.git] / csrDraw.h
1 typedef struct csr_frag csr_frag;
2 typedef struct csr_target csr_target;
3 typedef struct csr_filter csr_filter;
4
5 struct csr_frag
6 {
7 u32 id; // Triangle index
8 float depth; // 'depth testing'
9
10 v3f co;
11 v3f nrm;
12 };
13
14 struct csr_target
15 {
16 csr_frag *fragments;
17 u32 x, y;
18 v4f bounds;
19 };
20
21 struct csr_filter
22 {
23 const char *visgroup; // Limit to this visgroup only
24 const char *classname; // Limit to this exact classname. will not draw world
25 };
26
27 void csr_create_target( csr_target *rt, u32 x, u32 y, v4f bounds )
28 {
29 rt->x = x;
30 rt->y = y;
31 rt->fragments = (csr_frag *)csr_malloc( x*y*sizeof(csr_frag) );
32 v4_copy( bounds, rt->bounds );
33 }
34
35 void csr_rt_free( csr_target *rt )
36 {
37 free( rt->fragments );
38 }
39
40 void csr_rt_clear( csr_target *rt )
41 {
42 for( u32 i = 0; i < rt->x*rt->y; i ++ )
43 {
44 rt->fragments[ i ].depth = 0.f;
45 }
46 }
47
48 void simple_raster( csr_target *rt, vmf_vert tri[3], int id )
49 {
50 // Very simplified tracing algorithm
51 float tqa = 0.f, tqb = 0.f;
52
53 v2f bmin = { 0.f, 0.f };
54 v2f bmax = { rt->x, rt->y };
55
56 v2_minv( tri[0].co, tri[1].co, bmin );
57 v2_minv( tri[2].co, bmin, bmin );
58
59 v2_maxv( tri[0].co, tri[1].co, bmax );
60 v2_maxv( tri[2].co, bmax, bmax );
61
62 float range_x = (rt->bounds[2]-rt->bounds[0])/(float)rt->x;
63 float range_y = (rt->bounds[3]-rt->bounds[1])/(float)rt->y;
64
65 int start_x = csr_min( rt->x-1, csr_max( 0, floorf( (bmin[0]-rt->bounds[0])/range_x)));
66 int end_x = csr_max( 0, csr_min( rt->x-1, floorf( (bmax[0]-rt->bounds[0])/range_x )));
67 int start_y = csr_min( rt->y-1, csr_max( 0, ceilf( (bmin[1]-rt->bounds[1])/range_y )));
68 int end_y = csr_max( 0, csr_min( rt->y-1, ceilf( (bmax[1]-rt->bounds[1])/range_y )));
69
70 v3f trace_dir = { 0.f, 0.f, 1.f };
71 v3f trace_origin = { 0.f, 0.f, -16385.f };
72
73 for( u32 py = start_y; py <= end_y; py ++ )
74 {
75 trace_origin[1] = csr_lerpf( rt->bounds[1], rt->bounds[3], (float)py/(float)rt->y );
76
77 for( u32 px = start_x; px <= end_x; px ++ )
78 {
79 csr_frag *frag = &rt->fragments[ py * rt->y + px ];
80
81 trace_origin[0] = csr_lerpf( rt->bounds[0], rt->bounds[2], (float)px/(float)rt->x );
82 float tdepth = csr_ray_tri( trace_origin, trace_dir, tri[0].co, tri[1].co, tri[2].co, &tqa, &tqb );
83
84 if( tdepth > frag->depth )
85 {
86 frag->depth = tdepth;
87
88 v3_muls( tri[1].co, tqa, frag->co );
89 v3_muladds( frag->co, tri[2].co, tqb, frag->co );
90 v3_muladds( frag->co, tri[0].co, 1.f - tqa - tqb, frag->co );
91 }
92 }
93 }
94 }
95
96 void csr_draw( csr_target *rt, vmf_vert *triangles, u32 triangle_count, m4x3f transform )
97 {
98 m3x3f normal;
99 vmf_vert new_tri[3];
100
101 // Derive normal matrix
102 m4x3_to_3x3( transform, normal );
103 m3x3_inv_transpose( normal, normal );
104
105 for( u32 i = 0; i < triangle_count; i ++ )
106 {
107 vmf_vert *triangle = triangles + i*3;
108
109 m4x3_mulv( transform, triangle[0].co, new_tri[0].co );
110 m4x3_mulv( transform, triangle[1].co, new_tri[1].co );
111 m4x3_mulv( transform, triangle[2].co, new_tri[2].co );
112 m3x3_mulv( normal, triangle[0].nrm, new_tri[0].nrm );
113 m3x3_mulv( normal, triangle[1].nrm, new_tri[1].nrm );
114 m3x3_mulv( normal, triangle[2].nrm, new_tri[2].nrm );
115
116 simple_raster( rt, new_tri, 0 );
117 }
118 }
119
120 void draw_vmf_group( csr_target *rt, vmf_map *map, vdf_node *root, csr_filter *filter, m4x3f prev, m4x3f inst )
121 {
122 m4x3f transform = M4X3_IDENTITY;
123 vmf_solid solid;
124 vmf_vert tri[3];
125 vdf_node *ent_solid;
126
127 u32 group_id = 0;
128 int filter_visgroups = 0, filter_classname = 0;
129
130 if( filter )
131 {
132 if( filter->visgroup )
133 {
134 filter_visgroups = 1;
135 group_id = vmf_visgroup_id( root, filter->visgroup );
136 }
137
138 if( filter->classname )
139 {
140 filter_classname = 1;
141 }
142 }
143
144 // Multiply previous transform with instance transform to create basis
145 if( prev )
146 {
147 m4x3_mul( prev, inst, transform );
148 }
149
150 // Gather world brushes
151 solidgen_ctx_init( &solid );
152
153 if( !filter_classname )
154 {
155 vdf_node *world = vdf_next( root, "world", NULL );
156
157 vdf_foreach( world, "solid", brush )
158 {
159 if( filter_visgroups && !vmf_visgroup_match( brush, group_id ) )
160 continue;
161
162 solidgen_push( &solid, brush );
163 }
164 }
165
166 // Actual entity loop
167 m4x3f model;
168
169 vdf_foreach( root, "entity", ent )
170 {
171 if( filter_visgroups && !vmf_visgroup_match( ent, group_id ) )
172 continue;
173
174 if( filter_classname )
175 if( strcmp( kv_get( ent, "classname", "" ), filter->classname ) )
176 continue;
177
178 if( ent->user & VMF_FLAG_IS_PROP )
179 {
180 // Create model transform
181 m4x3_identity( model );
182
183 vmf_entity_transform( ent, model );
184 m4x3_mul( transform, model, model );
185
186 // Draw model
187 mdl_mesh_t *mdl = &map->models[ ent->user1 ].mdl;
188 for( int i = 0; i < mdl->num_indices/3; i ++ )
189 {
190 for( int j = 0; j < 3; j ++ )
191 {
192 v3_copy( &mdl->vertices[ mdl->indices[ i*3+j ] *8 ], tri[j].co );
193 v3_copy( &mdl->vertices[ mdl->indices[ i*3+j ] *8+3 ], tri[j].nrm );
194 tri[j].xy[0] = 0.f;
195 tri[j].xy[1] = 0.f;
196 }
197
198 csr_draw( rt, tri, 1, model );
199 }
200 }
201 else if( ent->user & VMF_FLAG_IS_INSTANCE )
202 {
203 m4x3_identity( model );
204 vmf_entity_transform( ent, model );
205
206 draw_vmf_group( rt, map, map->cache[ ent->user1 ].root, filter, transform, model );
207 }
208 else
209 {
210 // Brush entity
211 if( (ent_solid = vdf_next( ent, "solid", NULL )) )
212 {
213 solidgen_push( &solid, ent_solid );
214 }
215 }
216 }
217
218 // Draw brushes
219 for( int i = 0; i < csr_sb_count( solid.indices )/3; i ++ )
220 {
221 u32 * base = solid.indices + i*3;
222
223 tri[0] = solid.verts[ base[0] ];
224 tri[1] = solid.verts[ base[1] ];
225 tri[2] = solid.verts[ base[2] ];
226
227 csr_draw( rt, tri, 1, transform );
228 }
229
230 solidgen_ctx_reset( &solid );
231 solidgen_ctx_free( &solid );
232 }