X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=model.h;h=287eefd6e203692233ac71aaf973e632f7b8208f;hb=2a238d32da833812e837cf38e16a7685c98db5c3;hp=a1bf8ec24b70aa358abbe7f451c0cc20f196e7d3;hpb=a1a05787ada52089f30c533fb26b745554c07512;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/model.h b/model.h index a1bf8ec..287eefd 100644 --- a/model.h +++ b/model.h @@ -1,37 +1,106 @@ +/* + * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + #ifndef MODEL_H #define MODEL_H -#include "vg/vg.h" +#include "common.h" -typedef struct model model; typedef struct glmesh glmesh; -typedef struct submodel submodel; -typedef struct model_vert model_vert; -typedef struct scene scene; -typedef struct sdf_primative sdf_primative; -typedef enum esdf_type esdf_type; -#pragma pack(push,1) -struct model +typedef struct mdl_vert mdl_vert; +typedef struct mdl_submesh mdl_submesh; +typedef struct mdl_material mdl_material; +typedef struct mdl_node mdl_node; +typedef struct mdl_file_header mdl_file_header; +typedef struct mdl_animation mdl_animation; +typedef struct mdl_keyframe mdl_keyframe; +typedef struct mdl_texture mdl_texture; +typedef struct mdl_context mdl_context; + +#define MDL_SIZE_MAX 0x1000000 +#define MDL_VERT_MAX 1000000 +#define MDL_INDICE_MAX 1000000 +#define MDL_MATERIAL_MAX 32 +#define MDL_NODE_MAX 4000 +#define MDL_SUBMESH_MAX 8000 +#define MDL_STRING_LENGTH_MAX 64 + +enum classtype { - u32 identifier; + k_classtype_none = 0, + k_classtype_gate = 1, + k_classtype_spawn = 3, + k_classtype_water = 4, + k_classtype_route_node = 8, + k_classtype_route = 9, + k_classtype_bone = 10, + k_classtype_skeleton = 11, + k_classtype_skin = 12, + k_classtype_audio = 14, + k_classtype_trigger = 100, + k_classtype_logic_achievement = 101, + k_classtype_logic_relay = 102 +}; - u32 vertex_count, - indice_count, - layer_count; +enum mdl_shader +{ + k_shader_standard = 0, + k_shader_standard_cutout = 1, + k_shader_terrain_blend = 2, + k_shader_standard_vertex_blend = 3, + k_shader_water = 4 +}; + +enum mdl_surface_prop +{ + k_surface_prop_concrete = 0, + k_surface_prop_wood = 1, + k_surface_prop_grass = 2 +}; + +enum material_flag +{ + k_material_flag_skate_surface = 0x1, + k_material_flag_collision = 0x2, + k_material_flag_grow_grass = 0x4, + k_material_flag_grind_surface = 0x8 }; -struct sdf_primative +enum bone_flag { - v4f origin; /* xyz, yaw */ - /* Cone: - x base scale - y height - */ - v4f info; + k_bone_flag_deform = 0x1, + k_bone_flag_ik = 0x2, + k_bone_flag_collider_box = 0x4, + k_bone_flag_collider_capsule = 0x8, + k_bone_flag_collider_reserved0 = 0x10, + k_bone_flag_collider_reserved1 = 0x20, + k_bone_flag_collider_reserved2 = 0x40, + k_bone_flag_collider_reserved3 = 0x80, + k_bone_flag_collider_any = k_bone_flag_collider_box | + k_bone_flag_collider_capsule | + k_bone_flag_collider_reserved0 | + k_bone_flag_collider_reserved1 | + k_bone_flag_collider_reserved2 | + k_bone_flag_collider_reserved3, + k_bone_flag_cone_constraint = 0x100, + k_bone_flag_force_u32 = 0xffffffff }; -struct submodel +#pragma pack(push,1) + +struct mdl_vert +{ + v3f co, + norm; + v2f uv; + u8 colour[4]; + u16 weights[4]; + u8 groups[4]; +}; + +struct mdl_submesh { u32 indice_start, indice_count, @@ -39,144 +108,530 @@ struct submodel vertex_count; boxf bbx; - v3f pivot; - sdf_primative sdf; + u32 material_id; +}; - enum esdf_type - { - k_sdf_none = 0, - k_sdf_cone, - k_sdf_sphere, - k_sdf_box - } - sdf_type; +struct mdl_texture +{ + u32 pstr_name, + pack_offset, + pack_length; +}; - char name[32]; +struct mdl_material +{ + u32 pstr_name, + shader, + flags, + surface_prop; + + v4f colour, + colour1; + + u32 tex_diffuse, + tex_decal, + tex_normal; }; -struct model_vert +struct mdl_node { - v3f co, - norm; - v4f colour; - v2f uv; + v3f co; + v4f q; + v3f s; + + u32 sub_uid, /* allocated in-file... too bad. */ + submesh_start, + submesh_count, + classtype, + offset, + parent, + pstr_name; }; + +struct mdl_keyframe +{ + v3f co; + v4f q; + v3f s; +}; + +struct mdl_animation +{ + u32 pstr_name, + length; + + float rate; + + u32 offset; +}; + +struct mdl_file_header +{ + u32 identifier, version, file_length, pad0; + + u32 + node_count, node_offset, + submesh_count, submesh_offset, + material_count, material_offset, + texture_count, texture_offset, + anim_count, anim_offset, + entdata_size, entdata_offset, + strings_size, strings_offset, + + keyframe_count, keyframe_offset, + + vertex_count, vertex_offset, + indice_count, indice_offset, + + pack_size, pack_offset; +}; + +/* + * Entity data structures + */ + +struct classtype_gate +{ + u32 target; + v3f dims; +}; + +struct classtype_spawn +{ + u32 pstr_alias; +}; + +struct classtype_water +{ + u32 temp; +}; + +struct classtype_route_node +{ + u32 target, target1; +}; + +struct classtype_route +{ + u32 id_start; + u32 pstr_name; + v3f colour; +}; + +struct classtype_bone +{ + u32 flags, + ik_target, + ik_pole; + + boxf hitbox; + + v3f conevx, conevy, coneva; + float conet; +}; + +struct classtype_skeleton +{ + u32 channels, + ik_count, + collider_count, + anim_start, + anim_count; +}; + +struct classtype_skin +{ + u32 skeleton; +}; + +struct classtype_trigger +{ + u32 target; +}; + +struct classtype_logic_relay +{ + u32 targets[4]; +}; + +struct classtype_logic_achievement +{ + u32 pstr_name; +}; + +struct classtype_audio +{ + u32 pstr_file, + flags; + + float volume; +}; + #pragma pack(pop) + +struct mdl_context +{ + FILE *file; + mdl_file_header info; + + /* each buffer becomes availible after each _load function is called */ + mdl_node *node_buffer; /* mdl_load_metadata() */ + mdl_submesh *submesh_buffer; + mdl_material *material_buffer; + mdl_texture *texture_buffer; + mdl_animation *anim_buffer; + void *entdata_buffer; + const char *string_buffer; + + mdl_keyframe *keyframe_buffer; /* mdl_load_anim_data() */ + + mdl_vert *vertex_buffer; /* mdl_load_mesh_data() */ + u32 *index_buffer; + + void *pack; /* mdl_load_pack_data() */ +}; + +/* + * Simple mesh interface for OpenGL + */ + struct glmesh { GLuint vao, vbo, ebo; u32 indice_count; + u32 loaded; }; -static void mesh_upload( glmesh *mesh, - model_vert *verts, u32 vert_count, - u32 *indices, u32 indice_count ) +VG_STATIC void mesh_upload( glmesh *mesh, + mdl_vert *verts, u32 vert_count, + u32 *indices, u32 indice_count ) { + //assert( mesh->loaded == 0 ); + glGenVertexArrays( 1, &mesh->vao ); glGenBuffers( 1, &mesh->vbo ); glGenBuffers( 1, &mesh->ebo ); glBindVertexArray( mesh->vao ); + size_t stride = sizeof(mdl_vert); + glBindBuffer( GL_ARRAY_BUFFER, mesh->vbo ); - glBufferData( GL_ARRAY_BUFFER, vert_count*sizeof(model_vert), - verts, GL_STATIC_DRAW ); + glBufferData( GL_ARRAY_BUFFER, vert_count*stride, verts, GL_STATIC_DRAW ); glBindVertexArray( mesh->vao ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->ebo ); glBufferData( GL_ELEMENT_ARRAY_BUFFER, indice_count*sizeof(u32), indices, GL_STATIC_DRAW ); - glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, - sizeof(model_vert), (void*)0 ); + /* 0: coordinates */ + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (void*)0 ); glEnableVertexAttribArray( 0 ); + /* 1: normal */ glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, - sizeof(model_vert), (void *)offsetof(model_vert, norm) ); + stride, (void *)offsetof(mdl_vert, norm) ); glEnableVertexAttribArray( 1 ); - glVertexAttribPointer( 2, 4, GL_FLOAT, GL_FALSE, - sizeof(model_vert), (void *)offsetof(model_vert, colour) ); + /* 2: uv */ + glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, + stride, (void *)offsetof(mdl_vert, uv) ); glEnableVertexAttribArray( 2 ); - glVertexAttribPointer( 3, 2, GL_FLOAT, GL_FALSE, - sizeof(model_vert), (void *)offsetof(model_vert, uv) ); + /* 3: colour */ + glVertexAttribPointer( 3, 4, GL_UNSIGNED_BYTE, GL_TRUE, + stride, (void *)offsetof(mdl_vert, colour) ); glEnableVertexAttribArray( 3 ); - - VG_CHECK_GL(); + + /* 4: weights */ + glVertexAttribPointer( 4, 4, GL_UNSIGNED_SHORT, GL_TRUE, + stride, (void *)offsetof(mdl_vert, weights) ); + glEnableVertexAttribArray( 4 ); + + /* 5: groups */ + glVertexAttribIPointer( 5, 4, GL_UNSIGNED_BYTE, + stride, (void *)offsetof(mdl_vert, groups) ); + glEnableVertexAttribArray( 5 ); + + VG_CHECK_GL_ERR(); + mesh->indice_count = indice_count; + mesh->loaded = 1; } -static void mesh_bind( glmesh *mesh ) +VG_STATIC void mesh_bind( glmesh *mesh ) { glBindVertexArray( mesh->vao ); } -static void mesh_drawn( u32 start, u32 count ) +VG_STATIC void mesh_drawn( u32 start, u32 count ) { glDrawElements( GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start*sizeof(u32)) ); } -static void mesh_draw( glmesh *mesh ) +VG_STATIC void mesh_draw( glmesh *mesh ) { mesh_drawn( 0, mesh->indice_count ); } +VG_STATIC void mesh_free( glmesh *mesh ) +{ + if( mesh->loaded ) + { + glDeleteVertexArrays( 1, &mesh->vao ); + glDeleteBuffers( 1, &mesh->ebo ); + glDeleteBuffers( 1, &mesh->vbo ); + mesh->loaded = 0; + } +} + +VG_STATIC void mdl_load_fatal_corrupt( mdl_context *mdl ) +{ + fclose( mdl->file ); + vg_file_print_invalid( mdl->file ); + vg_fatal_exit_loop( "Corrupt model" ); +} + /* - * Helper functions for file offsets + * Model implementation + * + * TODO. + * + * you have two api options for loading a model, first, the easy way: + * mdl_load ... + * will put the entire model straight into the linear_alloc + * + * or, to target different allocators: + * + * mdl_open + * mdl_load_metadata + * mdl_load_vertex_data + * mdl_load_indice_data + * mdl_close + * + * these should ideally be called in quick succession to limit stalls. */ -static submodel *model_get_submodel( model *mdl, int id ) + +/* + * if calling mdl_open, and the file does not exist, the game will fatal quit + */ +VG_STATIC void mdl_open( mdl_context *mdl, const char *path ) { - return ((submodel*)(mdl+1)) + id; + memset( mdl, 0, sizeof( mdl_context ) ); + mdl->file = fopen( path, "rb" ); + + if( !mdl->file ) + { + vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) ); + vg_fatal_exit_loop( "see above for details" ); + } + + u64 l = fread( &mdl->info, sizeof(mdl_file_header), 1, mdl->file ); + if( l != 1 ) + mdl_load_fatal_corrupt( mdl ); } -static model_vert *model_vertex_base( model *mdl ) +/* + * Load all metadata (everything up until the large buffers). Probs at most 50k + */ +VG_STATIC void mdl_load_metadata( mdl_context *mdl, void *lin_alloc ) { - return (model_vert *)model_get_submodel( mdl, mdl->layer_count ); + assert( mdl->file ); + + u64 lheader = sizeof(mdl_file_header), + ldata = mdl->info.keyframe_offset - lheader; + + void *all_data = vg_linear_alloc( lin_alloc, ldata ); + + fseek( mdl->file, lheader, SEEK_SET ); + u64 l = fread( all_data, ldata, 1, mdl->file ); + + if( l != 1 ) + { + vg_file_print_invalid( mdl->file ); + vg_fatal_exit_loop( "Corrupt model" ); + } + + mdl->node_buffer = all_data + (mdl->info.node_offset - lheader); + mdl->submesh_buffer = all_data + (mdl->info.submesh_offset - lheader); + mdl->material_buffer = all_data + (mdl->info.material_offset - lheader); + mdl->texture_buffer = all_data + (mdl->info.texture_offset - lheader); + mdl->anim_buffer = all_data + (mdl->info.anim_offset - lheader); + mdl->entdata_buffer = all_data + (mdl->info.entdata_offset - lheader); + mdl->string_buffer = all_data + (mdl->info.strings_offset - lheader); } -static u32 *model_indice_base( model *mdl ) +/* + * Load just the mesh data + */ +VG_STATIC void mdl_load_mesh_data( mdl_context *mdl, void *lin_alloc ) { - return (u32 *)(model_vertex_base( mdl ) + mdl->vertex_count); + assert( mdl->file ); + + u64 size_verts = vg_align8( mdl->info.vertex_count * sizeof(mdl_vert) ), + size_index = vg_align8( mdl->info.indice_count * sizeof(u32) ); + + mdl->vertex_buffer = vg_linear_alloc( lin_alloc, size_verts ); + mdl->index_buffer = vg_linear_alloc( lin_alloc, size_index ); + + { + fseek( mdl->file, mdl->info.vertex_offset, SEEK_SET ); + u64 l = fread( mdl->vertex_buffer, size_verts, 1, mdl->file ); + if( l != 1 ) + mdl_load_fatal_corrupt( mdl ); + } + { + fseek( mdl->file, mdl->info.indice_offset, SEEK_SET ); + u64 l = fread( mdl->index_buffer, size_index, 1, mdl->file ); + if( l != 1 ) + mdl_load_fatal_corrupt( mdl ); + } } -static model_vert *submodel_vert_data( model *mdl, submodel *sub ) +/* + * Load animation data + */ +VG_STATIC void mdl_load_anim_data( mdl_context *mdl, void *lin_alloc ) { - return model_vertex_base(mdl) + sub->vertex_start; + assert( mdl->file ); + + if( mdl->info.keyframe_count == 0 ) + return; + + u64 size_kf = vg_align8( mdl->info.keyframe_count * sizeof(mdl_keyframe) ); + mdl->keyframe_buffer = vg_linear_alloc( lin_alloc, size_kf ); + + fseek( mdl->file, mdl->info.keyframe_offset, SEEK_SET ); + u64 l = fread( mdl->keyframe_buffer, size_kf, 1, mdl->file ); + if( l != 1 ) + mdl_load_fatal_corrupt( mdl ); } -static u32 *submodel_indice_data( model *mdl, submodel *sub ) +/* + * Load pack contents + * + * TODO request specific files (low) + */ +VG_STATIC void mdl_load_pack_data( mdl_context *mdl, void *lin_alloc ) { - return model_indice_base(mdl) + sub->indice_start; + assert( mdl->file ); + + if( mdl->info.pack_size == 0 ) + return; + + mdl->pack = vg_linear_alloc( lin_alloc, vg_align8( mdl->info.pack_size ) ); + fseek( mdl->file, mdl->info.pack_offset, SEEK_SET ); + + u64 l = fread( mdl->pack, mdl->info.pack_size, 1, mdl->file ); + if( l != 1 ) + mdl_load_fatal_corrupt( mdl ); } -static submodel *submodel_get( model *mdl, const char *name ) +/* + * close file handle + */ +VG_STATIC void mdl_close( mdl_context *mdl ) { - for( int i=0; ilayer_count; i++ ) - { - submodel *pmdl =model_get_submodel(mdl,i); + fclose( mdl->file ); + mdl->file = NULL; +} - if( !strcmp( pmdl->name, name ) ) - return pmdl; +/* open a model. TODO: make this flags ( ANIM_DATA|MESH_DATA ... ) */ +VG_STATIC mdl_context *mdl_load_full( void *lin_alloc, const char *path ) +{ + /* Inspect the header by opening it, give us the size needed */ + mdl_context temp_ctx; + mdl_open( &temp_ctx, path ); + + /* create allocator */ + u32 tot_size = temp_ctx.info.file_length + sizeof( mdl_context ) + 64; + void *data = vg_create_linear_allocator( lin_alloc, tot_size, + VG_MEMORY_SYSTEM ); + + /* copy context and load all other data */ + mdl_context *ctx = vg_linear_alloc( data, sizeof(mdl_context) ); + memcpy( ctx, &temp_ctx, sizeof(mdl_context) ); + + mdl_load_metadata( ctx, data ); + mdl_load_anim_data( ctx, data ); + mdl_load_mesh_data( ctx, data ); + mdl_load_pack_data( ctx, data ); + mdl_close( ctx ); + + return ctx; +} + +/* + * Item getters + * ---------------------------------------------------------------------------- + * TODO: Clamp access and oob errors + */ +VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr ) +{ + return mdl->string_buffer + pstr; +} + +VG_STATIC mdl_node *mdl_node_from_id( mdl_context *mdl, u32 id ) +{ + return &mdl->node_buffer[id]; +} + +VG_STATIC mdl_node *mdl_node_from_name( mdl_context *mdl, const char *name ) +{ + for( int i=0; i < mdl->info.node_count; i++ ) + { + mdl_node *pnode = mdl_node_from_id( mdl, i ); + + if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) ) + return pnode; } return NULL; } -static void submodel_draw( submodel *sm ) +VG_STATIC mdl_submesh *mdl_node_submesh( mdl_context *mdl, + mdl_node *node, u32 i ) { - mesh_drawn( sm->indice_start, sm->indice_count ); + return &mdl->submesh_buffer[ node->submesh_start+i ]; +} + +VG_STATIC u32 *mdl_submesh_indices( mdl_context *mdl, mdl_submesh *sm ) +{ + return &mdl->index_buffer[ sm->indice_start ]; +} + +VG_STATIC mdl_vert *mdl_submesh_vertices( mdl_context *mdl, mdl_submesh *sm ) +{ + return &mdl->vertex_buffer[ sm->vertex_start ]; +} + +VG_STATIC void mdl_node_transform( mdl_node *pnode, m4x3f transform ) +{ + q_m3x3( pnode->q, transform ); + v3_muls( transform[0], pnode->s[0], transform[0] ); + v3_muls( transform[1], pnode->s[1], transform[1] ); + v3_muls( transform[2], pnode->s[2], transform[2] ); + v3_copy( pnode->co, transform[3] ); +} + +/* upload a mesh based on file submesh */ +VG_STATIC void mdl_unpack_submesh( mdl_context *mdl, glmesh *mesh, + mdl_submesh *sm ) +{ + mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count, + mdl_submesh_indices( mdl, sm ), sm->indice_count ); } -static void model_unpack( model *model, glmesh *mesh ) +/* upload entire mesh from model */ +VG_STATIC void mdl_unpack_glmesh( mdl_context *mdl, glmesh *mesh ) { - u32 offset = model_get_submodel( model, 0 )->vertex_count; + u32 offset = mdl->submesh_buffer[0].vertex_count; - for( int i=1; ilayer_count; i++ ) + for( int i=1; i< mdl->info.submesh_count; i++ ) { - submodel *sm = model_get_submodel( model, i ); - u32 *indices = submodel_indice_data( model, sm ); + mdl_submesh *sm = &mdl->submesh_buffer[i]; + u32 *indices = mdl_submesh_indices( mdl, sm ); for( u32 j=0; jindice_count; j++ ) indices[j] += offset; @@ -184,8 +639,69 @@ static void model_unpack( model *model, glmesh *mesh ) offset += sm->vertex_count; } - mesh_upload( mesh, model_vertex_base( model ), model->vertex_count, - model_indice_base( model ), model->indice_count ); + mesh_upload( mesh, mdl->vertex_buffer, mdl->info.vertex_count, + mdl->index_buffer, mdl->info.indice_count ); +} + +VG_STATIC void mdl_draw_submesh( mdl_submesh *sm ) +{ + mesh_drawn( sm->indice_start, sm->indice_count ); +} + +VG_STATIC void *mdl_get_entdata( mdl_context *mdl, mdl_node *pnode ) +{ + return mdl->entdata_buffer + pnode->offset; +} + +VG_STATIC mdl_keyframe *mdl_get_animdata( mdl_context *mdl, mdl_animation *anim ) +{ + return mdl->keyframe_buffer + anim->offset; +} + +VG_STATIC void mdl_link_materials( mdl_context *root, mdl_context *child ) +{ + u32 lookup[MDL_MATERIAL_MAX]; + + for( int i=0; iinfo.material_count; i++ ) + { + mdl_material *mi = &child->material_buffer[i]; + const char *si = mdl_pstr( child, mi->pstr_name ); + + lookup[i] = 0; + + for( int j=0; jinfo.material_count; j++ ) + { + mdl_material *mj = &root->material_buffer[j]; + const char *sj = mdl_pstr( root, mj->pstr_name ); + + if( !strcmp( si, sj ) ) + { + lookup[i] = j; + break; + } + } + + if( lookup[i] == 0 && i != 0 ) + { + vg_warn( "Could not link material '%s' (not present in root model)\n", + si ); + } + } + + for( int i=0; iinfo.submesh_count; i++ ) + { + mdl_submesh *sm = &child->submesh_buffer[i]; + sm->material_id = lookup[sm->material_id]; + } +} + +VG_STATIC void mdl_invert_uv_coordinates( mdl_context *mdl ) +{ + for( int i=0; iinfo.vertex_count; i++ ) + { + mdl_vert *vert = &mdl->vertex_buffer[i]; + vert->uv[1] = 1.0f-vert->uv[1]; + } } #endif