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