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