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.
5 // All trademarks are property of their respective owners
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
;
14 typedef enum ESolidResult ESolidResult
;
17 //=======================================================================================================================
20 vmf_map
*vmf_init( const char *path
);
21 void vmf_free( vmf_map
*map
);
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
);
30 ESolidResult
solidgen_push( vmf_solid
*ctx
, vdf_node
*node
);
34 int solid_has_displacement( vdf_node
*node
);
35 int vmf_class_is_prop( vdf_node
*ent
);
37 // Build the list of all models used in this map, including instances
38 void vmf_index_models( vmf_map
*map
);
40 // Loads all models that have the resource flagged with need_load
41 void vmf_load_models( vmf_map
*map
);
43 // Create matrix describing this entities transform
44 void vmf_entity_transform( vdf_node
*ent
, m4x3f mat
);
46 u32
vmf_visgroup_id( vdf_node
*root
, const char *name
);
47 int vmf_visgroup_match( vdf_node
*ent
, u32 target
);
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 );
56 //=======================================================================================================================
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
66 k_ESolidResult_maxsides
,
67 k_ESolidResult_invalid
,
68 k_ESolidResult_errnomem
,
69 k_ESolidResult_corrupt
,
70 k_ESolidResult_degenerate
131 #ifdef VALVE_IMPLEMENTATION
133 void solidgen_ctx_reset( vmf_solid
*ctx
)
135 csr_sb_clear( ctx
->verts
);
136 csr_sb_clear( ctx
->indices
);
139 void solidgen_ctx_init( vmf_solid
*ctx
)
141 const u32 init_size
= 128;
143 ctx
->verts
= csr_sb_reserve( NULL
, init_size
, sizeof(vmf_vert
) );
144 ctx
->indices
= csr_sb_reserve( NULL
, init_size
, sizeof(u32
) );
147 void solidgen_ctx_free( vmf_solid
*ctx
)
149 csr_sb_free( ctx
->verts
);
150 csr_sb_free( ctx
->indices
);
153 // Compute bounds of solid gen ctx
154 void solidgen_bounds( vmf_solid
*ctx
, boxf box
)
156 v3f mine
= { INFINITY
, INFINITY
, INFINITY
};
157 v3f maxe
= {-INFINITY
,-INFINITY
,-INFINITY
};
159 for( int i
= 0; i
< csr_sb_count( ctx
->verts
); i
++ )
161 vmf_vert
*vert
= ctx
->verts
+ i
;
162 v3_minv( mine
, vert
->co
, mine
);
163 v3_maxv( maxe
, vert
->co
, maxe
);
166 v3_copy( mine
, box
[0] );
167 v3_copy( maxe
, box
[1] );
174 double planes
[ SOLID_MAX_SIDES
*4 ];
179 // put an extra plane into the planes list
180 void vmf_addbisector( double p
[4] )
182 double *plane
= vmf_api
.planes
+ vmf_api
.bisectors
* 4;
189 vmf_api
.bisectors
++;
192 void vmf_clearbisectors( void )
194 vmf_api
.bisectors
= 0;
197 void vmf_ignore_mat( const char *material
)
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
);
202 mat
->str
= csr_malloc( strlen( material
) + 1 );
203 strcpy( mat
->str
, material
);
205 mat
->hash
= djb2( ( const unsigned char * )material
);
208 void vmf_clearignore( void )
210 for( int i
= 0; i
< csr_sb_count( vmf_api
.blacklist
); i
++ )
212 free( vmf_api
.blacklist
[ i
].str
);
215 csr_sb_free( vmf_api
.blacklist
);
216 vmf_api
.blacklist
= NULL
;
219 int mat_blacklisted( const char *material
)
221 u32 hash
= djb2((const u8
*)material
);
223 for( int j
= 0; j
< csr_sb_count( vmf_api
.blacklist
); j
++ )
225 if( vmf_api
.blacklist
[ j
].hash
== hash
)
227 if( !strcmp( material
, vmf_api
.blacklist
[ j
].str
) )
237 void sort_coplanar( double p
[4], vmf_vert
*points
, u32
*indices
, u32 count
)
239 v3f center
= {0.f
, 0.f
, 0.f
};
242 v3_normalize( norm
);
244 for( int i
= 0; i
< count
; i
++ )
246 v3_add( points
[ indices
[i
] ].co
, center
, center
);
248 v3_divs( center
, count
, center
);
251 v3_sub( points
[ indices
[0] ].co
, center
, ref
);
253 // Calc angles compared to ref
254 float *angles
= (float*)alloca( sizeof(float)*count
);
255 for( int i
= 0; i
< count
; i
++ )
260 v3_sub( points
[ indices
[i
] ].co
, center
, diff
);
261 v3_cross( diff
, ref
, c
);
264 atan2f( v3_length(c
), v3_dot( diff
, ref
) )
265 * (v3_dot( c
, norm
) < 0.f
? -1.f
: 1.f
);
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
;
272 // Slow sort on large vertex counts
277 for( int i
= 0; i
< count
-1; i
++ )
279 int s0
= i
; int s1
= i
+ 1;
281 if( angles
[temp_indices
[s0
]] > angles
[temp_indices
[s1
]] )
283 // swap indices and mirror on local
284 u32 temp
= indices
[s1
];
285 indices
[s1
] = indices
[s0
];
288 temp
= temp_indices
[s1
];
289 temp_indices
[s1
] = temp_indices
[s0
];
290 temp_indices
[s0
] = temp
;
297 if( !modified
) break;
301 int solid_has_displacement( vdf_node
*node
)
306 while( (pSide
= vdf_next(node
, "side", &it
)) )
308 if( vdf_next( pSide
, "dispinfo", NULL
) )
316 void solid_disp_tri( vmf_solid
*ctx
, u32 a
, u32 b
, u32 c
)
318 *((u32
*)csr_sb_use( ctx
->indices
)) = a
;
319 *((u32
*)csr_sb_use( ctx
->indices
)) = b
;
320 *((u32
*)csr_sb_use( ctx
->indices
)) = c
;
323 void face_add_indice( vmf_face
*f
, u32 idx
)
325 f
->indices
= csr_sb_reserve( f
->indices
, 1, sizeof( u32
) );
326 *((u32
*)csr_sb_use( f
->indices
)) = idx
;
329 ESolidResult
solidgen_push( vmf_solid
*ctx
, vdf_node
*node
)
331 ESolidResult flag
= k_ESolidResult_valid
;
333 double planes
[ SOLID_MAX_SIDES
*4 ];
334 vmf_face faces
[ SOLID_MAX_SIDES
];
336 int is_displacement
= solid_has_displacement( node
);
341 while( (pSide
= vdf_next(node
, "side", &it
)) )
343 if( num_planes
>= SOLID_MAX_SIDES
)
345 flag
= k_ESolidResult_maxsides
;
346 log_error( "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES
);
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
);
358 kv_double_array( pSide
, "plane", 9, points
);
360 tri_to_plane( points
+6, points
+3, points
+0, planes
+ num_planes
* 4 );
364 // Compute plane intersections
366 csr_comb_init( 3, i
);
368 v3f center
= { 0.f
, 0.f
, 0.f
};
370 u32 vert_start
= csr_sb_count( ctx
->verts
);
374 // DO something with i j k
377 if( (faces
[ i
[0] ].blacklisted
&& faces
[ i
[1] ].blacklisted
&& faces
[ i
[2] ].blacklisted
) )
380 if( !plane_intersect( planes
+i
[0]*4, planes
+i
[1]*4, planes
+i
[2]*4, p
) )
383 // Check for illegal verts (eg: got clipped by bisectors)
385 for( int m
= 0; m
< num_planes
; m
++ )
387 if( plane_polarity( planes
+m
*4, p
) > 1e-6f
)
396 ctx
->verts
= csr_sb_reserve( ctx
->verts
, 3, sizeof( vmf_vert
) );
398 // Take the vertex position and add it for centering base on average
400 v3_add( (v3f
){ p
[0], p
[1], p
[2] }, center
, center
);
402 // Store point / respecive normal for each plane that triggered the collision
403 for( int k
= 0; k
< 3; k
++ )
405 if( !faces
[ i
[k
] ].blacklisted
)
407 u32 c
= csr_sb_count( ctx
->verts
);
409 face_add_indice( faces
+ i
[k
], c
);
411 v3d_v3f( p
, ctx
->verts
[ c
].co
);
412 v3d_v3f( planes
+i
[k
]*4, ctx
->verts
[ c
].nrm
);
414 csr_sb_inc( ctx
->verts
, 1 );
419 while( csr_comb( 3, num_planes
, i
) );
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
++ )
425 v2_copy( center
, ctx
->verts
[ vert_start
].xy
);
428 // Sort each faces and trianglulalate them
429 for( int k
= 0; k
< num_planes
; k
++ )
431 vmf_face
*face
= faces
+ k
;
433 if( face
->blacklisted
) continue;
435 if( csr_sb_count( face
->indices
) < 3 )
437 flag
= k_ESolidResult_degenerate
;
438 log_error( "Skipping degenerate face\n" );
442 // Sort only if there is no displacements, or if this side is
443 if( !is_displacement
|| ( is_displacement
&& face
->dispinfo
) )
445 sort_coplanar( planes
+k
*4, ctx
->verts
, face
->indices
, csr_sb_count( face
->indices
) );
448 if( is_displacement
)
450 // Compute displacement
453 if( csr_sb_count( face
->indices
) != 4 )
455 flag
= k_ESolidResult_degenerate
;
456 log_error( "Skipping degenerate displacement\n" );
460 // Match starting position
463 float dmin
= 999999.f
;
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
);
469 kv_float_array( dispinfo
, "startposition", 3, start
);
471 for( int j
= 0; j
< csr_sb_count( face
->indices
); j
++ )
473 float d2
= v3_dist2( start
, ctx
->verts
[ face
->indices
[ j
] ].co
);
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;
486 ctx
->verts
= csr_sb_reserve( ctx
->verts
, reqverts
, sizeof( vmf_vert
) );
487 ctx
->indices
= csr_sb_reserve( ctx
->indices
, reqidx
, sizeof( u32
) );
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
;
495 float normals
[ 17*3 ];
496 float distances
[ 17 ];
498 // Calculate displacement positions
499 for( int j
= 0; j
< numpoints
; j
++ )
502 sprintf( key
, "row%i", j
);
504 kv_float_array( vdf_normals
, key
, 17*3, normals
);
505 kv_float_array( vdf_distances
, key
, 17, distances
);
507 float dx
= (float)j
/ (float)(numpoints
- 1); //Time values for linear interpolation
509 for( int m
= 0; m
< numpoints
; m
++ )
511 vmf_vert
*vert
= &ctx
->verts
[ csr_sb_count( ctx
->verts
) + j
*numpoints
+ m
];
513 float dy
= (float)m
/ (float)(numpoints
- 1);
517 v3_lerp( SW
, SE
, dx
, lwr
);
518 v3_lerp( NW
, NE
, dx
, upr
);
519 v3_lerp( lwr
, upr
, dy
, vert
->co
);
521 v3_muladds( vert
->co
, normals
+ m
* 3, distances
[ m
], vert
->co
);
523 // Todo, put correct normal
524 v3_copy( (v3f
){ 0.f
, 0.f
, 1.f
}, vert
->nrm
);
528 // Build displacement indices
530 for( int row
= 0; row
< numpoints
- 1; row
++ )
532 for( int col
= 0; col
< numpoints
- 1; col
++ )
534 u32 c
= csr_sb_count( ctx
->verts
);
536 u32 idxsw
= c
+ ( row
+ 0 ) * numpoints
+ col
+ 0 ;
537 u32 idxse
= c
+ ( row
+ 0 ) * numpoints
+ col
+ 1 ;
538 u32 idxnw
= c
+ ( row
+ 1 ) * numpoints
+ col
+ 0 ;
539 u32 idxne
= c
+ ( row
+ 1 ) * numpoints
+ col
+ 1 ;
541 if( (condition
++) % 2 == 0 )
543 solid_disp_tri( ctx
, idxne
, idxnw
, idxsw
);
544 solid_disp_tri( ctx
, idxse
, idxne
, idxsw
);
548 solid_disp_tri( ctx
, idxse
, idxnw
, idxsw
);
549 solid_disp_tri( ctx
, idxse
, idxne
, idxnw
);
555 csr_sb_inc( ctx
->verts
, numpoints
*numpoints
);
560 u32 tris
= csr_sb_count( face
->indices
) -2;
561 ctx
->indices
= csr_sb_reserve( ctx
->indices
, tris
*3, sizeof( u32
) );
563 u32 c
= csr_sb_count( ctx
->indices
);
565 for( int j
= 0; j
< tris
; j
++ )
567 ctx
->indices
[ c
+j
*3 +0 ] = face
->indices
[ 0 ];
568 ctx
->indices
[ c
+j
*3 +1 ] = face
->indices
[ j
+1 ];
569 ctx
->indices
[ c
+j
*3 +2 ] = face
->indices
[ j
+2 ];
576 csr_sb_inc( ctx
->indices
, tris
*3 );
580 // Free temp polyon buffers
581 for( int j
= 0; j
< num_planes
; j
++ )
583 csr_sb_free( faces
[ j
].indices
);
589 u32
vmf_get_mdl( vmf_map
*map
, const char *mdl
)
591 u32 hash
= djb2( (const unsigned char *)mdl
);
593 for( u32 i
= 0; i
< csr_sb_count( map
->models
); i
++ )
595 if( hash
== map
->models
[i
].hash
&& !strcmp( map
->models
[i
].str
, mdl
) )
604 int vmf_class_is_prop( vdf_node
*ent
)
606 return !strncmp( kv_get( ent
, "classname", "" ), "prop_", 5 );
609 void vmf_populate_models( vdf_node
*vmf
, vmf_map
*map
)
611 vdf_foreach( vmf
, "entity", ent
)
613 // Use any class name with prop_
614 if( vmf_class_is_prop( ent
) )
616 // Check if it exists
617 const char *model_path
= kv_get( ent
, "model", "" );
618 u32 mdl_id
= vmf_get_mdl( map
, model_path
);
622 map
->models
= csr_sb_reserve( map
->models
, 1, sizeof( struct vmf_model
));
624 struct vmf_model
*entry
= &map
->models
[ csr_sb_count( map
->models
) ];
625 entry
->need_load
= 0;
626 mdl_error( &entry
->mdl
); // (just setting arrays to 0)
628 entry
->str
= csr_malloc( strlen( model_path
) +1 );
629 strcpy( entry
->str
, model_path
);
630 entry
->hash
= djb2( (const unsigned char *)model_path
);
632 mdl_id
= csr_sb_count( map
->models
);
633 csr_sb_use( map
->models
);
636 // Assign prop-ID for later use
637 ent
->user
= VMF_FLAG_IS_PROP
;
644 void vmf_index_models( vmf_map
*map
)
646 log_info( "Indexing all models\n" );
648 // Error model == blank
649 map
->models
= csr_sb_reserve( map
->models
, 1, sizeof( struct vmf_model
));
650 csr_sb_use( map
->models
);
651 mdl_error( &map
->models
[0].mdl
);
653 // Create listings for each model
654 vmf_populate_models( map
->root
, map
);
656 for( int i
= 0; i
< csr_sb_count( map
->cache
); i
++ )
658 vmf_populate_models( map
->cache
[i
].root
, map
);
661 log_info( "Indexed (%u) models\n", csr_sb_count( map
->models
)-1 );
664 void vmf_load_models( vmf_map
*map
)
669 for( int i
= 1; i
< csr_sb_count( map
->models
); i
++ )
671 struct vmf_model
*mdl
= &map
->models
[i
];
677 if( mdl_from_find_files( mdl
->str
, &mdl
->mdl
) )
683 log_warn( "Failed to load model: %s\n", mdl
->str
);
688 log_info( "Done (%u of %u loaded)\n", num_success
, num_reqs
);
691 u32
vmf_init_subvmf( vmf_map
*map
, const char *subvmf
);
693 void vmf_load_all_instances( vmf_map
*map
, vdf_node
*vmf
)
695 vdf_foreach( vmf
, "entity", ent
)
697 if( !strcmp( kv_get( ent
, "classname", "" ), "func_instance" ))
699 // Entity is in use if file is specified, if not just ignore the entity.
700 const char *path
= kv_get( ent
, "file", "" );
701 if( strcmp( path
, "" ) )
703 if( (ent
->user1
= vmf_init_subvmf( map
, path
)))
706 ent
->user
= VMF_FLAG_IS_INSTANCE
;
713 // TODO: Merge this into above function.. doesnt need to be seperated
714 u32
vmf_init_subvmf( vmf_map
*map
, const char *subvmf
)
717 u32 hash
= djb2( (const unsigned char *)subvmf
);
720 for( u32 i
= 0; i
< csr_sb_count( map
->cache
); i
++ )
722 if( hash
== map
->cache
[i
].hash
)
724 if( !strcmp( map
->cache
[i
].name
, subvmf
) )
726 if( map
->cache
[i
].root
)
734 id
= csr_sb_count( map
->cache
);
735 map
->cache
= csr_sb_reserve( map
->cache
, 1, sizeof( struct vmf_instance
));
736 struct vmf_instance
*inst
= &map
->cache
[ id
];
738 csr_sb_use( map
->cache
);
740 inst
->name
= csr_malloc( strlen( subvmf
)+1 );
741 strcpy( inst
->name
, subvmf
);
743 if( (inst
->root
= vdf_open_file( subvmf
)) )
745 // Recursive load other instances
746 vmf_load_all_instances( map
, inst
->root
);
751 log_error( "Failed to load instance file: %s\n", subvmf
);
756 vmf_map
*vmf_init( const char *path
)
758 vmf_map
*map
= csr_calloc( sizeof( vmf_map
) );
759 map
->root
= vdf_open_file( path
);
768 vmf_load_all_instances( map
, map
->root
);
771 vmf_index_models( map
);
775 void vmf_free( vmf_map
*map
)
777 for( int i
= 0; i
< csr_sb_count( map
->cache
); i
++ )
779 if( map
->cache
[i
].root
)
780 vdf_free_r( map
->cache
[i
].root
);
782 free( map
->cache
[i
].name
);
785 for( int i
= 1; i
< csr_sb_count( map
->models
); i
++ )
787 free( map
->models
[i
].str
);
788 mdl_free( &map
->models
[i
].mdl
);
791 vdf_free_r( map
->root
);
792 csr_sb_free( map
->models
);
793 csr_sb_free( map
->cache
);
797 void vmf_entity_transform( vdf_node
*ent
, m4x3f mat
)
799 v3f angles
= {0.f
,0.f
,0.f
};
800 v3f offset
= {0.f
,0.f
,0.f
};
804 scale
= kv_get_float( ent
, "uniformscale", 1.f
);
805 kv_float_array( ent
, "angles", 3, angles
);
806 kv_float_array( ent
, "origin", 3, offset
);
809 m4x3_translate( mat
, offset
);
811 // Make rotation ( Pitch yaw roll // YZX. Source->OpenGL ordering a lil messed up )
812 m4x3_rotate_z( mat
, csr_rad( angles
[1] ) );
813 m4x3_rotate_y( mat
, csr_rad( angles
[0] ) );
814 m4x3_rotate_x( mat
, csr_rad( angles
[2] ) );
817 m4x3_scale( mat
, scale
);
820 u32
vmf_visgroup_id( vdf_node
*root
, const char *name
)
822 vdf_node
*dict
= vdf_next( root
, "visgroups", NULL
);
826 vdf_foreach( dict
, "visgroup", group
)
828 if( !strcmp( kv_get( group
, "name", "" ), name
) )
830 return kv_get_int( group
, "visgroupid", 0 );
838 int vmf_visgroup_match( vdf_node
*ent
, u32 target
)
840 vdf_node
*editor
= vdf_next( ent
, "editor", NULL
);
844 kv_foreach( editor
, "visgroupid", groupe
)
846 if( target
== atoi( groupe
) )