From 14851c4c820eb07a0db0ec0366a70bdd6518c331 Mon Sep 17 00:00:00 2001 From: hgn Date: Sat, 24 Feb 2024 04:38:57 +0000 Subject: [PATCH 1/1] basic npc --- blender_export.py | 23 +++++++++- ent_challenge.c | 1 + ent_glider.c | 3 +- ent_glider.h | 2 +- ent_miniworld.c | 2 + ent_npc.c | 90 ++++++++++++++++++++++++++++++++++++++ ent_npc.h | 26 +++++++++++ ent_region.h | 1 + ent_traffic.c | 3 +- entity.h | 10 ++++- freecam.c | 2 + maps_src/dev_hub/main.mdl | Bin 1687416 -> 1687488 bytes models_src/gumpa.mdl | Bin 0 -> 66240 bytes player_common.c | 12 +++-- player_common.h | 16 ++----- player_effects.c | 1 + player_effects.h | 1 + player_replay.c | 1 + player_walk.c | 2 - save.c | 1 + skaterift.c | 3 ++ world.h | 4 +- world_entity.c | 22 ++++++++-- world_gate.h | 1 + world_gen.c | 12 +++-- world_gen.h | 16 +++---- world_load.c | 4 +- world_physics.h | 1 + world_render.c | 28 +++++++++--- world_routes.h | 1 + world_routes_ui.c | 1 + 31 files changed, 242 insertions(+), 48 deletions(-) create mode 100644 ent_npc.c create mode 100644 ent_npc.h create mode 100644 models_src/gumpa.mdl diff --git a/blender_export.py b/blender_export.py index 421cf5d..7c8c69f 100644 --- a/blender_export.py +++ b/blender_export.py @@ -45,6 +45,7 @@ sr_entity_list = [ ('ent_list', 'Entity List', '', 24 ), ('ent_region', 'Region', '', 25 ), ('ent_glider', 'Glider', '', 26 ), + ('ent_npc', 'npc', '', 27 ) ] MDL_VERSION_NR = 105 @@ -302,6 +303,12 @@ class ent_glider(Structure):#{ 1: 'equip' } #} +class ent_npc(Structure):#{ + _fields_ = [("transform",mdl_transform), + ("id",c_uint32), + ("context",c_uint32)] +#} + class ent_water(Structure): #{ _fields_ = [("transform",mdl_transform), @@ -2067,6 +2074,14 @@ def sr_compile( collection ): compile_obj_transform( obj, glider.transform ) sr_ent_push( glider ) #} + elif ent_type == 'ent_npc':#{ + obj_data = obj.SR_data.ent_npc[0] + npc = ent_npc() + compile_obj_transform( obj, npc.transform ) + npc.id = obj_data.au + npc.context = obj_data.context + sr_ent_push( npc ) + #} elif ent_type == 'ent_cubemap':#{ cubemap = ent_cubemap() co = obj.matrix_world @ Vector((0,0,0)) @@ -3148,6 +3163,11 @@ class SR_OBJECT_ENT_GLIDER(bpy.types.PropertyGroup):#{ nothing: bpy.props.StringProperty() #} +class SR_OBJECT_ENT_NPC(bpy.types.PropertyGroup):#{ + au: bpy.props.IntProperty() + context: bpy.props.IntProperty() +#} + class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{ subtype: bpy.props.EnumProperty( name="Subtype", @@ -3651,6 +3671,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): ent_miniworld: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MINIWORLD) ent_list: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_LIST) ent_glider: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLIDER) + ent_npc: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_NPC) ent_type: bpy.props.EnumProperty( name="Type", @@ -4930,7 +4951,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\ SR_OBJECT_ENT_RELAY,SR_OBJECT_ENT_MINIWORLD,\ SR_OBJECT_ENT_LIST_ENTRY, SR_UL_ENT_LIST, SR_OBJECT_ENT_LIST, \ SR_OT_ENT_LIST_NEW_ITEM, SR_OT_ENT_LIST_DEL_ITEM,\ - SR_OBJECT_ENT_GLIDER, \ + SR_OBJECT_ENT_GLIDER, SR_OBJECT_ENT_NPC, \ \ SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \ diff --git a/ent_challenge.c b/ent_challenge.c index ce87366..c2ee3b6 100644 --- a/ent_challenge.c +++ b/ent_challenge.c @@ -1,3 +1,4 @@ +#include "vg/vg_engine.h" #include "entity.h" #include "input.h" #include "gui.h" diff --git a/ent_glider.c b/ent_glider.c index d1e2a36..dbea8ea 100644 --- a/ent_glider.c +++ b/ent_glider.c @@ -2,7 +2,8 @@ #include "entity.h" #include "player_glide.h" -static void ent_glider_call( world_instance *world, ent_call *call ){ +void ent_glider_call( world_instance *world, ent_call *call ) +{ u32 index = mdl_entity_id_id( call->id ); ent_glider *glider = mdl_arritm( &world->ent_glider, index ); diff --git a/ent_glider.h b/ent_glider.h index e61bdd0..51857b2 100644 --- a/ent_glider.h +++ b/ent_glider.h @@ -1,4 +1,4 @@ #pragma once #include "entity.h" -static void ent_glider_call( world_instance *world, ent_call *call ); +void ent_glider_call( world_instance *world, ent_call *call ); diff --git a/ent_miniworld.c b/ent_miniworld.c index 4f22df8..fcdba0b 100644 --- a/ent_miniworld.c +++ b/ent_miniworld.c @@ -1,9 +1,11 @@ #include "entity.h" #include "ent_miniworld.h" #include "world_render.h" +#include "world_load.h" #include "input.h" #include "gui.h" #include "menu.h" +#include "audio.h" struct global_miniworld global_miniworld; diff --git a/ent_npc.c b/ent_npc.c new file mode 100644 index 0000000..048a1b9 --- /dev/null +++ b/ent_npc.c @@ -0,0 +1,90 @@ +#include "vg/vg_mem.h" +#include "ent_npc.h" +#include "shaders/model_character_view.h" + +struct npc npc_gumpa; +static struct skeleton_anim *gumpa_idle; + +void npc_load_model( struct npc *npc, const char *path ) +{ + vg_linear_clear( vg_mem.scratch ); + + mdl_context *meta = &npc->meta; + mdl_open( meta, path, vg_mem.rtmemory ); + mdl_load_metadata_block( meta, vg_mem.rtmemory ); + mdl_load_animation_block( meta, vg_mem.rtmemory ); + + struct skeleton *sk = &npc->skeleton; + skeleton_setup( sk, vg_mem.rtmemory, meta ); + + u32 mtx_size = sizeof(m4x3f)*sk->bone_count; + npc->final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size ); + + if( !mdl_arrcount( &meta->textures ) ) + vg_fatal_error( "No texture in model" ); + + mdl_texture *tex0 = mdl_arritm( &meta->textures, 0 ); + void *data = vg_linear_alloc( vg_mem.scratch, tex0->file.pack_size ); + mdl_fread_pack_file( meta, &tex0->file, data ); + + vg_tex2d_load_qoi_async( data, tex0->file.pack_size, + VG_TEX2D_NEAREST|VG_TEX2D_CLAMP, + &npc->texture ); + + mdl_async_load_glmesh( meta, &npc->mesh, NULL ); + mdl_close( meta ); +} + +void npc_init(void) +{ + npc_load_model( &npc_gumpa, "models/gumpa.mdl" ); + gumpa_idle = skeleton_get_anim( &npc_gumpa.skeleton, "gumpa_idle" ); +} + +static struct npc *npc_resolve( u32 id ) +{ + if( id == 1 ) return &npc_gumpa; + else return NULL; +} + +void npc_update( ent_npc *ent ) +{ + struct npc *npc_def = npc_resolve( ent->id ); + VG_ASSERT( npc_def ); + + player_pose pose; + struct skeleton *sk = &npc_def->skeleton; + pose.type = k_player_pose_type_ik; + pose.board.lean = 0.0f; + + skeleton_sample_anim( sk, gumpa_idle, vg.time, pose.keyframes ); + + v3_copy( ent->transform.co, pose.root_co ); + v4_copy( ent->transform.q, pose.root_q ); + apply_full_skeleton_pose( &npc_def->skeleton, &pose, npc_def->final_mtx ); +} + +void npc_render( ent_npc *ent, world_instance *world, vg_camera *cam ) +{ + struct npc *npc_def = npc_resolve( ent->id ); + VG_ASSERT( npc_def ); + + shader_model_character_view_use(); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, npc_def->texture ); + shader_model_character_view_uTexMain( 0 ); + shader_model_character_view_uCamera( cam->transform[3] ); + shader_model_character_view_uPv( cam->mtx.pv ); + shader_model_character_view_uDepthCompare( 0 ); + + WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_character_view ); + + glUniformMatrix4x3fv( _uniform_model_character_view_uTransforms, + npc_def->skeleton.bone_count, + 0, + (const GLfloat *)npc_def->final_mtx ); + + mesh_bind( &npc_def->mesh ); + mesh_draw( &npc_def->mesh ); +} diff --git a/ent_npc.h b/ent_npc.h new file mode 100644 index 0000000..f474d0d --- /dev/null +++ b/ent_npc.h @@ -0,0 +1,26 @@ +#pragma once +#include "player_render.h" +#include "entity.h" + +struct npc +{ + glmesh mesh; + GLuint texture; + + mdl_context meta; + struct skeleton skeleton; + + m4x3f *final_mtx; +} +extern npc_gumpa; + +enum npc_id +{ + k_npc_id_none = 0, + k_npc_id_gumpa = 1 +}; + +void npc_load_model( struct npc *npc, const char *path ); +void npc_update( ent_npc *ent ); +void npc_render( ent_npc *ent, world_instance *world, vg_camera *cam ); +void npc_init(void); diff --git a/ent_region.h b/ent_region.h index 4b27825..8aed240 100644 --- a/ent_region.h +++ b/ent_region.h @@ -1,5 +1,6 @@ #pragma once #include "world_entity.h" +#include "network_common.h" struct global_ent_region { diff --git a/ent_traffic.c b/ent_traffic.c index 5735d5b..8bb19b9 100644 --- a/ent_traffic.c +++ b/ent_traffic.c @@ -1,6 +1,7 @@ #include "world.h" -static void ent_traffic_update( world_instance *world, v3f pos ){ +void ent_traffic_update( world_instance *world, v3f pos ) +{ for( u32 i=0; ient_traffic ); i++ ){ ent_traffic *traffic = mdl_arritm( &world->ent_traffic, i ); diff --git a/entity.h b/entity.h index a925318..105f449 100644 --- a/entity.h +++ b/entity.h @@ -36,6 +36,7 @@ typedef struct ent_prop ent_prop; typedef struct ent_region ent_region; typedef struct ent_list ent_list; typedef struct ent_glider ent_glider; +typedef struct ent_npc ent_npc; enum entity_alias{ k_ent_none = 0, @@ -64,7 +65,8 @@ enum entity_alias{ k_ent_prop = 23, k_ent_list = 24, k_ent_region = 25, - k_ent_glider = 26 + k_ent_glider = 26, + k_ent_npc = 27 }; static inline u32 mdl_entity_id_type( u32 entity_id ) @@ -557,5 +559,11 @@ struct ent_glider { f32 cooldown; }; +struct ent_npc +{ + mdl_transform transform; + u32 id, context; +}; + #include "world.h" void entity_call( world_instance *world, ent_call *call ); diff --git a/freecam.c b/freecam.c index 246af3c..45713c6 100644 --- a/freecam.c +++ b/freecam.c @@ -1,4 +1,6 @@ #include "skaterift.h" +#include "player.h" +#include "player_render.h" #include "player_replay.h" #include "input.h" diff --git a/maps_src/dev_hub/main.mdl b/maps_src/dev_hub/main.mdl index 5772a92abffb76584c32fd327d4319f51fda2801..75298a2d952e59009e1d73ff1425d26fe802c147 100644 GIT binary patch delta 523 zcmYMwzfS@&6bJD3Kt)CIw_rU3@ehzhW1{gSIx#rtV4@Qs99jbh!UY&H4u-+u($PgX z9ZZZ4Zbn8EhZ`9g8Hs~o5&c#mzU1@n+V}F>wAD_&{UigN5aNe9m&t{tY_EWBn31Avm3;`W zpp?QKyqld`4NF-&#+`b#rufGEn(!%1t!;Js+}*x>F`v!p1+Et{ zU+gK2HbnX_SJ3ThkCLbB&i&=1;0<}aJJ%Lfg+14m=6Z1Pv$B0_W+l^MvM^bD2&n43K13=vb#jG0ZWaAT9~kz#Kwja zO>DHVrNqt`FyRp_X+;0gaFXxL?wmi9*=_$6d;MapL`16)g=LU#!8*q}sIDIq?7&Y7 zmhq2-*Y!%7p&ty>-H|p&N>mp_6At4m#yol&goK&2^Lo- zdPXIfFhiR}K@9dlrzy5A>?W2(QdW>p7aYJZ36 z<>CL5dWIh*^ZItE8}-RW@Co(W<6qlXM(Wx~G2jq@APm7Uj6euRVGPD$0wy605txE$ Un1NZCgLznhMKIosRJ3*Z3wT6qvj6}9 diff --git a/models_src/gumpa.mdl b/models_src/gumpa.mdl new file mode 100644 index 0000000000000000000000000000000000000000..d0ccc288a7d79a031c2187a920048b65a8c6820c GIT binary patch literal 66240 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 }, { k_ent_volume, &world->ent_volume }, { k_ent_challenge, &world->ent_challenge }, - { k_ent_glider, &world->ent_glider } + { k_ent_glider, &world->ent_glider }, + { k_ent_npc, &world->ent_npc } }; for( u32 i=0; ient_npc, index ); + box_addpt( bound, npc->transform.co ); + } else{ vg_fatal_error( "Programming error\n" ); } @@ -535,15 +541,23 @@ float entity_bh_centroid( void *user, u32 item_index, int axis ) ent_volume *volume = mdl_arritm( &world->ent_volume, index ); return volume->transform.co[axis]; } - else if( type == k_ent_challenge ){ + else if( type == k_ent_challenge ) + { ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index ); return challenge->transform.co[axis]; } - else if( type == k_ent_glider ){ + else if( type == k_ent_glider ) + { ent_glider *glider = mdl_arritm( &world->ent_glider, index ); return glider->transform.co[axis]; } - else { + else if( type == k_ent_npc ) + { + ent_npc *npc = mdl_arritm( &world->ent_npc, index ); + return npc->transform.co[axis]; + } + else + { vg_fatal_error( "Programming error\n" ); return INFINITY; } diff --git a/world_gate.h b/world_gate.h index 03e4568..6339f1d 100644 --- a/world_gate.h +++ b/world_gate.h @@ -7,6 +7,7 @@ #include "vg/vg_camera.h" #include "world.h" #include "shaders/model_gate.h" +#include "entity.h" struct world_gates { diff --git a/world_gen.c b/world_gen.c index 047155e..bc9b222 100644 --- a/world_gen.c +++ b/world_gen.c @@ -206,7 +206,8 @@ void world_unpack_submesh_dynamic( world_instance *world, /* * Create the main meshes for the world */ -static void world_gen_generate_meshes( world_instance *world ){ +void world_gen_generate_meshes( world_instance *world ) +{ /* * Compile meshes into the world scenes */ @@ -395,7 +396,8 @@ static void async_upload_light_indices( void *payload, u32 size ){ /* * Computes light indices for world */ -static void world_gen_compute_light_indices( world_instance *world ){ +void world_gen_compute_light_indices( world_instance *world ) +{ /* light cubes */ v3f cubes_min, cubes_max; v3_muls( world->scene_geo.bbx[0], 1.0f/k_world_light_cube_size, cubes_min ); @@ -555,7 +557,8 @@ static void world_gen_compute_light_indices( world_instance *world ){ /* * Rendering pass needed to complete the world */ -static void async_world_postprocess( void *payload, u32 _size ){ +void async_world_postprocess( void *payload, u32 _size ) +{ /* create scene lighting buffer */ world_instance *world = payload; @@ -709,7 +712,8 @@ static void async_world_postprocess( void *payload, u32 _size ){ } /* Loads textures from the pack file */ -static void world_gen_load_surfaces( world_instance *world ){ +void world_gen_load_surfaces( world_instance *world ) +{ vg_info( "Loading textures\n" ); world->texture_count = 0; diff --git a/world_gen.h b/world_gen.h index ba4adc5..c6ffb92 100644 --- a/world_gen.h +++ b/world_gen.h @@ -5,15 +5,11 @@ * create geometry, apply procedural stuff and save that image to files etc. */ -#ifndef WORLD_GEN_H -#define WORLD_GEN_H - +#pragma once #include "world.h" -static void world_init_blank( world_instance *world ); -static void world_gen_load_surfaces( world_instance *world ); -static void world_gen_generate_meshes( world_instance *world ); -static void world_gen_compute_light_indices( world_instance *world ); -static void async_world_postprocess( void *payload, u32 _size ); - -#endif /* WORLD_GEN_H */ +void world_init_blank( world_instance *world ); +void world_gen_load_surfaces( world_instance *world ); +void world_gen_generate_meshes( world_instance *world ); +void world_gen_compute_light_indices( world_instance *world ); +void async_world_postprocess( void *payload, u32 _size ); diff --git a/world_load.c b/world_load.c index f124e24..b741a31 100644 --- a/world_load.c +++ b/world_load.c @@ -71,6 +71,7 @@ static void world_instance_load_mdl( u32 instance_id, const char *path ){ MDL_LOAD_ARRAY( meta, &world->ent_prop, ent_prop, heap ); MDL_LOAD_ARRAY( meta, &world->ent_region, ent_region, heap ); MDL_LOAD_ARRAY( meta, &world->ent_glider, ent_glider, heap ); + MDL_LOAD_ARRAY( meta, &world->ent_npc, ent_npc, heap ); mdl_array_ptr infos; MDL_LOAD_ARRAY( meta, &infos, ent_worldinfo, vg_mem.scratch ); @@ -459,7 +460,8 @@ void world_free( world_instance *world ) * reset the world structure without deallocating persistent buffers * TODO: Make this a memset(0), and have persistent items live in a static loc */ -static void world_init_blank( world_instance *world ){ +void world_init_blank( world_instance *world ) +{ memset( &world->meta, 0, sizeof(mdl_context) ); world->textures = NULL; diff --git a/world_physics.h b/world_physics.h index ce65e5b..06143b9 100644 --- a/world_physics.h +++ b/world_physics.h @@ -2,6 +2,7 @@ #include "world.h" #include "vg/vg_rigidbody.h" #include "vg/vg_rigidbody_collision.h" +#include "vg/vg_bvh.h" void ray_world_get_tri( world_instance *world, ray_hit *hit, v3f tri[3] ); diff --git a/world_render.c b/world_render.c index 029a95f..1b1a9c0 100644 --- a/world_render.c +++ b/world_render.c @@ -10,6 +10,7 @@ #include "ent_miniworld.h" #include "player_remote.h" #include "ent_skateshop.h" +#include "ent_npc.h" #include "shaders/model_entity.h" struct world_render world_render; @@ -886,13 +887,16 @@ void world_prerender( world_instance *world ) sizeof(struct ub_world_lighting), &world->ub_lighting ); } -static void render_other_entities( world_instance *world, vg_camera *cam ){ +static void render_other_entities( world_instance *world, vg_camera *cam ) +{ f32 radius = 40.0f; bh_iter it; bh_iter_init_range( 0, &it, cam->pos, radius+10.0f ); u32 glider_list[4], - glider_count = 0; + glider_count = 0, + npc_list[4], + npc_count = 0; i32 idx; while( bh_next( world->entity_bh, &it, &idx ) ){ @@ -900,20 +904,26 @@ static void render_other_entities( world_instance *world, vg_camera *cam ){ type = mdl_entity_id_type( id ), index = mdl_entity_id_id( id ); - if( type == k_ent_glider ) { + if( type == k_ent_glider ) + { if( glider_count < vg_list_size(glider_list) ) glider_list[ glider_count ++ ] = index; } + else if( type == k_ent_npc ) + { + if( npc_count < vg_list_size(npc_list) ) + npc_list[ npc_count ++ ] = index; + } } shader_model_entity_use(); shader_model_entity_uTexMain( 0 ); shader_model_entity_uCamera( cam->transform[3] ); shader_model_entity_uPv( cam->mtx.pv ); - WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_entity ); - for( u32 j=0; jent_glider, glider_list[j] ); if( !(glider->flags & 0x1) ) @@ -928,6 +938,14 @@ static void render_other_entities( world_instance *world, vg_camera *cam ){ render_glider_model( cam, world, mdl, k_board_shader_entity ); } + + for( u32 j=0; jent_npc, npc_list[j] ); + npc_update( npc ); + npc_render( npc, world, cam ); + } } void render_world( world_instance *world, vg_camera *cam, diff --git a/world_routes.h b/world_routes.h index 6467977..621c28a 100644 --- a/world_routes.h +++ b/world_routes.h @@ -4,6 +4,7 @@ #pragma once #include "vg/vg_camera.h" +#include "vg/vg_msg.h" #include "world.h" #include "network_msg.h" diff --git a/world_routes_ui.c b/world_routes_ui.c index ace8a3d..a4e2f10 100644 --- a/world_routes_ui.c +++ b/world_routes_ui.c @@ -1,6 +1,7 @@ #include "skaterift.h" #include "world_routes_ui.h" #include "world_routes.h" +#include "player.h" static u32 v4_rgba( v4f colour ){ u32 r = vg_minf(1.0f,colour[0])*255.0f, -- 2.25.1