-#include "csrTypes.h"
-#include "csrComb.h"
-
-#define SOLID_MAX_SIDES 512
+// This software is not affiliated with Valve Corporation
+// We are not affiliated, associated, authorized, endorsed by, or in any way officially
+// connected with Valve Corporation, or any of its subsidiaries or its affiliates.
+//
+// All trademarks are property of their respective owners
typedef struct vmf_solid vmf_solid;
typedef struct vmf_vert vmf_vert;
typedef struct vmf_mat vmf_mat;
typedef struct vmf_face vmf_face;
+typedef struct vmf_userdata vmf_userdata;
+typedef struct vmf_map vmf_map;
typedef enum ESolidResult ESolidResult;
+// API
+//=======================================================================================================================
+
+// Load vmf from disk
+vmf_map *vmf_init( const char *path );
+void vmf_free( vmf_map *map );
+
+// Solidgen API ~ Converting brushes into meshes
+// ---------------------------------------------
+void solidgen_ctx_init( vmf_solid *ctx );
+void solidgen_ctx_reset( vmf_solid *ctx );
+void solidgen_ctx_free( vmf_solid *ctx );
+void solidgen_bounds( vmf_solid *ctx, boxf box );
+
+ESolidResult solidgen_push( vmf_solid *ctx, vdf_node *node );
+
+// General VMF
+// -----------
+int solid_has_displacement( vdf_node *node );
+int vmf_class_is_prop( vdf_node *ent );
+
+// Build the list of all models used in this map, including instances
+void vmf_index_models( vmf_map *map );
+
+// Loads all models that have the resource flagged with need_load
+void vmf_load_models( vmf_map *map );
+
+// Create matrix describing this entities transform
+void vmf_entity_transform( vdf_node *ent, m4x3f mat );
+
+int vmf_visgroup_id( vdf_node *root, const char *name );
+int vmf_visgroup_match( vdf_node *ent, u32 target );
+
+// Currently unused
+//void vmf_addbisector( double p[4] );
+//void vmf_clearbisectors( void );
+//void vmf_ignore_mat( const char *material );
+//void vmf_clearignore( void );
+
+// Implementation
+//=======================================================================================================================
+
+#define SOLID_MAX_SIDES 512
+#define VMF_FLAG_IS_PROP 0x1
+#define VMF_FLAG_IS_INSTANCE 0x2
+#define VMF_FLAG_BRUSH_ENT 0x4
+
enum ESolidResult
{
k_ESolidResult_valid,
{
v3f co;
v3f nrm;
- v2f xy;
+ v3f origin;
};
struct vmf_face
u32 hash;
};
-// IMPLEMENTATION
+struct vmf_map
+{
+ vdf_node *root;
+
+ struct vmf_model
+ {
+ char *str;
+ u32 hash;
+
+ mdl_mesh_t mdl;
+
+ int need_load;
+ }
+ *models;
+
+ struct vmf_instance
+ {
+ char *name;
+ u32 hash;
+
+ vdf_node *root;
+
+ m4x3f transform;
+ }
+ *cache;
+
+ m4x3f transform;
+};
+
+#ifdef VALVE_IMPLEMENTATION
+
+void solidgen_ctx_reset( vmf_solid *ctx )
+{
+ csr_sb_clear( ctx->verts );
+ csr_sb_clear( ctx->indices );
+}
void solidgen_ctx_init( vmf_solid *ctx )
{
}
// Compute bounds of solid gen ctx
-void solidgen_bounds( vmf_solid *ctx, u32 start, u32 end, v3f min, v3f max )
+void solidgen_bounds( vmf_solid *ctx, boxf box )
{
v3f mine = { INFINITY, INFINITY, INFINITY };
v3f maxe = {-INFINITY,-INFINITY,-INFINITY };
- for( int i = start; i < end; i ++ )
+ for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ )
{
vmf_vert *vert = ctx->verts + i;
- float *co = vert->co;
-
- mine[0] = fminf( mine[0], co[0] );
- mine[1] = fminf( mine[1], co[1] );
- mine[2] = fminf( mine[2], co[2] );
-
- maxe[0] = fmaxf( maxe[0], co[0] );
- maxe[1] = fmaxf( maxe[1], co[1] );
- maxe[2] = fmaxf( maxe[2], co[2] );
+ v3_minv( mine, vert->co, mine );
+ v3_maxv( maxe, vert->co, maxe );
}
- v3_copy( mine, min );
- v3_copy( maxe, max );
+ v3_copy( mine, box[0] );
+ v3_copy( maxe, box[1] );
}
struct
{
ESolidResult flag = k_ESolidResult_valid;
+ double planes[ SOLID_MAX_SIDES*4 ];
vmf_face faces[ SOLID_MAX_SIDES ];
- int is_displacement = 0;
+ int is_displacement = solid_has_displacement( node );
int num_planes = 0;
- // TODO: What is this for again? surely it should be the other way around... i think...
- if( solid_has_displacement( node ) )
- {
- printf( "solid_has_displacement\n" );
- num_planes = vmf_api.bisectors;
-
- // Add dummy stuff for globals
- // ???
- for( int k = 0; k < vmf_api.bisectors; k ++ )
- {
- vmf_face *dummy = faces + k;
- dummy->indices = NULL;
- dummy->dispinfo = NULL;
- dummy->material = NULL;
- }
-
- is_displacement = 1;
- }
-
int it = 0;
vdf_node *pSide;
while( (pSide = vdf_next(node, "side", &it)) )
if( num_planes >= SOLID_MAX_SIDES )
{
flag = k_ESolidResult_maxsides;
- fprintf( stderr, "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES );
+ log_error( "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES );
break;
}
kv_double_array( pSide, "plane", 9, points );
- tri_to_plane( points+6, points+3, points+0, vmf_api.planes + num_planes * 4 );
+ tri_to_plane( points+6, points+3, points+0, planes + num_planes * 4 );
num_planes ++;
}
if( (faces[ i[0] ].blacklisted && faces[ i[1] ].blacklisted && faces[ i[2] ].blacklisted) )
continue;
- if( !plane_intersect( vmf_api.planes+i[0]*4, vmf_api.planes+i[1]*4, vmf_api.planes+i[2]*4, p ) )
+ if( !plane_intersect( planes+i[0]*4, planes+i[1]*4, planes+i[2]*4, p ) )
continue;
// Check for illegal verts (eg: got clipped by bisectors)
int valid = 1;
for( int m = 0; m < num_planes; m ++ )
{
- if( plane_polarity( vmf_api.planes+m*4, p ) > 1e-6f )
+ if( plane_polarity( planes+m*4, p ) > 1e-6f )
{
valid = 0;
break;
face_add_indice( faces + i[k], c );
v3d_v3f( p, ctx->verts[ c ].co );
- v3d_v3f( vmf_api.planes+i[k]*4, ctx->verts[ c ].nrm );
+ v3d_v3f( planes+i[k]*4, ctx->verts[ c ].nrm );
csr_sb_inc( ctx->verts, 1 );
}
v3_divs( center, (float)numpoints, center );
for( ; vert_start < csr_sb_count( ctx->verts ); vert_start ++ )
{
- v2_copy( center, ctx->verts[ vert_start ].xy );
+ v3_copy( center, ctx->verts[ vert_start ].origin );
}
// Sort each faces and trianglulalate them
- for( int k = vmf_api.bisectors; k < num_planes; k ++ )
+ for( int k = 0; k < num_planes; k ++ )
{
vmf_face *face = faces + k;
if( csr_sb_count( face->indices ) < 3 )
{
- if( !vmf_api.bisectors )
- {
- flag = k_ESolidResult_degenerate;
- fprintf( stderr, "Skipping degenerate face\n" );
- }
+ flag = k_ESolidResult_degenerate;
+ log_error( "Skipping degenerate face\n" );
continue;
}
// Sort only if there is no displacements, or if this side is
if( !is_displacement || ( is_displacement && face->dispinfo ) )
{
- sort_coplanar( vmf_api.planes+k*4, ctx->verts, face->indices, csr_sb_count( face->indices ) );
+ sort_coplanar( planes+k*4, ctx->verts, face->indices, csr_sb_count( face->indices ) );
}
if( is_displacement )
{
if( csr_sb_count( face->indices ) != 4 )
{
- // Mute error if we have global planes cause they
- // are of course gonna fuck things up here
- if( !vmf_api.bisectors )
- {
- flag = k_ESolidResult_degenerate;
- fprintf( stderr, "Skipping degenerate displacement\n" );
- }
+ flag = k_ESolidResult_degenerate;
+ log_error( "Skipping degenerate displacement\n" );
continue;
}
}
}
- // Get corners of displacement
- float *SW = ctx->verts[ face->indices[ sw ] ].co;
- float *NW = ctx->verts[ face->indices[ (sw+1) % 4] ].co;
- float *NE = ctx->verts[ face->indices[ (sw+2) % 4] ].co;
- float *SE = ctx->verts[ face->indices[ (sw+3) % 4] ].co;
-
// Can be either 5, 9, 17
numpoints = pow( 2, kv_get_int( dispinfo, "power", 2 ) ) + 1;
u32 reqverts = numpoints*numpoints;
ctx->verts = csr_sb_reserve( ctx->verts, reqverts, sizeof( vmf_vert ) );
ctx->indices = csr_sb_reserve( ctx->indices, reqidx, sizeof( u32 ) );
+
+ // Get corners of displacement
+ float *SW = ctx->verts[ face->indices[ sw ] ].co;
+ float *NW = ctx->verts[ face->indices[ (sw+1) % 4] ].co;
+ float *NE = ctx->verts[ face->indices[ (sw+2) % 4] ].co;
+ float *SE = ctx->verts[ face->indices[ (sw+3) % 4] ].co;
float normals[ 17*3 ];
float distances[ 17 ];
// Todo, put correct normal
v3_copy( (v3f){ 0.f, 0.f, 1.f }, vert->nrm );
+
+ // Todo: use real bounds of displaced vertices
+ v3_copy( center, vert->origin );
}
}
return flag;
}
-void solidgen_to_obj( vmf_solid *ctx, const char *path )
+u32 vmf_get_mdl( vmf_map *map, const char *mdl )
+{
+ u32 hash = djb2( (const unsigned char *)mdl );
+
+ for( u32 i = 0; i < csr_sb_count( map->models ); i ++ )
+ {
+ if( hash == map->models[i].hash && !strcmp( map->models[i].str, mdl ) )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+int vmf_class_is_prop( vdf_node *ent )
+{
+ return !strncmp( kv_get( ent, "classname", "" ), "prop_", 5 );
+}
+
+void vmf_populate_models( vdf_node *vmf, vmf_map *map )
{
- FILE *fp = fopen( path, "w" );
+ vdf_foreach( vmf, "entity", ent )
+ {
+ // Use any class name with prop_
+ if( vmf_class_is_prop( ent ) )
+ {
+ // Check if it exists
+ const char *model_path = kv_get( ent, "model", "" );
+ u32 mdl_id = vmf_get_mdl( map, model_path );
+
+ if( !mdl_id )
+ {
+ map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
+
+ struct vmf_model *entry = &map->models[ csr_sb_count( map->models ) ];
+ entry->need_load = 0;
+ mdl_error( &entry->mdl ); // (just setting arrays to 0)
+
+ entry->str = csr_malloc( strlen( model_path ) +1 );
+ strcpy( entry->str, model_path );
+ entry->hash = djb2( (const unsigned char *)model_path );
+
+ mdl_id = csr_sb_count( map->models );
+ csr_sb_use( map->models );
+ }
+
+ // Assign prop-ID for later use
+ ent->user = VMF_FLAG_IS_PROP;
+ ent->user1 = mdl_id;
+ }
+ }
+}
- if( fp )
+// Load all models
+void vmf_index_models( vmf_map *map )
+{
+ log_info( "Indexing all models\n" );
+
+ // Error model == blank
+ map->models = csr_sb_reserve( map->models, 1, sizeof( struct vmf_model ));
+ csr_sb_use( map->models );
+ mdl_error( &map->models[0].mdl );
+
+ // Create listings for each model
+ vmf_populate_models( map->root, map );
+
+ for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
{
- fprintf( fp, "o vmf_export\n" );
+ vmf_populate_models( map->cache[i].root, map );
+ }
- vmf_vert *vert;
+ log_info( "Indexed (%u) models\n", csr_sb_count( map->models )-1 );
+}
+
+void vmf_load_models( vmf_map *map )
+{
+ u32 num_success = 0;
+ u32 num_reqs = 0;
- // Write vertex block
- for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ )
+ for( int i = 1; i < csr_sb_count( map->models ); i ++ )
+ {
+ struct vmf_model *mdl = &map->models[i];
+
+ if( mdl->need_load )
{
- vert = &ctx->verts[i];
- fprintf( fp, "v %f %f %f\n", vert->co[0], vert->co[1], vert->co[2] );
- }
+ num_reqs ++;
- // Write normals block
- for( int i = 0; i < csr_sb_count( ctx->verts ); i ++ )
+ if( mdl_from_find_files( mdl->str, &mdl->mdl ) )
+ {
+ num_success ++;
+ }
+ else
+ {
+ log_warn( "Failed to load model: %s\n", mdl->str );
+ }
+ }
+ }
+
+ log_info( "Done (%u of %u loaded)\n", num_success, num_reqs );
+}
+
+u32 vmf_init_subvmf( vmf_map *map, const char *subvmf );
+
+void vmf_load_all_instances( vmf_map *map, vdf_node *vmf )
+{
+ char nextvmf[ 512 ];
+ const char *base = kv_get( vmf, "csr_path", "" );
+
+ vdf_foreach( vmf, "entity", ent )
+ {
+ if( !strcmp( kv_get( ent, "classname", "" ), "func_instance" ))
{
- vert = &ctx->verts[i];
- fprintf( fp, "vn %f %f %f\n", vert->nrm[0], vert->nrm[1], vert->nrm[2] );
+ // Entity is in use if file is specified, if not just ignore the entity.
+ const char *path = kv_get( ent, "file", NULL );
+
+ if( path )
+ {
+ // Make relative path real
+ strcpy( nextvmf, base );
+ csr_downlvl( nextvmf );
+ strcat( nextvmf, path );
+
+ if( (ent->user1 = vmf_init_subvmf( map, nextvmf )))
+ {
+ ent->user1 --;
+ ent->user = VMF_FLAG_IS_INSTANCE;
+ }
+ }
}
-
- fprintf( fp, "s off\n" );
-
- // Indices
- for( int i = 0; i < csr_sb_count( ctx->indices )/3; i ++ )
+ }
+}
+
+// TODO: Merge this into above function.. doesnt need to be seperated
+u32 vmf_init_subvmf( vmf_map *map, const char *subvmf )
+{
+ u32 id;
+ u32 hash = djb2( (const unsigned char *)subvmf );
+
+ // Check if present
+ for( u32 i = 0; i < csr_sb_count( map->cache ); i ++ )
+ {
+ if( hash == map->cache[i].hash )
{
- u32 * base = ctx->indices + i*3;
- fprintf( fp, "f %u//%u %u//%u %u//%u\n",
- base[2]+1, base[2]+1,
- base[1]+1, base[1]+1,
- base[0]+1, base[0]+1
- );
+ if( !strcmp( map->cache[i].name, subvmf ) )
+ {
+ if( map->cache[i].root )
+ return i+1;
+ else
+ return 0;
+ }
}
+ }
+
+ id = csr_sb_count( map->cache );
+ map->cache = csr_sb_reserve( map->cache, 1, sizeof( struct vmf_instance ));
+ struct vmf_instance *inst = &map->cache[ id ];
- fclose( fp );
+ csr_sb_use( map->cache );
+ inst->hash = hash;
+ inst->name = csr_malloc( strlen( subvmf )+1 );
+ strcpy( inst->name, subvmf );
+
+ if( (inst->root = vdf_open_file( subvmf )) )
+ {
+ vdf_kv_append( inst->root, "csr_path", subvmf );
+
+ // Recursive load other instances
+ vmf_load_all_instances( map, inst->root );
+ return id+1;
}
else
{
- fprintf( stderr, "Could not open %s for writing\n", path );
+ log_error( "Failed to load instance file: %s\n", subvmf );
+ return 0;
}
}
+
+vmf_map *vmf_init( const char *path )
+{
+ vmf_map *map = csr_calloc( sizeof( vmf_map ) );
+ map->root = vdf_open_file( path );
+
+ if( !map->root )
+ {
+ free( map );
+ return NULL;
+ }
+
+ vdf_kv_append( map->root, "csr_path", path );
+
+ // Prepare instances
+ vmf_load_all_instances( map, map->root );
+
+ // Other resources
+ vmf_index_models( map );
+ return map;
+}
+
+void vmf_free( vmf_map *map )
+{
+ for( int i = 0; i < csr_sb_count( map->cache ); i ++ )
+ {
+ if( map->cache[i].root )
+ vdf_free_r( map->cache[i].root );
+
+ free( map->cache[i].name );
+ }
+
+ for( int i = 1; i < csr_sb_count( map->models ); i ++ )
+ {
+ free( map->models[i].str );
+ mdl_free( &map->models[i].mdl );
+ }
+
+ vdf_free_r( map->root );
+ csr_sb_free( map->models );
+ csr_sb_free( map->cache );
+ free( map );
+}
+
+void vmf_entity_transform( vdf_node *ent, m4x3f mat )
+{
+ v3f angles = {0.f,0.f,0.f};
+ v3f offset = {0.f,0.f,0.f};
+ float scale;
+
+ // Parse
+ scale = kv_get_float( ent, "uniformscale", 1.f );
+ kv_float_array( ent, "angles", 3, angles );
+ kv_float_array( ent, "origin", 3, offset );
+
+ // Translation
+ m4x3_translate( mat, offset );
+
+ // Make rotation ( Pitch yaw roll // YZX. Source->OpenGL ordering a lil messed up )
+ m4x3_rotate_z( mat, csr_rad( angles[1] ) );
+ m4x3_rotate_y( mat, csr_rad( angles[0] ) );
+ m4x3_rotate_x( mat, csr_rad( angles[2] ) );
+
+ // Scale
+ m4x3_scale( mat, scale );
+}
+
+int vmf_visgroup_id( vdf_node *root, const char *name )
+{
+ vdf_node *dict = vdf_next( root, "visgroups", NULL );
+
+ if( dict )
+ {
+ vdf_foreach( dict, "visgroup", group )
+ {
+ if( !strcmp( kv_get( group, "name", "" ), name ) )
+ {
+ return kv_get_int( group, "visgroupid", 0 );
+ }
+ }
+ }
+
+ return -1;
+}
+
+int vmf_visgroup_match( vdf_node *ent, u32 target )
+{
+ vdf_node *editor = vdf_next( ent, "editor", NULL );
+
+ if( editor )
+ {
+ kv_foreach( editor, "visgroupid", groupe )
+ {
+ if( target == atoi( groupe ) )
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#endif