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