740a3749da0af94070dabd7da5077aaae7ac8197
[csRadar.git] / vmf.h
1 // This software is not affiliated with Valve Corporation
2 // We are not affiliated, associated, authorized, endorsed by, or in any way officially
3 // connected with Valve Corporation, or any of its subsidiaries or its affiliates.
4 //
5 // All trademarks are property of their respective owners
6
7 typedef struct vmf_solid vmf_solid;
8 typedef struct vmf_vert vmf_vert;
9 typedef struct vmf_mat vmf_mat;
10 typedef struct vmf_face vmf_face;
11 typedef struct vmf_userdata vmf_userdata;
12 typedef struct vmf_map vmf_map;
13
14 typedef enum ESolidResult ESolidResult;
15
16 // API
17 //=======================================================================================================================
18
19 // Load vmf from disk
20 vmf_map *vmf_init( const char *path );
21 void vmf_free( vmf_map *map );
22
23 // Solidgen API ~ Converting brushes into meshes
24 // ---------------------------------------------
25 void solidgen_ctx_init( vmf_solid *ctx );
26 void solidgen_ctx_reset( vmf_solid *ctx );
27 void solidgen_ctx_free( vmf_solid *ctx );
28 void solidgen_bounds( vmf_solid *ctx, boxf box );
29
30 ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node );
31
32 // General VMF
33 // -----------
34 int solid_has_displacement( vdf_node *node );
35 int vmf_class_is_prop( vdf_node *ent );
36
37 // Build the list of all models used in this map, including instances
38 void vmf_index_models( vmf_map *map );
39
40 // Loads all models that have the resource flagged with need_load
41 void vmf_load_models( vmf_map *map );
42
43 // Create matrix describing this entities transform
44 void vmf_entity_transform( vdf_node *ent, m4x3f mat );
45
46 int vmf_visgroup_id( vdf_node *root, const char *name );
47 int vmf_visgroup_match( vdf_node *ent, u32 target );
48
49 // Currently unused
50 //void vmf_addbisector( double p[4] );
51 //void vmf_clearbisectors( void );
52 //void vmf_ignore_mat( const char *material );
53 //void vmf_clearignore( void );
54
55 // Implementation
56 //=======================================================================================================================
57
58 #define SOLID_MAX_SIDES 512
59 #define VMF_FLAG_IS_PROP 0x1
60 #define VMF_FLAG_IS_INSTANCE 0x2
61 #define VMF_FLAG_BRUSH_ENT 0x4
62
63 enum ESolidResult
64 {
65 k_ESolidResult_valid,
66 k_ESolidResult_maxsides,
67 k_ESolidResult_invalid,
68 k_ESolidResult_errnomem,
69 k_ESolidResult_corrupt,
70 k_ESolidResult_degenerate
71 };
72
73 struct vmf_vert
74 {
75 v3f co;
76 v3f nrm;
77 v3f origin;
78 };
79
80 struct vmf_face
81 {
82 u32 *indices;
83
84 vdf_node *dispinfo;
85
86 const char *material;
87 int blacklisted;
88 };
89
90 struct vmf_solid
91 {
92 vmf_vert *verts;
93 u32 *indices;
94 };
95
96 struct vmf_mat
97 {
98 char *str;
99 u32 hash;
100 };
101
102 struct vmf_map
103 {
104 vdf_node *root;
105
106 struct vmf_model
107 {
108 char *str;
109 u32 hash;
110
111 mdl_mesh_t mdl;
112
113 int need_load;
114 }
115 *models;
116
117 struct vmf_instance
118 {
119 char *name;
120 u32 hash;
121
122 vdf_node *root;
123
124 m4x3f transform;
125 }
126 *cache;
127
128 m4x3f transform;
129 };
130
131 #ifdef VALVE_IMPLEMENTATION
132
133 void solidgen_ctx_reset( vmf_solid *ctx )
134 {
135 csr_sb_clear( ctx->verts );
136 csr_sb_clear( ctx->indices );
137 }
138
139 void solidgen_ctx_init( vmf_solid *ctx )
140 {
141 const u32 init_size = 128;
142
143 ctx->verts = csr_sb_reserve( NULL, init_size, sizeof(vmf_vert) );
144 ctx->indices = csr_sb_reserve( NULL, init_size, sizeof(u32) );
145 }
146
147 void solidgen_ctx_free( vmf_solid *ctx )
148 {
149 csr_sb_free( ctx->verts );
150 csr_sb_free( ctx->indices );
151 }
152
153 // Compute bounds of solid gen ctx
154 void solidgen_bounds( vmf_solid *ctx, boxf box )
155 {
156 v3f mine = { INFINITY, INFINITY, INFINITY };
157 v3f maxe = {-INFINITY,-INFINITY,-INFINITY };
158
159 for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ )
160 {
161 vmf_vert *vert = ctx->verts + i;
162 v3_minv( mine, vert->co, mine );
163 v3_maxv( maxe, vert->co, maxe );
164 }
165
166 v3_copy( mine, box[0] );
167 v3_copy( maxe, box[1] );
168 }
169
170 struct
171 {
172 vmf_mat *blacklist;
173
174 double planes[ SOLID_MAX_SIDES*4 ];
175 u32 bisectors;
176 }
177 vmf_api;
178
179 // put an extra plane into the planes list
180 void vmf_addbisector( double p[4] )
181 {
182 double *plane = vmf_api.planes + vmf_api.bisectors * 4;
183
184 plane[0] = p[0];
185 plane[1] = p[1];
186 plane[2] = p[2];
187 plane[3] = p[3];
188
189 vmf_api.bisectors ++;
190 }
191
192 void vmf_clearbisectors( void )
193 {
194 vmf_api.bisectors = 0;
195 }
196
197 void vmf_ignore_mat( const char *material )
198 {
199 vmf_api.blacklist = csr_sb_reserve( vmf_api.blacklist, 1, sizeof( vmf_mat ) );
200 vmf_mat *mat = (vmf_mat *)csr_sb_use( vmf_api.blacklist );
201
202 mat->str = csr_malloc( strlen( material ) + 1 );
203 strcpy( mat->str, material );
204
205 mat->hash = djb2( ( const unsigned char * )material );
206 }
207
208 void vmf_clearignore( void )
209 {
210 for( int i = 0; i < csr_sb_count( vmf_api.blacklist ); i ++ )
211 {
212 free( vmf_api.blacklist[ i ].str );
213 }
214
215 csr_sb_free( vmf_api.blacklist );
216 vmf_api.blacklist = NULL;
217 }
218
219 int mat_blacklisted( const char *material )
220 {
221 u32 hash = djb2((const u8 *)material);
222
223 for( int j = 0; j < csr_sb_count( vmf_api.blacklist ); j ++ )
224 {
225 if( vmf_api.blacklist[ j ].hash == hash )
226 {
227 if( !strcmp( material, vmf_api.blacklist[ j ].str ) )
228 {
229 return 1;
230 }
231 }
232 }
233
234 return 0;
235 }
236
237 void sort_coplanar( double p[4], vmf_vert *points, u32 *indices, u32 count )
238 {
239 v3f center = {0.f, 0.f, 0.f};
240 v3f norm;
241 v3d_v3f( p, norm );
242 v3_normalize( norm );
243
244 for( int i = 0; i < count; i ++ )
245 {
246 v3_add( points[ indices[i] ].co, center, center );
247 }
248 v3_divs( center, count, center );
249
250 v3f ref;
251 v3_sub( points[ indices[0] ].co, center, ref );
252
253 // Calc angles compared to ref
254 float *angles = (float*)alloca( sizeof(float)*count );
255 for( int i = 0; i < count; i ++ )
256 {
257 v3f diff;
258 v3f c;
259
260 v3_sub( points[ indices[i] ].co, center, diff );
261 v3_cross( diff, ref, c );
262
263 angles[i] =
264 atan2f( v3_length(c), v3_dot( diff, ref ) )
265 * (v3_dot( c, norm ) < 0.f ? -1.f: 1.f);
266 }
267
268 // Temporary local indexes
269 u32 *temp_indices = (u32 *)alloca( sizeof(u32)*count );
270 for( u32 i = 0; i < count; i ++ ) temp_indices[i] = i;
271
272 // Slow sort on large vertex counts
273 int it = 0;
274 while(1)
275 {
276 int modified = 0;
277 for( int i = 0; i < count-1; i ++ )
278 {
279 int s0 = i; int s1 = i + 1;
280
281 if( angles[temp_indices[s0]] > angles[temp_indices[s1]] )
282 {
283 // swap indices and mirror on local
284 u32 temp = indices[s1];
285 indices[s1] = indices[s0];
286 indices[s0] = temp;
287
288 temp = temp_indices[s1];
289 temp_indices[s1] = temp_indices[s0];
290 temp_indices[s0] = temp;
291
292 modified = 1;
293 }
294 }
295
296 it ++;
297 if( !modified ) break;
298 }
299 }
300
301 int solid_has_displacement( vdf_node *node )
302 {
303 int it = 0;
304 vdf_node *pSide;
305
306 while( (pSide = vdf_next(node, "side", &it)) )
307 {
308 if( vdf_next( pSide, "dispinfo", NULL ) )
309 {
310 return 1;
311 }
312 }
313 return 0;
314 }
315
316 void solid_disp_tri( vmf_solid *ctx, u32 a, u32 b, u32 c )
317 {
318 *((u32 *)csr_sb_use( ctx->indices )) = a;
319 *((u32 *)csr_sb_use( ctx->indices )) = b;
320 *((u32 *)csr_sb_use( ctx->indices )) = c;
321 }
322
323 void face_add_indice( vmf_face *f, u32 idx )
324 {
325 f->indices = csr_sb_reserve( f->indices, 1, sizeof( u32 ) );
326 *((u32 *)csr_sb_use( f->indices )) = idx;
327 }
328
329 ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node )
330 {
331 ESolidResult flag = k_ESolidResult_valid;
332
333 double planes[ SOLID_MAX_SIDES*4 ];
334 vmf_face faces[ SOLID_MAX_SIDES ];
335
336 int is_displacement = solid_has_displacement( node );
337 int num_planes = 0;
338
339 int it = 0;
340 vdf_node *pSide;
341 while( (pSide = vdf_next(node, "side", &it)) )
342 {
343 if( num_planes >= SOLID_MAX_SIDES )
344 {
345 flag = k_ESolidResult_maxsides;
346 log_error( "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES );
347 break;
348 }
349
350 double points[3*3];
351
352 vmf_face *face = faces + num_planes;
353 face->indices = NULL;
354 face->dispinfo = vdf_next( pSide, "dispinfo", NULL );
355 face->material = kv_get( pSide, "material", "" );
356 face->blacklisted = mat_blacklisted( face->material );
357
358 kv_double_array( pSide, "plane", 9, points );
359
360 tri_to_plane( points+6, points+3, points+0, planes + num_planes * 4 );
361 num_planes ++;
362 }
363
364 // Compute plane intersections
365 int i[3];
366 csr_comb_init( 3, i );
367
368 v3f center = { 0.f, 0.f, 0.f };
369 int numpoints = 0;
370 u32 vert_start = csr_sb_count( ctx->verts );
371
372 do
373 {
374 // DO something with i j k
375 double p[3];
376
377 if( (faces[ i[0] ].blacklisted && faces[ i[1] ].blacklisted && faces[ i[2] ].blacklisted) )
378 continue;
379
380 if( !plane_intersect( planes+i[0]*4, planes+i[1]*4, planes+i[2]*4, p ) )
381 continue;
382
383 // Check for illegal verts (eg: got clipped by bisectors)
384 int valid = 1;
385 for( int m = 0; m < num_planes; m ++ )
386 {
387 if( plane_polarity( planes+m*4, p ) > 1e-6f )
388 {
389 valid = 0;
390 break;
391 }
392 }
393
394 if( valid )
395 {
396 ctx->verts = csr_sb_reserve( ctx->verts, 3, sizeof( vmf_vert ) );
397
398 // Take the vertex position and add it for centering base on average
399 numpoints ++;
400 v3_add( (v3f){ p[0], p[1], p[2] }, center, center );
401
402 // Store point / respecive normal for each plane that triggered the collision
403 for( int k = 0; k < 3; k ++ )
404 {
405 if( !faces[ i[k] ].blacklisted )
406 {
407 u32 c = csr_sb_count( ctx->verts );
408
409 face_add_indice( faces + i[k], c );
410
411 v3d_v3f( p, ctx->verts[ c ].co );
412 v3d_v3f( planes+i[k]*4, ctx->verts[ c ].nrm );
413
414 csr_sb_inc( ctx->verts, 1 );
415 }
416 }
417 }
418 }
419 while( csr_comb( 3, num_planes, i ) );
420
421 // Retrospectively set the center for each point
422 v3_divs( center, (float)numpoints, center );
423 for( ; vert_start < csr_sb_count( ctx->verts ); vert_start ++ )
424 {
425 v3_copy( center, ctx->verts[ vert_start ].origin );
426 }
427
428 // Sort each faces and trianglulalate them
429 for( int k = 0; k < num_planes; k ++ )
430 {
431 vmf_face *face = faces + k;
432
433 if( face->blacklisted ) continue;
434
435 if( csr_sb_count( face->indices ) < 3 )
436 {
437 flag = k_ESolidResult_degenerate;
438 log_error( "Skipping degenerate face\n" );
439 continue;
440 }
441
442 // Sort only if there is no displacements, or if this side is
443 if( !is_displacement || ( is_displacement && face->dispinfo ) )
444 {
445 sort_coplanar( planes+k*4, ctx->verts, face->indices, csr_sb_count( face->indices ) );
446 }
447
448 if( is_displacement )
449 {
450 // Compute displacement
451 if( face->dispinfo )
452 {
453 if( csr_sb_count( face->indices ) != 4 )
454 {
455 flag = k_ESolidResult_degenerate;
456 log_error( "Skipping degenerate displacement\n" );
457 continue;
458 }
459
460 // Match starting position
461 v3f start;
462 int sw = 0;
463 float dmin = 999999.f;
464
465 vdf_node *dispinfo = face->dispinfo;
466 vdf_node *vdf_normals = vdf_next( dispinfo, "normals", NULL );
467 vdf_node *vdf_distances = vdf_next( dispinfo, "distances", NULL );
468
469 kv_float_array( dispinfo, "startposition", 3, start );
470
471 for( int j = 0; j < csr_sb_count( face->indices ); j ++ )
472 {
473 float d2 = v3_dist2( start, ctx->verts[ face->indices[ j ] ].co );
474 if( d2 < dmin )
475 {
476 dmin = d2;
477 sw = j;
478 }
479 }
480
481 // Can be either 5, 9, 17
482 numpoints = pow( 2, kv_get_int( dispinfo, "power", 2 ) ) + 1;
483 u32 reqverts = numpoints*numpoints;
484 u32 reqidx = (numpoints-1)*(numpoints-1)*6;
485
486 ctx->verts = csr_sb_reserve( ctx->verts, reqverts, sizeof( vmf_vert ) );
487 ctx->indices = csr_sb_reserve( ctx->indices, reqidx, sizeof( u32 ) );
488
489 // Get corners of displacement
490 float *SW = ctx->verts[ face->indices[ sw ] ].co;
491 float *NW = ctx->verts[ face->indices[ (sw+1) % 4] ].co;
492 float *NE = ctx->verts[ face->indices[ (sw+2) % 4] ].co;
493 float *SE = ctx->verts[ face->indices[ (sw+3) % 4] ].co;
494
495 float normals[ 17*3 ];
496 float distances[ 17 ];
497
498 // Calculate displacement positions
499 for( int j = 0; j < numpoints; j ++ )
500 {
501 char key[14];
502 sprintf( key, "row%i", j );
503
504 kv_float_array( vdf_normals, key, 17*3, normals );
505 kv_float_array( vdf_distances, key, 17, distances );
506
507 float dx = (float)j / (float)(numpoints - 1); //Time values for linear interpolation
508
509 for( int m = 0; m < numpoints; m ++ )
510 {
511 vmf_vert *vert = &ctx->verts[ csr_sb_count( ctx->verts ) + j*numpoints + m ];
512
513 float dy = (float)m / (float)(numpoints - 1);
514
515 v3f lwr; v3f upr;
516
517 v3_lerp( SW, SE, dx, lwr );
518 v3_lerp( NW, NE, dx, upr );
519 v3_lerp( lwr, upr, dy, vert->co );
520
521 v3_muladds( vert->co, normals + m * 3, distances[ m ], vert->co );
522
523 // Todo, put correct normal
524 v3_copy( (v3f){ 0.f, 0.f, 1.f }, vert->nrm );
525
526 // Todo: use real bounds of displaced vertices
527 v3_copy( center, vert->origin );
528 }
529 }
530
531 // Build displacement indices
532 int condition = 0;
533 for( int row = 0; row < numpoints - 1; row ++ )
534 {
535 for( int col = 0; col < numpoints - 1; col ++ )
536 {
537 u32 c = csr_sb_count( ctx->verts );
538
539 u32 idxsw = c + ( row + 0 ) * numpoints + col + 0 ;
540 u32 idxse = c + ( row + 0 ) * numpoints + col + 1 ;
541 u32 idxnw = c + ( row + 1 ) * numpoints + col + 0 ;
542 u32 idxne = c + ( row + 1 ) * numpoints + col + 1 ;
543
544 if( (condition ++) % 2 == 0 )
545 {
546 solid_disp_tri( ctx, idxne, idxnw, idxsw );
547 solid_disp_tri( ctx, idxse, idxne, idxsw );
548 }
549 else
550 {
551 solid_disp_tri( ctx, idxse, idxnw, idxsw );
552 solid_disp_tri( ctx, idxse, idxne, idxnw );
553 }
554 }
555 condition ++;
556 }
557
558 csr_sb_inc( ctx->verts, numpoints*numpoints );
559 }
560 }
561 else
562 {
563 u32 tris = csr_sb_count( face->indices ) -2;
564 ctx->indices = csr_sb_reserve( ctx->indices, tris*3, sizeof( u32 ) );
565
566 u32 c = csr_sb_count( ctx->indices );
567
568 for( int j = 0; j < tris; j ++ )
569 {
570 ctx->indices[ c +j*3 +0 ] = face->indices[ 0 ];
571 ctx->indices[ c +j*3 +1 ] = face->indices[ j+1 ];
572 ctx->indices[ c +j*3 +2 ] = face->indices[ j+2 ];
573
574 // A 0,1,2:: A,B,C
575 // D B 0,2,3:: A,C,D
576 // C
577 }
578
579 csr_sb_inc( ctx->indices, tris*3 );
580 }
581 }
582
583 // Free temp polyon buffers
584 for( int j = 0; j < num_planes; j ++ )
585 {
586 csr_sb_free( faces[ j ].indices );
587 }
588
589 return flag;
590 }
591
592 u32 vmf_get_mdl( vmf_map *map, const char *mdl )
593 {
594 u32 hash = djb2( (const unsigned char *)mdl );
595
596 for( u32 i = 0; i < csr_sb_count( map->models ); i ++ )
597 {
598 if( hash == map->models[i].hash && !strcmp( map->models[i].str, mdl ) )
599 {
600 return i;
601 }
602 }
603
604 return 0;
605 }
606
607 int vmf_class_is_prop( vdf_node *ent )
608 {
609 return !strncmp( kv_get( ent, "classname", "" ), "prop_", 5 );
610 }
611
612 void vmf_populate_models( vdf_node *vmf, vmf_map *map )
613 {
614 vdf_foreach( vmf, "entity", ent )
615 {
616 // Use any class name with prop_
617 if( vmf_class_is_prop( ent ) )
618 {
619 // Check if it exists
620 const char *model_path = kv_get( ent, "model", "" );
621 u32 mdl_id = vmf_get_mdl( map, model_path );
622
623 if( !mdl_id )
624 {
625 map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
626
627 struct vmf_model *entry = &map->models[ csr_sb_count( map->models ) ];
628 entry->need_load = 0;
629 mdl_error( &entry->mdl ); // (just setting arrays to 0)
630
631 entry->str = csr_malloc( strlen( model_path ) +1 );
632 strcpy( entry->str, model_path );
633 entry->hash = djb2( (const unsigned char *)model_path );
634
635 mdl_id = csr_sb_count( map->models );
636 csr_sb_use( map->models );
637 }
638
639 // Assign prop-ID for later use
640 ent->user = VMF_FLAG_IS_PROP;
641 ent->user1 = mdl_id;
642 }
643 }
644 }
645
646 // Load all models
647 void vmf_index_models( vmf_map *map )
648 {
649 log_info( "Indexing all models\n" );
650
651 // Error model == blank
652 map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
653 csr_sb_use( map->models );
654 mdl_error( &map->models[0].mdl );
655
656 // Create listings for each model
657 vmf_populate_models( map->root, map );
658
659 for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
660 {
661 vmf_populate_models( map->cache[i].root, map );
662 }
663
664 log_info( "Indexed (%u) models\n", csr_sb_count( map->models )-1 );
665 }
666
667 void vmf_load_models( vmf_map *map )
668 {
669 u32 num_success = 0;
670 u32 num_reqs = 0;
671
672 for( int i = 1; i < csr_sb_count( map->models ); i ++ )
673 {
674 struct vmf_model *mdl = &map->models[i];
675
676 if( mdl->need_load )
677 {
678 num_reqs ++;
679
680 if( mdl_from_find_files( mdl->str, &mdl->mdl ) )
681 {
682 num_success ++;
683 }
684 else
685 {
686 log_warn( "Failed to load model: %s\n", mdl->str );
687 }
688 }
689 }
690
691 log_info( "Done (%u of %u loaded)\n", num_success, num_reqs );
692 }
693
694 u32 vmf_init_subvmf( vmf_map *map, const char *subvmf );
695
696 void vmf_load_all_instances( vmf_map *map, vdf_node *vmf )
697 {
698 char nextvmf[ 512 ];
699 const char *base = kv_get( vmf, "csr_path", "" );
700
701 vdf_foreach( vmf, "entity", ent )
702 {
703 if( !strcmp( kv_get( ent, "classname", "" ), "func_instance" ))
704 {
705 // Entity is in use if file is specified, if not just ignore the entity.
706 const char *path = kv_get( ent, "file", NULL );
707
708 if( path )
709 {
710 // Make relative path real
711 strcpy( nextvmf, base );
712 csr_downlvl( nextvmf );
713 strcat( nextvmf, path );
714
715 if( (ent->user1 = vmf_init_subvmf( map, nextvmf )))
716 {
717 ent->user1 --;
718 ent->user = VMF_FLAG_IS_INSTANCE;
719 }
720 }
721 }
722 }
723 }
724
725 // TODO: Merge this into above function.. doesnt need to be seperated
726 u32 vmf_init_subvmf( vmf_map *map, const char *subvmf )
727 {
728 u32 id;
729 u32 hash = djb2( (const unsigned char *)subvmf );
730
731 // Check if present
732 for( u32 i = 0; i < csr_sb_count( map->cache ); i ++ )
733 {
734 if( hash == map->cache[i].hash )
735 {
736 if( !strcmp( map->cache[i].name, subvmf ) )
737 {
738 if( map->cache[i].root )
739 return i+1;
740 else
741 return 0;
742 }
743 }
744 }
745
746 id = csr_sb_count( map->cache );
747 map->cache = csr_sb_reserve( map->cache, 1, sizeof( struct vmf_instance ));
748 struct vmf_instance *inst = &map->cache[ id ];
749
750 csr_sb_use( map->cache );
751 inst->hash = hash;
752 inst->name = csr_malloc( strlen( subvmf )+1 );
753 strcpy( inst->name, subvmf );
754
755 if( (inst->root = vdf_open_file( subvmf )) )
756 {
757 vdf_kv_append( inst->root, "csr_path", subvmf );
758
759 // Recursive load other instances
760 vmf_load_all_instances( map, inst->root );
761 return id+1;
762 }
763 else
764 {
765 log_error( "Failed to load instance file: %s\n", subvmf );
766 return 0;
767 }
768 }
769
770 vmf_map *vmf_init( const char *path )
771 {
772 vmf_map *map = csr_calloc( sizeof( vmf_map ) );
773 map->root = vdf_open_file( path );
774
775 if( !map->root )
776 {
777 free( map );
778 return NULL;
779 }
780
781 vdf_kv_append( map->root, "csr_path", path );
782
783 // Prepare instances
784 vmf_load_all_instances( map, map->root );
785
786 // Other resources
787 vmf_index_models( map );
788 return map;
789 }
790
791 void vmf_free( vmf_map *map )
792 {
793 for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
794 {
795 if( map->cache[i].root )
796 vdf_free_r( map->cache[i].root );
797
798 free( map->cache[i].name );
799 }
800
801 for( int i = 1; i < csr_sb_count( map->models ); i ++ )
802 {
803 free( map->models[i].str );
804 mdl_free( &map->models[i].mdl );
805 }
806
807 vdf_free_r( map->root );
808 csr_sb_free( map->models );
809 csr_sb_free( map->cache );
810 free( map );
811 }
812
813 void vmf_entity_transform( vdf_node *ent, m4x3f mat )
814 {
815 v3f angles = {0.f,0.f,0.f};
816 v3f offset = {0.f,0.f,0.f};
817 float scale;
818
819 // Parse
820 scale = kv_get_float( ent, "uniformscale", 1.f );
821 kv_float_array( ent, "angles", 3, angles );
822 kv_float_array( ent, "origin", 3, offset );
823
824 // Translation
825 m4x3_translate( mat, offset );
826
827 // Make rotation ( Pitch yaw roll // YZX. Source->OpenGL ordering a lil messed up )
828 m4x3_rotate_z( mat, csr_rad( angles[1] ) );
829 m4x3_rotate_y( mat, csr_rad( angles[0] ) );
830 m4x3_rotate_x( mat, csr_rad( angles[2] ) );
831
832 // Scale
833 m4x3_scale( mat, scale );
834 }
835
836 int vmf_visgroup_id( vdf_node *root, const char *name )
837 {
838 vdf_node *dict = vdf_next( root, "visgroups", NULL );
839
840 if( dict )
841 {
842 vdf_foreach( dict, "visgroup", group )
843 {
844 if( !strcmp( kv_get( group, "name", "" ), name ) )
845 {
846 return kv_get_int( group, "visgroupid", 0 );
847 }
848 }
849 }
850
851 return -1;
852 }
853
854 int vmf_visgroup_match( vdf_node *ent, u32 target )
855 {
856 vdf_node *editor = vdf_next( ent, "editor", NULL );
857
858 if( editor )
859 {
860 kv_foreach( editor, "visgroupid", groupe )
861 {
862 if( target == atoi( groupe ) )
863 return 1;
864 }
865 }
866
867 return 0;
868 }
869
870 #endif