From: hgn Date: Thu, 8 Feb 2024 03:46:07 +0000 (+0000) Subject: Merge all features from glider feature branch X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=217627b6b4eef904ac89b5f7f7f819b3a0970b20;hp=284dd9f21b28acb957eb6f16ef48f4f9527daa54;p=carveJwlIkooP6JGAAIwe30JlM.git Merge all features from glider feature branch --- diff --git a/blender_export.py b/blender_export.py index 695fff0..421cf5d 100644 --- a/blender_export.py +++ b/blender_export.py @@ -43,13 +43,14 @@ sr_entity_list = [ ('ent_miniworld', 'Mini World', '', 22 ), ('ent_prop', 'Prop', '', 23 ), ('ent_list', 'Entity List', '', 24 ), - ('ent_region', 'Region', '', 25 ) + ('ent_region', 'Region', '', 25 ), + ('ent_glider', 'Glider', '', 26 ), ] -MDL_VERSION_NR = 104 +MDL_VERSION_NR = 105 SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_challenge', \ 'ent_relay', 'ent_skateshop', 'ent_objective', 'ent_route',\ - 'ent_miniworld', 'ent_region' ] + 'ent_miniworld', 'ent_region', 'ent_glider', 'ent_list' ] def get_entity_enum_id( alias ): #{ @@ -244,12 +245,26 @@ class union_file_audio_clip(Union): ("reserved",vg_audio_clip)] #} +# NOTE: not really an entity. no reason for ent_ -- makes more sense as file_, +# but then again, too late to change because compat. class ent_audio_clip(Structure): #{ _fields_ = [("_anon",union_file_audio_clip), ("probability",c_float)] #} +class ent_list(Structure): +#{ + _fields_ = [("entity_ref_start",c_uint32), + ("entity_ref_count",c_uint32)] +#} + +# used in ent_list +class file_entity_ref(Structure): +#{ + _fields_ = [("index",c_uint32)] +#} + class ent_checkpoint(Structure): #{ _fields_ = [("gate_index",c_uint16), @@ -279,6 +294,14 @@ class ent_list(Structure):#{ _fields_ = [("start",c_uint16),("count",c_uint16)] #} +class ent_glider(Structure):#{ + _fields_ = [("transform",mdl_transform), + ("flags",c_uint32), + ("cooldown",c_float)] + sr_functions = { 0: 'unlock', + 1: 'equip' } +#} + class ent_water(Structure): #{ _fields_ = [("transform",mdl_transform), @@ -528,7 +551,9 @@ class ent_region(Structure):#{ ("submesh_start",c_uint32), ("submesh_count",c_uint32), ("pstr_title",c_uint32), ("flags",c_uint32), - ("zone_volume",c_uint32)] + ("zone_volume",c_uint32), + #105+ + ("target0",c_uint32*2)] sr_functions = { 0: 'enter', 1: 'leave' } #} @@ -1729,6 +1754,7 @@ def sr_compile( collection ): #} audio_clip_count = 0 + entity_file_ref_count = 0 for ent_type, arr in sr_compile.entities.items():#{ print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}") @@ -2003,6 +2029,8 @@ def sr_compile( collection ): sr_compile_mesh_internal( obj ) region.pstr_title = sr_compile_string( obj_data.title ) region.zone_volume = sr_entity_id( obj_data.zone_volume ) + region.target0[0] = sr_entity_id( obj_data.target0 ) + region.target0[1] = obj_data.target0_event sr_ent_push( region ) #} elif ent_type == 'ent_relay':#{ @@ -2018,6 +2046,27 @@ def sr_compile( collection ): relay.targets[3][1] = obj_data.target3_event sr_ent_push( relay ) #} + # elif ent_type == 'ent_list':#{ + # lista = ent_list() + # obj_data = obj.SR_data.ent_list[0] + + # lista.entity_ref_start = entity_file_ref_count + # lista.entity_ref_count = len( obj_data.entities ) + # entity_file_ref_count += lista.entity_ref_count + + # for child in obj_data.entities:#{ + # reference_struct = file_entity_ref() + # reference_struct.index = sr_entity_id( child.target ) + # sr_ent_push( reference_struct ) + # #} + + # sr_ent_push( lista ) + # #} + elif ent_type == 'ent_glider':#{ + glider = ent_glider() + compile_obj_transform( obj, glider.transform ) + sr_ent_push( glider ) + #} elif ent_type == 'ent_cubemap':#{ cubemap = ent_cubemap() co = obj.matrix_world @ Vector((0,0,0)) @@ -3095,6 +3144,9 @@ class SR_OBJECT_ENT_LIST(bpy.types.PropertyGroup):#{ #} #} +class SR_OBJECT_ENT_GLIDER(bpy.types.PropertyGroup):#{ + nothing: bpy.props.StringProperty() +#} class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{ subtype: bpy.props.EnumProperty( @@ -3532,6 +3584,18 @@ class SR_OBJECT_ENT_REGION(bpy.types.PropertyGroup):#{ zone_volume: bpy.props.PointerProperty( type=bpy.types.Object, name="Zone Volume", \ poll=lambda self,obj: sr_filter_ent_type(obj,['ent_volume'])) + + target0: bpy.props.PointerProperty( \ + type=bpy.types.Object, name="Triger on unlock", \ + poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE)) + target0_event: bpy.props.IntProperty( name="Event/Method" ) + + @staticmethod + def sr_inspector( layout, data ):#{ + layout.prop( data[0], 'title' ) + layout.prop( data[0], 'zone_volume' ) + SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target0' ) + #} #} class SR_OBJECT_ENT_RELAY(bpy.types.PropertyGroup):#{ @@ -3586,6 +3650,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): ent_relay: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_RELAY) 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_type: bpy.props.EnumProperty( name="Type", @@ -4210,6 +4275,14 @@ def draw_skeleton_helpers( obj ): #} #} +def cv_draw_wireframe( mdl, points, colour ):#{ + for i in range(len(points)//2):#{ + p0 = mdl@points[i*2+0] + p1 = mdl@points[i*2+1] + cv_draw_line( p0, p1, colour ) + #} +#} + def cv_ent_gate( obj ): #{ global cv_view_verts, cv_view_colours @@ -4624,6 +4697,20 @@ def cv_draw():#{ #} #} #} + elif ent_type == 'ent_glider':#{ + mesh = [Vector((-1.13982, 0.137084, -0.026358)), \ + Vector(( 1.13982, 0.137084, -0.026358)), \ + Vector(( 0.0, 1.6, 1.0)), \ + Vector(( 0.0, -3.0, 1.0)), \ + Vector(( -3.45, -1.78, 0.9)), \ + Vector(( 0.0, 1.6, 1.0)), \ + Vector(( 3.45, -1.78, 0.9)), \ + Vector(( 0.0, 1.6, 1.0)), \ + Vector(( 3.45, -1.78, 0.9)), \ + Vector(( -3.45, -1.78, 0.9))] + + cv_draw_wireframe( obj.matrix_world, mesh, (1,1,1) ) + #} elif ent_type == 'ent_skateshop':#{ data = obj.SR_data.ent_skateshop[0] display = data.mark_display @@ -4691,6 +4778,22 @@ def cv_draw():#{ if display1: cv_draw_ucube(display1.matrix_world, cc1, display_cu, display_co) #} + # elif ent_type == 'ent_list':#{ + # data = obj.SR_data.ent_list[0] + # for child in data.entities:#{ + # if child.target:#{ + # cv_draw_arrow( obj.location, child.target.location, \ + # (.5,.5,.5), 0.1 ) + # #} + # #} + # #} + elif ent_type == 'ent_region':#{ + data = obj.SR_data.ent_region[0] + if data.target0:#{ + cv_draw_arrow( obj.location, data.target0.location, \ + (.5,.5,.5), 0.1 ) + #} + #} elif ent_type == 'ent_menuitem':#{ for i,col in enumerate(obj.users_collection):#{ colour32 = hash_djb2( col.name ) @@ -4827,6 +4930,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_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \ diff --git a/build.c b/build.c index 5958fa3..cb7a71a 100644 --- a/build.c +++ b/build.c @@ -267,9 +267,8 @@ void build_shaders(void){ _S( "model_gate_unlinked", "model.vs", "model_gate_unlinked.fs" ); _S( "model_font", "model_font.vs", "model_font.fs" ); - /* Pointcloud */ - //_S( "point_map", "cloud.vs", "cloud.fs" ); _S( "particle", "particle.vs", "particle.fs" ); + _S( "trail", "trail.vs", "trail.fs" ); /* 2D */ _S( "blit", "blit.vs", "blit.fs" ); diff --git a/ent_glider.c b/ent_glider.c new file mode 100644 index 0000000..d1e2a36 --- /dev/null +++ b/ent_glider.c @@ -0,0 +1,21 @@ +#pragma once +#include "entity.h" +#include "player_glide.h" + +static 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 ); + + if( call->function == 0 ){ + glider->flags |= 0x1; + } + else if( call->function == 1 ){ + if( glider->flags & 0x1 ){ + player_glide_equip_glider(); + } + } + else { + vg_print_backtrace(); + vg_error( "Unhandled function id: %i\n", call->function ); + } +} diff --git a/ent_glider.h b/ent_glider.h new file mode 100644 index 0000000..e61bdd0 --- /dev/null +++ b/ent_glider.h @@ -0,0 +1,4 @@ +#pragma once +#include "entity.h" + +static void ent_glider_call( world_instance *world, ent_call *call ); diff --git a/ent_region.c b/ent_region.c index fd24f4e..44d49a0 100644 --- a/ent_region.c +++ b/ent_region.c @@ -113,6 +113,20 @@ static void ent_region_re_eval( world_instance *world ){ region->flags = combined; world_total &= combined; + + /* run unlock triggers. v105+ */ + if( world->meta.info.version >= 105 ){ + if( region->flags & (k_ent_route_flag_achieve_gold| + k_ent_route_flag_achieve_silver) ){ + if( region->target0[0] ){ + ent_call call; + call.data = NULL; + call.id = region->target0[0]; + call.function = region->target0[1]; + entity_call( world, &call ); + } + } + } } u32 instance_id = world - world_static.instances; diff --git a/entity.c b/entity.c index 2ff0cab..ce6d98e 100644 --- a/entity.c +++ b/entity.c @@ -13,6 +13,7 @@ #include "ent_miniworld.c" #include "ent_region.c" #include "ent_traffic.c" +#include "ent_glider.c" typedef void (*fn_entity_call_handler)( world_instance *, ent_call *); @@ -30,7 +31,8 @@ static void entity_call( world_instance *world, ent_call *call ){ [k_ent_challenge] = ent_challenge_call, [k_ent_route] = ent_route_call, [k_ent_miniworld] = ent_miniworld_call, - [k_ent_region] = ent_region_call + [k_ent_region] = ent_region_call, + [k_ent_glider] = ent_glider_call }; if( type >= vg_list_size(table) ){ diff --git a/entity.h b/entity.h index 1c11f38..1adeb7e 100644 --- a/entity.h +++ b/entity.h @@ -33,6 +33,8 @@ typedef struct ent_cubemap ent_cubemap; typedef struct ent_miniworld ent_miniworld; typedef struct ent_prop ent_prop; typedef struct ent_region ent_region; +typedef struct ent_list ent_list; +typedef struct ent_glider ent_glider; enum entity_alias{ k_ent_none = 0, @@ -60,7 +62,8 @@ enum entity_alias{ k_ent_miniworld = 22, k_ent_prop = 23, k_ent_list = 24, - k_ent_region = 25 + k_ent_region = 25, + k_ent_glider = 26 }; static u32 mdl_entity_id_type( u32 entity_id ){ @@ -548,7 +551,16 @@ struct ent_prop { struct ent_region { mdl_transform transform; - u32 submesh_start, submesh_count, pstr_title, flags, zone_volume; + u32 submesh_start, submesh_count, pstr_title, flags, zone_volume, + + /* 105+ */ + target0[2]; +}; + +struct ent_glider { + mdl_transform transform; + u32 flags; + f32 cooldown; }; #include "world.h" diff --git a/gameserver.c b/gameserver.c index a93d705..5bbb8ae 100644 --- a/gameserver.c +++ b/gameserver.c @@ -10,7 +10,6 @@ volatile sig_atomic_t sig_stop; #include "gameserver.h" -#include "highscores.c" #include "vg/vg_opt.h" #include "network_common.h" #include "gameserver_db.h" @@ -516,7 +515,7 @@ static void gameserver_propogate_player_frame( int client_id, basic->sound_effects = 0; struct gameserver_client *c0 = &gameserver.clients[client_id]; - c0->instance = frame->instance_id; + c0->instance = frame->flags & NETMSG_PLAYERFRAME_INSTANCE_ID; for( int i=0; i #define CLIENT_KNOWLEDGE_SAME_WORLD0 0x1 diff --git a/gameserver_db.h b/gameserver_db.h index 55a4f47..fe39931 100644 --- a/gameserver_db.h +++ b/gameserver_db.h @@ -5,7 +5,6 @@ #include "vg/vg_mem_queue.h" #include "network_common.h" #include "dep/sqlite3/sqlite3.h" -#include "highscores.h" #include #include diff --git a/gui.h b/gui.h index 1414bd5..67f78b5 100644 --- a/gui.h +++ b/gui.h @@ -19,6 +19,7 @@ enum gui_icon { k_gui_icon_player, k_gui_icon_rift_run_gold, k_gui_icon_rift_run_silver, + k_gui_icon_glider, k_gui_icon_count, }; @@ -329,6 +330,7 @@ static void gui_init(void){ gui.icons[ k_gui_icon_rift_run_2d ] = gui_find_icon( "icon_rift_run2d" ); gui.icons[ k_gui_icon_friend ] = gui_find_icon( "icon_friend" ); gui.icons[ k_gui_icon_player ] = gui_find_icon( "icon_player" ); + gui.icons[ k_gui_icon_glider ] = gui_find_icon( "icon_glider" ); gui.icons[ k_gui_icon_rift_run_gold ] = gui_find_icon("icon_rift_run_medal_gold"); gui.icons[ k_gui_icon_rift_run_silver]= diff --git a/maps_src/dev_flatworld/main.mdl b/maps_src/dev_flatworld/main.mdl index 10619c5..f9c326c 100644 Binary files a/maps_src/dev_flatworld/main.mdl and b/maps_src/dev_flatworld/main.mdl differ diff --git a/maps_src/mp_mtzero/main.mdl b/maps_src/mp_mtzero/main.mdl index 76139c8..b743ebe 100644 Binary files a/maps_src/mp_mtzero/main.mdl and b/maps_src/mp_mtzero/main.mdl differ diff --git a/model.h b/model.h index 62438dc..ba1c3f2 100644 --- a/model.h +++ b/model.h @@ -8,7 +8,7 @@ #include "skaterift.h" #define MDL_VERSION_MIN 101 -#define MDL_VERSION_NR 104 +#define MDL_VERSION_NR 105 enum mdl_shader{ k_shader_standard = 0, diff --git a/models_src/ch_none.mdl b/models_src/ch_none.mdl index 5dacbe1..760fda4 100644 Binary files a/models_src/ch_none.mdl and b/models_src/ch_none.mdl differ diff --git a/models_src/glider.mdl b/models_src/glider.mdl new file mode 100644 index 0000000..0a2ca3c Binary files /dev/null and b/models_src/glider.mdl differ diff --git a/models_src/rs_icons.mdl b/models_src/rs_icons.mdl index e3ee8b5..7dec122 100644 Binary files a/models_src/rs_icons.mdl and b/models_src/rs_icons.mdl differ diff --git a/network_msg.h b/network_msg.h index dfd6dab..5d3beee 100644 --- a/network_msg.h +++ b/network_msg.h @@ -44,6 +44,9 @@ struct netmsg_version{ #define NETMSG_BOUNDARY_BIT 0x8000 #define NETMSG_GATE_BOUNDARY_BIT 0x4000 #define NETMSG_BOUNDARY_MASK (NETMSG_BOUNDARY_BIT|NETMSG_GATE_BOUNDARY_BIT) +#define NETMSG_PLAYERFRAME_INSTANCE_ID 0x3 +#define NETMSG_PLAYERFRAME_HAVE_GLIDER 0x4 +#define NETMSG_PLAYERFRAME_GLIDER_ORPHAN 0x8 typedef struct netmsg_playerframe netmsg_playerframe; enum{ k_inetmsg_playerframe = 200 }; @@ -52,7 +55,7 @@ struct netmsg_playerframe{ f64 timestamp; u8 client, subsystem, - instance_id, sound_effects; + flags, sound_effects; u16 boundary_hash; /* used for animating correctly through gates, teleport.. msb is a flip flop for teleporting second msb is flip flop for gate */ diff --git a/particle.c b/particle.c index d12a87a..9c1f87e 100644 --- a/particle.c +++ b/particle.c @@ -1,4 +1,5 @@ #include "particle.h" +#include "shaders/trail.h" static void particle_spawn( particle_system *sys, v3f co, v3f v, f32 lifetime, u32 colour ){ @@ -94,13 +95,11 @@ static void async_particle_init( void *payload, u32 size ){ VG_CHECK_GL_ERR(); } -static void particle_alloc( particle_system *sys, u32 max ){ - static int reg = 1; - if( reg ){ - shader_particle_register(); - reg = 0; - } +static void particle_init(void){ + shader_particle_register(); +} +static void particle_alloc( particle_system *sys, u32 max ){ size_t stride = sizeof(particle_vert); sys->max = max; diff --git a/player.c b/player.c index 7a099f0..1d690a0 100644 --- a/player.c +++ b/player.c @@ -112,6 +112,10 @@ static void player__pre_update(void){ static void player__update(void){ if( player_subsystems[ localplayer.subsystem ]->update ) player_subsystems[ localplayer.subsystem ]->update(); + + if( localplayer.glider_orphan && + (skaterift.activity != k_skaterift_replay) ) + glider_physics( (v2f){0,0} ); } static void player__post_update(void) @@ -245,7 +249,7 @@ static void player__im_gui(void) static void player__setpos( v3f pos ){ v3_copy( pos, localplayer.rb.co ); v3_zero( localplayer.rb.v ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); } static void player__clean_refs(void){ @@ -277,13 +281,15 @@ static void player__reset(void){ if( (l < 0.9f) || (l > 1.1f) ) q_identity( localplayer.rb.q ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); localplayer.subsystem = k_player_subsystem_walk; player__walk_reset(); localplayer.immobile = 0; localplayer.gate_waiting = NULL; + localplayer.have_glider = 0; + localplayer.glider_orphan = 0; v3_copy( localplayer.rb.co, localplayer.cam_control.tpv_lpf ); player__clean_refs(); @@ -377,6 +383,7 @@ static void player__networked_sfx( u8 system, u8 priority, u8 id, #include "player_skate.c" #include "player_dead.c" #include "player_drive.c" +#include "player_glide.c" #include "player_basic_info.c" #include "player_render.c" diff --git a/player.h b/player.h index cb37b7c..ad15227 100644 --- a/player.h +++ b/player.h @@ -12,6 +12,7 @@ enum player_subsystem{ k_player_subsystem_dead = 2, k_player_subsystem_drive = 3, k_player_subsystem_basic_info = 4, + k_player_subsystem_glide = 5, k_player_subsystem_max, k_player_subsystem_invalid = 255 }; @@ -47,6 +48,7 @@ struct player_subsystem_interface{ void(*effects)( void *animator, m4x3f *final_mtx, struct player_board *board, struct player_effects_data *effect_data ); void(*post_animate)(void); + void(*network_animator_exchange)( bitpack_ctx *ctx, void *data ); void(*sfx_oneshot)( u8 id, v3f pos, f32 volume ); @@ -68,6 +70,7 @@ struct player_subsystem_interface{ #include "player_skate.h" #include "player_dead.h" #include "player_drive.h" +#include "player_glide.h" #include "player_basic_info.h" #include "player_replay.h" @@ -78,12 +81,15 @@ struct player_subsystem_interface{ static i32 k_cinema_fixed = 0; static f32 k_cinema = 0.0f; static i32 k_invert_y = 0; +static f32 k_cam_dist = 1.8f; struct { /* transform definition */ rigidbody rb; v3f angles; + bool have_glider, glider_orphan; + /* * Camera management * --------------------------- @@ -97,7 +103,9 @@ struct { cam_velocity_constant, cam_velocity_coefficient_smooth, cam_velocity_constant_smooth, - cam_velocity_influence_smooth; + cam_velocity_influence_smooth, + cam_dist, + cam_dist_smooth; v3f cam_land_punch, cam_land_punch_v; ent_gate *gate_waiting; @@ -193,6 +201,7 @@ struct player_subsystem_interface static *player_subsystems[] = { [k_player_subsystem_drive] = &player_subsystem_drive, [k_player_subsystem_skate] = &player_subsystem_skate, [k_player_subsystem_basic_info]=&player_subsystem_basic_info, + [k_player_subsystem_glide] = &player_subsystem_glide, }; /* diff --git a/player_common.c b/player_common.c index 07b1f5a..5236ee5 100644 --- a/player_common.c +++ b/player_common.c @@ -69,6 +69,12 @@ static void player__cam_iterate(void){ v3_copy( (v3f){0.0f,0.0f,0.0f}, cc->fpv_offset ); v3_copy( (v3f){0.0f,1.4f,0.0f}, cc->tpv_offset ); } + else if( localplayer.subsystem == k_player_subsystem_glide ){ + v3_copy( (v3f){-0.15f,1.75f,0.0f}, cc->fpv_viewpoint ); + v3_copy( (v3f){0.0f,0.0f,0.0f}, cc->fpv_offset ); + v3_copy( (v3f){0.0f,-1.0f,0.0f}, cc->tpv_offset ); + v3_add( cc->tpv_offset_extra, cc->tpv_offset, cc->tpv_offset ); + } else{ v3_copy( (v3f){-0.15f,1.75f,0.0f}, cc->fpv_viewpoint ); v3_copy( (v3f){0.0f,0.0f,0.0f}, cc->fpv_offset ); @@ -83,6 +89,16 @@ static void player__cam_iterate(void){ /* lerping */ + if( localplayer.cam_dist_smooth == 0.0f ){ + localplayer.cam_dist_smooth = localplayer.cam_dist; + } + else { + localplayer.cam_dist_smooth = vg_lerpf( + localplayer.cam_dist_smooth, + localplayer.cam_dist, + vg.time_frame_delta * 8.0f ); + } + localplayer.cam_velocity_influence_smooth = vg_lerpf( localplayer.cam_velocity_influence_smooth, localplayer.cam_velocity_influence, @@ -194,12 +210,15 @@ static void player__cam_iterate(void){ /* now move into world */ v3f tpv_pos, tpv_offset, tpv_origin; + /* TODO: whats up with CC and not CC but both sets of variables are doing + * the same ideas just saved in different places? + */ /* origin */ q_mulv( pq, cc->tpv_offset_smooth, tpv_origin ); v3_add( tpv_origin, cc->tpv_lpf, tpv_origin ); /* offset */ - v3_muls( camera_follow_dir, 1.8f, tpv_offset ); + v3_muls( camera_follow_dir, localplayer.cam_dist_smooth, tpv_offset ); v3_muladds( tpv_offset, cc->cam_velocity_smooth, -0.025f, tpv_offset ); v3_add( tpv_origin, tpv_offset, tpv_pos ); diff --git a/player_dead.c b/player_dead.c index 7733c32..625ff0e 100644 --- a/player_dead.c +++ b/player_dead.c @@ -15,11 +15,11 @@ static void player__dead_post_update(void){ v3f ext_co; v4f ext_q; - rb_extrapolate( &part->obj.rb, ext_co, ext_q ); + rb_extrapolate( &part->rb, ext_co, ext_q ); v3_lerp( d->co_lpf, ext_co, vg.time_frame_delta*4.0f, d->co_lpf ); - v3_lerp( d->v_lpf, part->obj.rb.v, vg.time_frame_delta*4.0f, d->v_lpf ); - v3_lerp( d->w_lpf, part->obj.rb.w, vg.time_frame_delta*4.0f, d->w_lpf ); + v3_lerp( d->v_lpf, part->rb.v, vg.time_frame_delta*4.0f, d->v_lpf ); + v3_lerp( d->w_lpf, part->rb.w, vg.time_frame_delta*4.0f, d->w_lpf ); v3_copy( d->co_lpf, localplayer.rb.co ); v3_zero( localplayer.rb.v ); @@ -66,9 +66,9 @@ static void player__dead_animate(void){ v3f co_int; float substep = vg.time_fixed_extrapolate; - v3_lerp( part->prev_co, part->obj.rb.co, substep, co_int ); - q_nlerp( part->prev_q, part->obj.rb.q, substep, q_int ); - v4_copy( part->obj.rb.q, q_int ); + v3_lerp( part->prev_co, part->rb.co, substep, co_int ); + q_nlerp( part->prev_q, part->rb.q, substep, q_int ); + v4_copy( part->rb.q, q_int ); q_m3x3( q_int, mtx ); v3_copy( co_int, mtx[3] ); @@ -144,9 +144,9 @@ static void player__dead_transition( enum player_die_type type ){ struct ragdoll_part *part = &localplayer.ragdoll.parts[ localplayer.id_hip-1 ]; - v3_copy( part->obj.rb.co, player_dead.co_lpf ); - v3_copy( part->obj.rb.v, player_dead.v_lpf ); - v3_copy( part->obj.rb.w, player_dead.w_lpf ); + v3_copy( part->rb.co, player_dead.co_lpf ); + v3_copy( part->rb.v, player_dead.v_lpf ); + v3_copy( part->rb.w, player_dead.w_lpf ); gui_helper_clear(); vg_str str; diff --git a/player_drive.c b/player_drive.c index 38b7e58..7881302 100644 --- a/player_drive.c +++ b/player_drive.c @@ -17,10 +17,10 @@ static void player__drive_pre_update(void){ static void player__drive_update(void){} static void player__drive_post_update(void){ - v3_copy( player_drive.vehicle->obj.rb.co,localplayer.rb.co ); - v3_copy( player_drive.vehicle->obj.rb.v, localplayer.rb.v ); - v4_copy( player_drive.vehicle->obj.rb.q, localplayer.rb.q ); - v3_copy( player_drive.vehicle->obj.rb.w, localplayer.rb.w ); + v3_copy( player_drive.vehicle->rb.co,localplayer.rb.co ); + v3_copy( player_drive.vehicle->rb.v, localplayer.rb.v ); + v4_copy( player_drive.vehicle->rb.q, localplayer.rb.q ); + v3_copy( player_drive.vehicle->rb.w, localplayer.rb.w ); } static void player__drive_animate(void){} @@ -39,7 +39,7 @@ static void player__drive_post_animate(void){ else localplayer.cam_velocity_influence = 1.0f; - rigidbody *rb = &gzoomer.obj.rb; + rigidbody *rb = &gzoomer.rb; float yaw = atan2f( -rb->to_world[2][0], rb->to_world[2][2] ), pitch = atan2f ( diff --git a/player_glide.c b/player_glide.c new file mode 100644 index 0000000..af44ea6 --- /dev/null +++ b/player_glide.c @@ -0,0 +1,626 @@ +#ifndef PLAYER_GLIDE_C +#define PLAYER_GLIDE_C + +#include "player_glide.h" +#include "input.h" + +#include "vg/vg_rigidbody.h" +#include "scene_rigidbody.h" +#include "shaders/model_board_view.h" +#include "shaders/model_entity.h" + +static f32 k_glide_steer = 2.0f, + k_glide_cl = 0.04f, + k_glide_cs = 0.02f, + k_glide_drag = 0.0001f, + k_glide_slip_yaw = 0.1f, + k_glide_lift_pitch = 0.0f, + k_glide_wing_orient = -0.1f, + k_glide_balance = 1.0f; + +static i32 k_glide_pause = 0; + +static void player_glide_pre_update(void){ + if( button_down(k_srbind_use) ){ + localplayer.subsystem = k_player_subsystem_skate; + localplayer.glider_orphan = 1; + + player_skate.state.activity = k_skate_activity_air; + player_skate.state.activity_prev = k_skate_activity_air; + + q_mulv( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f}, player_skate.state.cog ); + v3_add( player_skate.state.cog, localplayer.rb.co, + player_skate.state.cog ); + v3_copy( localplayer.rb.v, player_skate.state.cog_v ); + + player__begin_holdout( (v3f){0.0f,0.0f,0.0f} ); + player__skate_reset_animator(); + player__skate_clear_mechanics(); + v3_copy( (v3f){0.0f,0.0f,0.0f}, player_skate.state.trick_euler ); + + player__approximate_best_trajectory(); + } +} + +static void massless_accel( rigidbody *rb, v3f delta, v3f impulse ){ + /* linear */ + v3_muladds( rb->v, impulse, k_rb_delta, rb->v ); + + /* Angular velocity */ + v3f wa; + v3_cross( delta, impulse, wa ); + v3_muladds( rb->w, wa, k_rb_delta, rb->w ); +} + +static void calculate_lift( v3f vl, f32 aoa_bias, + v3f axis, v3f back, f32 power, + v3f out_force ){ + v3f up; + v3_cross( back, axis, up ); + + v3f wind; + v3_muladds( vl, axis, -v3_dot(axis,vl), wind ); + + f32 windv2 = v3_length2(wind), + aoa = atan2f( v3_dot( up, wind ), v3_dot( back, wind ) ) + aoa_bias, + cl = aoa / VG_PIf, /* TODO: make it a curve */ + L = windv2 * cl * power; + + v3f lift_dir; + v3_normalize( wind ); + v3_cross( wind, axis, lift_dir ); + + /* this is where induced drag (from the flappy things) would go */ + + v3_muls( lift_dir, L, out_force ); +} + +static void calculate_drag( v3f vl, f32 cd, v3f out_force ){ + f32 v2 = v3_length2( vl ); + v3f dir; + v3_copy( vl, dir ); + v3_normalize( dir ); + v3_muls( vl, -cd*v2, out_force ); +} + +/* + * Returns true if the bottom sphere is hit + */ +static bool glider_physics( v2f steer ){ + rigidbody *rb = &player_glide.rb; + + /* lift */ + v3f vl, wl; + m3x3_mulv( rb->to_local, rb->v, vl ); + m3x3_mulv( rb->to_local, rb->w, wl ); + + v3f F, Flift, Fslip, Fdrag, FslipW, FliftW; + + calculate_lift( vl, steer[1]*k_glide_steer, + (v3f){1,0,0}, + (v3f){0,sinf(k_glide_wing_orient),cosf(k_glide_wing_orient)}, + k_glide_cl, Flift ); + v3_copy( Flift, player_glide.info_lift ); + v3_cross( (v3f){0,0,0}, Flift, FliftW ); + + calculate_lift( vl, 0.0f, + (v3f){0,1,0},(v3f){0,0,1}, + k_glide_cs, Fslip ); + v3_copy( Fslip, player_glide.info_slip ); + v3_cross( (v3f){0,k_glide_lift_pitch,k_glide_slip_yaw}, Fslip, FslipW ); + + calculate_drag( vl, k_glide_drag, Fdrag ); + v3_copy( Fdrag, player_glide.info_drag ); + + v3f balance = {0.0f,-k_glide_balance,0.0f}; + m3x3_mulv( rb->to_local, balance, balance ); + + v3f Fw = { + steer[1]*k_glide_steer - balance[2], + 0.0f, + -steer[0]*k_glide_steer + balance[0], + }; + + if( player_glide.ticker ){ + player_glide.ticker --; + return 0; + } + player_glide.ticker += k_glide_pause; + + /* apply forces */ + v3_add( Flift, Fslip, F ); + v3_add( F, Fdrag, F ); + + m3x3_mulv( rb->to_world, F, F ); + v3_muladds( rb->v, F, k_rb_delta, rb->v ); + + v3_add( Fw, FslipW, Fw ); + v3_add( Fw, FliftW, Fw ); + m3x3_mulv( rb->to_world, Fw, Fw ); + v3_muladds( rb->w, Fw, k_rb_delta, rb->w ); + + + /* + * collisions & constraints + */ + world_instance *world = world_current_instance(); + rb_solver_reset(); + + bool bottom_hit = 0; + + rigidbody _null = {0}; + _null.inv_mass = 0.0f; + m3x3_zero( _null.iI ); + for( u32 i=0; i < vg_list_size(player_glide.parts); i ++ ){ + m4x3f mmdl; + m4x3_mul( rb->to_world, player_glide.parts[i].mdl, mmdl ); + + if( player_glide.parts[i].shape == k_rb_shape_capsule ){ + vg_line_capsule( mmdl, + player_glide.parts[i].inf.r, + player_glide.parts[i].inf.h, + VG__BLACK ); + } + else if( player_glide.parts[i].shape == k_rb_shape_sphere ){ + vg_line_sphere( mmdl, player_glide.parts[i].r, 0 ); + } + + if( rb_global_has_space() ){ + rb_ct *buf = rb_global_buffer(); + + u32 l = 0; + + if( player_glide.parts[i].shape == k_rb_shape_capsule ){ + l = rb_capsule__scene( mmdl, &player_glide.parts[i].inf, + NULL, world->geo_bh, buf, + k_material_flag_ghosts ); + } + else if( player_glide.parts[i].shape == k_rb_shape_sphere ){ + l = rb_sphere__scene( mmdl, player_glide.parts[i].r, + NULL, world->geo_bh, buf, + k_material_flag_ghosts ); + } + + if( player_glide.parts[i].is_damage && l ){ + bottom_hit = 1; + } + + for( u32 j=0; janimator; + rb_extrapolate( &localplayer.rb, animator->root_co, animator->root_q ); +} + +static void player_glide_pose( void *_animator, player_pose *pose ){ + struct skeleton *sk = &localplayer.skeleton; + struct player_glide_animator *animator = _animator; + pose->type = k_player_pose_type_ik; + pose->board.lean = 0.0f; + + skeleton_sample_anim( sk, player_glide.anim_glide, 0.0f, pose->keyframes ); + + v3f temp; + q_mulv( animator->root_q, (v3f){0,-0.5f,0}, temp ); + v3_add( animator->root_co, temp, pose->root_co ); + + v4_copy( animator->root_q, pose->root_q ); +} + +static void player_glide_post_animate(void){ + if( localplayer.cam_control.camera_mode == k_cam_firstperson ) + localplayer.cam_velocity_influence = 0.0f; + else + localplayer.cam_velocity_influence = 0.0f; + + v3f fwd; + v3_muls( localplayer.rb.to_world[2], -1.0f, fwd ); + v3_angles( fwd, localplayer.angles ); + + localplayer.cam_dist = 2.0f + v3_length( localplayer.rb.v )*0.2f; +} + +static void player_glide_animator_exchange( bitpack_ctx *ctx, void *data ){ + struct player_glide_animator *animator = data; + + bitpack_qv3f( ctx, 24, -1024.0f, 1024.0f, animator->root_co ); + bitpack_qquat( ctx, animator->root_q ); +} + +static void +player_glide_remote_animator_exchange( bitpack_ctx *ctx, void *data ){ + struct remote_glider_animator *animator = data; + + bitpack_qv3f( ctx, 24, -1024.0f, 1024.0f, animator->root_co ); + bitpack_qf32( ctx, 8, 0.0f, 1.0f, &animator->s ); + bitpack_qquat( ctx, animator->root_q ); +} + +static void player_glide_im_gui(void){ + player__debugtext( 1, "Nothing here" ); + player__debugtext( 1, " lift: %.2f %.2f %.2f", + player_glide.info_lift[0], + player_glide.info_lift[1], + player_glide.info_lift[2] ); + player__debugtext( 1, " slip: %.2f %.2f %.2f", + player_glide.info_slip[0], + player_glide.info_slip[1], + player_glide.info_slip[2] ); + player__debugtext( 1, " drag: %.2f %.2f %.2f", + player_glide.info_drag[0], + player_glide.info_drag[1], + player_glide.info_drag[2] ); +} + +static void player_glide_equip_glider(void){ + if( !localplayer.have_glider ){ + localplayer.have_glider = 1; + localplayer.glider_orphan = 0; + player_glide.t = -1.0f; + } +} + +static int ccmd_player_glider_spawn( int argc, const char *argv[] ){ + if( vg_console.cheats ){ + player_glide_equip_glider(); + } + else { + vg_error( "Can't spawn without cheats enabled.\n" ); + } + return 0; +} + +static void player_glide_bind(void){ + + u32 mask = VG_VAR_CHEAT|VG_VAR_PERSISTENT; + VG_VAR_F32( k_glide_steer, flags=mask ); + VG_VAR_F32( k_glide_cl, flags=mask ); + VG_VAR_F32( k_glide_cs, flags=mask ); + VG_VAR_F32( k_glide_drag, flags=mask ); + VG_VAR_F32( k_glide_slip_yaw, flags=mask ); + VG_VAR_F32( k_glide_lift_pitch, flags=mask ); + VG_VAR_I32( k_glide_pause, flags=mask ); + VG_VAR_F32( k_glide_balance, flags=mask ); + VG_VAR_F32( k_glide_wing_orient, flags=mask ); + + vg_console_reg_cmd( "spawn_glider", ccmd_player_glider_spawn, NULL ); + + f32 mass = 0.0f, + k_density = 8.0f, + k_inertia_scale = 1.0f; + m3x3f I; + m3x3_zero( I ); + + for( u32 i=0; itextures ); + player_glide.glider_textures = + vg_linear_alloc(alloc,vg_align8(sizeof(GLuint)*(count+1))); + player_glide.glider_textures[0] = vg.tex_missing; + + mdl_async_load_glmesh( mdl, &player_glide.glider_mesh, NULL ); + + for( u32 i=0; itextures, i ); + void *data = vg_linear_alloc( vg_mem.scratch, tex->file.pack_size ); + mdl_fread_pack_file( mdl, &tex->file, data ); + vg_tex2d_load_qoi_async( data, tex->file.pack_size, + VG_TEX2D_LINEAR|VG_TEX2D_CLAMP, + &player_glide.glider_textures[i+1] ); + } + + /* load trail positions */ + mdl_array_ptr markers; + MDL_LOAD_ARRAY( mdl, &markers, ent_marker, vg_mem.scratch ); + + for( u32 i=0; itransform.co, + player_glide.trail_positions[ player_glide.trail_count ++ ] ); + + if( player_glide.trail_count == vg_list_size(trails_glider) ) + break; + } + + mdl_close( mdl ); + + /* allocate effects */ + for( u32 i=0; i 0.0f ){ + v4f qyaw; + q_axis_angle( qyaw, (v3f){0,1,0}, VG_TAUf*0.5f ); + q_mul( qyaw, localplayer.rb.q, player_glide.rb.q ); + q_normalize( player_glide.rb.q ); + } + else + v4_copy( localplayer.rb.q, player_glide.rb.q ); + + v3_copy( localplayer.rb.v, player_glide.rb.v ); + v3_copy( localplayer.rb.w, player_glide.rb.w ); + rb_update_matrices( &player_glide.rb ); + + player__begin_holdout( (v3f){0,0,0} ); +} + +static void render_glider_model( camera *cam, world_instance *world, + m4x3f mmdl, enum board_shader shader ){ + u32 current_tex = 0xffffffff; + glActiveTexture( GL_TEXTURE0 ); + + mdl_context *mdl = &player_glide.glider; + mesh_bind( &player_glide.glider_mesh ); + + for( u32 i=0; imeshs); i ++ ){ + mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i ); + + m4x3f mmmdl; + mdl_transform_m4x3( &mesh->transform, mmmdl ); + m4x3_mul( mmdl, mmmdl, mmmdl ); + + if( shader == k_board_shader_player ) + shader_model_board_view_uMdl( mmmdl ); + else if( shader == k_board_shader_entity ){ + m4x4f m4mmmdl; + m4x3_expand( mmmdl, m4mmmdl ); + m4x4_mul( cam->mtx_prev.pv, m4mmmdl, m4mmmdl ); + + shader_model_entity_uMdl( mmmdl ); + shader_model_entity_uPvmPrev( m4mmmdl ); + } + + for( u32 j=0; jsubmesh_count; j ++ ){ + mdl_submesh *sm = mdl_arritm( &mdl->submeshs, mesh->submesh_start+j ); + if( !sm->material_id ) { + vg_error( "Invalid material ID 0\n" ); + continue; + } + + mdl_material *mat = mdl_arritm( &mdl->materials, sm->material_id-1 ); + if( mat->tex_diffuse != current_tex ){ + glBindTexture( GL_TEXTURE_2D, + player_glide.glider_textures[ mat->tex_diffuse ] ); + current_tex = mat->tex_diffuse; + } + + mdl_draw_submesh( sm ); + } + } +} + +/* + * TODO: more flexible way to call + * - this depends on the current state, but we need to pass a struct in + * that can hold that information instead so we can save it into + * the replay + */ +static void player_glide_render( camera *cam, world_instance *world, + player_pose *pose ){ + if( !((localplayer.subsystem == k_player_subsystem_glide) || + (localplayer.observing_system == k_player_subsystem_glide) || + localplayer.have_glider || + localplayer.glider_orphan) ) + return; + + shader_model_board_view_use(); + shader_model_board_view_uTexMain( 0 ); + shader_model_board_view_uCamera( cam->transform[3] ); + shader_model_board_view_uPv( cam->mtx.pv ); + + shader_model_board_view_uDepthCompare(1); + depth_compare_bind( + shader_model_board_view_uTexSceneDepth, + shader_model_board_view_uInverseRatioDepth, + shader_model_board_view_uInverseRatioMain, + cam ); + + WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_board_view ); + + mdl_keyframe kf_res; + if( localplayer.glider_orphan ){ + rb_extrapolate( &player_glide.rb, kf_res.co, kf_res.q ); + v3_fill( kf_res.s, 1.0f ); + + v3f temp; + q_mulv( kf_res.q, (v3f){0,-0.5f,0}, temp ); + v3_add( temp, kf_res.co, kf_res.co ); + } + else { + f32 target; + if( localplayer.subsystem == k_player_subsystem_glide ) target = 1.0f; + else target = 0.0f; + + /* TODO: TEMP */ + if( skaterift.activity != k_skaterift_replay ) + vg_slewf( &player_glide.t, target, vg.time_frame_delta * 4.0f ); + + mdl_keyframe kf_backpack; + + struct skeleton *sk = &localplayer.skeleton; + m4x3_mulv( localplayer.final_mtx[localplayer.id_chest ], + sk->bones[localplayer.id_chest].co, + kf_backpack.co ); + + v4f qyaw, qpitch, qchest, q; + q_axis_angle( qyaw, (v3f){0,1,0}, VG_TAUf*0.25f ); + q_axis_angle( qpitch, (v3f){1,0,0}, VG_TAUf*0.25f ); + m3x3_q( localplayer.final_mtx[ localplayer.id_chest ], qchest ); + + q_mul( qyaw, qpitch, q ); + q_mul( qchest, q, kf_backpack.q ); + q_normalize( kf_backpack.q ); + + f32 scale; + if( player_glide.t <= 0.0f ){ + f32 st = player_glide.t + 1.0f, + sst = vg_smoothstepf(st), + isst= 1.0f - sst; + scale = vg_lerpf( 0.0f, 0.2f, sst ); + + v4f qspin; + q_axis_angle( qspin, (v3f){0,0,1}, VG_TAUf * isst * 0.5f ); + q_mul( kf_backpack.q, qspin, kf_backpack.q ); + kf_backpack.co[1] += isst * 1.0f; + v3_muladds( kf_backpack.co, + localplayer.final_mtx[ localplayer.id_chest ][0], + isst * 0.25f, + kf_backpack.co ); + } + else{ + scale = vg_lerpf( 0.2f, 1.0f, vg_smoothstepf(player_glide.t) ); + } + + + v3_fill( kf_backpack.s, scale ); + + v3_copy( pose->root_co, kf_res.co ); + v4_copy( pose->root_q, kf_res.q ); + v3_fill( kf_res.s, scale ); + + f32 blend = vg_smoothstepf( vg_maxf( 0, player_glide.t ) ); + keyframe_lerp( &kf_backpack, &kf_res, blend, &kf_res ); + } + + m4x3f mmdl; + q_m3x3( kf_res.q, mmdl ); + m3x3_scale( mmdl, kf_res.s ); + v3_copy( kf_res.co, mmdl[3] ); + + render_glider_model( cam, world, mmdl, k_board_shader_player ); + + /* totally FUCKED */ + v4_copy( kf_res.q, player_glide.remote_animator.root_q ); + v3_copy( kf_res.co, player_glide.remote_animator.root_co ); + player_glide.remote_animator.s = kf_res.s[0]; +} + +static void player_glide_render_effects( camera *cam ){ + v3f co, temp; + v4f q; + rb_extrapolate( &player_glide.rb, co, q ); + q_mulv( q, (v3f){0,-0.5f,0}, temp ); + v3_add( temp, co, co ); + + f32 alpha = vg_maxf( (fabsf(player_glide.info_lift[2])-1.0f), 0.0f ) /18.0f; + + for( u32 i=0; icollider_mtx ); + rp->type = bone->collider; if( bone->collider == k_bone_collider_box ){ v3f delta; v3_sub( bone->hitbox[1], bone->hitbox[0], delta ); v3_muls( delta, 0.5f, delta ); v3_add( bone->hitbox[0], delta, rp->collider_mtx[3] ); - v3_copy( delta, rp->obj.rb.bbx[1] ); - v3_muls( delta, -1.0f, rp->obj.rb.bbx[0] ); + v3_muls( delta, -1.0f, rp->inf.box[0] ); + v3_copy( delta, rp->inf.box[1] ); - q_identity( rp->obj.rb.q ); - rp->obj.type = k_rb_shape_box; rp->colour = 0xffcccccc; + + rb_setbody_box( &rp->rb, rp->inf.box, k_density, k_inertia_scale ); } else if( bone->collider == k_bone_collider_capsule ){ v3f v0, v1, tx, ty; @@ -88,9 +95,9 @@ static void player_init_ragdoll_bone_collider( struct skeleton_bone *bone, v1[ major_axis ] = 1.0f; v3_tangent_basis( v1, tx, ty ); - float r = (fabsf(v3_dot(tx,v0)) + fabsf(v3_dot(ty,v0))) * 0.25f, - l = fabsf(v0[ major_axis ]); - + rp->inf.capsule.r = (fabsf(v3_dot(tx,v0)) + fabsf(v3_dot(ty,v0))) * 0.25f; + rp->inf.capsule.h = fabsf(v0[ major_axis ]); + /* orientation */ v3_muls( tx, -1.0f, rp->collider_mtx[0] ); v3_muls( v1, -1.0f, rp->collider_mtx[1] ); @@ -98,11 +105,10 @@ static void player_init_ragdoll_bone_collider( struct skeleton_bone *bone, v3_add( bone->hitbox[0], bone->hitbox[1], rp->collider_mtx[3] ); v3_muls( rp->collider_mtx[3], 0.5f, rp->collider_mtx[3] ); - rp->obj.type = k_rb_shape_capsule; - rp->obj.inf.capsule.height = l; - rp->obj.inf.capsule.radius = r; - rp->colour = 0xff000000 | (0xff << (major_axis*8)); + + rb_setbody_capsule( &rp->rb, rp->inf.capsule.r, rp->inf.capsule.h, + k_density, k_inertia_scale ); } else{ vg_warn( "type: %u\n", bone->collider ); @@ -112,11 +118,11 @@ static void player_init_ragdoll_bone_collider( struct skeleton_bone *bone, m4x3_invert_affine( rp->collider_mtx, rp->inv_collider_mtx ); /* Position collider into rest */ - m3x3_q( rp->collider_mtx, rp->obj.rb.q ); - v3_add( rp->collider_mtx[3], bone->co, rp->obj.rb.co ); - v3_zero( rp->obj.rb.v ); - v3_zero( rp->obj.rb.w ); - rb_init_object( &rp->obj ); + m3x3_q( rp->collider_mtx, rp->rb.q ); + v3_add( rp->collider_mtx[3], bone->co, rp->rb.co ); + v3_zero( rp->rb.v ); + v3_zero( rp->rb.w ); + rb_update_matrices( &rp->rb ); } /* @@ -187,8 +193,8 @@ static void setup_ragdoll_from_skeleton( struct skeleton *sk, rd->constraint_associations[conid][1] = part_id; /* Convention: rba -- parent, rbb -- child */ - c->rba = &pp->obj.rb; - c->rbb = &rp->obj.rb; + c->rba = &pp->rb; + c->rbb = &rp->rb; v3f delta; v3_sub( bj->co, bp->co, delta ); @@ -201,8 +207,8 @@ static void setup_ragdoll_from_skeleton( struct skeleton *sk, struct rb_constr_swingtwist *a = &rd->cone_constraints[ rd->cone_constraints_count ++ ]; - a->rba = &pp->obj.rb; - a->rbb = &rp->obj.rb; + a->rba = &pp->rb; + a->rbb = &rp->rb; a->conet = cosf( inf->conet )-0.0001f; /* Store constraint in local space vectors */ @@ -240,8 +246,8 @@ static void copy_ragdoll_pose_to_localplayer( struct player_ragdoll *rd ){ v3f co_int; float substep = vg.time_fixed_extrapolate; - v3_lerp( part->prev_co, part->obj.rb.co, substep, co_int ); - q_nlerp( part->prev_q, part->obj.rb.q, substep, q_int ); + v3_lerp( part->prev_co, part->rb.co, substep, co_int ); + q_nlerp( part->prev_q, part->rb.q, substep, q_int ); q_m3x3( q_int, mtx ); v3_copy( co_int, mtx[3] ); @@ -292,30 +298,30 @@ static void copy_localplayer_to_ragdoll( struct player_ragdoll *rd, m4x3_mulv( bone_mtx, localplayer.skeleton.bones[bone].co, pos ); m3x3_mulv( bone_mtx, part->collider_mtx[3], offset ); - v3_add( pos, offset, part->obj.rb.co ); + v3_add( pos, offset, part->rb.co ); m3x3f r; m3x3_mul( bone_mtx, part->collider_mtx, r ); - m3x3_q( r, part->obj.rb.q ); + m3x3_q( r, part->rb.q ); v3f ra, v; - v3_sub( part->obj.rb.co, centroid, ra ); + v3_sub( part->rb.co, centroid, ra ); v3_cross( localplayer.rb.w, ra, v ); - v3_add( localplayer.rb.v, v, part->obj.rb.v ); + v3_add( localplayer.rb.v, v, part->rb.v ); if( type == k_player_die_type_feet ){ if( (bone == localplayer.id_foot_l) || (bone == localplayer.id_foot_r) ){ - v3_zero( part->obj.rb.v ); + v3_zero( part->rb.v ); } } - v3_copy( localplayer.rb.w, part->obj.rb.w ); + v3_copy( localplayer.rb.w, part->rb.w ); - v3_copy( part->obj.rb.co, part->prev_co ); - v4_copy( part->obj.rb.q, part->prev_q ); + v3_copy( part->rb.co, part->prev_co ); + v4_copy( part->rb.q, part->prev_q ); - rb_update_transform( &part->obj.rb ); + rb_update_matrices( &part->rb ); } } @@ -337,32 +343,36 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ float contact_velocities[256]; + rigidbody _null = {0}; + _null.inv_mass = 0.0f; + m3x3_zero( _null.iI ); + for( int i=0; ipart_count; i ++ ){ - v4_copy( rd->parts[i].obj.rb.q, rd->parts[i].prev_q ); - v3_copy( rd->parts[i].obj.rb.co, rd->parts[i].prev_co ); + v4_copy( rd->parts[i].rb.q, rd->parts[i].prev_q ); + v3_copy( rd->parts[i].rb.co, rd->parts[i].prev_co ); if( rb_global_has_space() ){ rb_ct *buf = rb_global_buffer(); int l; - if( rd->parts[i].obj.type == k_rb_shape_capsule ){ - l = rb_capsule__scene( rd->parts[i].obj.rb.to_world, - &rd->parts[i].obj.inf.capsule, - NULL, &world->rb_geo.inf.scene, buf, + if( rd->parts[i].type == k_bone_collider_capsule ){ + l = rb_capsule__scene( rd->parts[i].rb.to_world, + &rd->parts[i].inf.capsule, + NULL, world->geo_bh, buf, k_material_flag_ghosts ); } - else if( rd->parts[i].obj.type == k_rb_shape_box ){ - l = rb_box__scene( rd->parts[i].obj.rb.to_world, - rd->parts[i].obj.rb.bbx, - NULL, &world->rb_geo.inf.scene, buf, + else if( rd->parts[i].type == k_bone_collider_box ){ + l = rb_box__scene( rd->parts[i].rb.to_world, + rd->parts[i].inf.box, + NULL, world->geo_bh, buf, k_material_flag_ghosts ); } else continue; for( int j=0; jparts[i].obj.rb; - buf[j].rbb = &world->rb_geo.rb; + buf[j].rba = &rd->parts[i].rb; + buf[j].rbb = &_null; } rb_contact_count += l; @@ -378,23 +388,23 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ if( !rb_global_has_space() ) break; - if( rd->parts[j].obj.type != k_rb_shape_capsule ) + if( rd->parts[j].type != k_bone_collider_capsule ) continue; - if( rd->parts[i].obj.type != k_rb_shape_capsule ) + if( rd->parts[i].type != k_bone_collider_capsule ) continue; rb_ct *buf = rb_global_buffer(); - int l = rb_capsule__capsule( rd->parts[i].obj.rb.to_world, - &rd->parts[i].obj.inf.capsule, - rd->parts[j].obj.rb.to_world, - &rd->parts[j].obj.inf.capsule, + int l = rb_capsule__capsule( rd->parts[i].rb.to_world, + &rd->parts[i].inf.capsule, + rd->parts[j].rb.to_world, + &rd->parts[j].inf.capsule, buf ); for( int k=0; kparts[i].obj.rb; - buf[k].rbb = &rd->parts[j].obj.rb; + buf[k].rba = &rd->parts[i].rb; + buf[k].rbb = &rd->parts[j].rb; } rb_contact_count += l; @@ -407,7 +417,7 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ struct ragdoll_part *pj = &rd->parts[j]; if( run_sim ){ - rb_effect_simple_bouyency( &pj->obj.rb, world->water.plane, + rb_effect_simple_bouyency( &pj->rb, world->water.plane, k_ragdoll_floatyiness, k_ragdoll_floatydrag ); } @@ -437,8 +447,18 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ * DEBUG */ if( k_ragdoll_debug_collider ){ - for( u32 i=0; ipart_count; i ++ ) - rb_object_debug( &rd->parts[i].obj, rd->parts[i].colour ); + for( u32 i=0; ipart_count; i ++ ){ + struct ragdoll_part *rp = &rd->parts[i]; + + if( rp->type == k_bone_collider_capsule ){ + vg_line_capsule( rp->rb.to_world, + rp->inf.capsule.r, rp->inf.capsule.h, rp->colour ); + } + else if( rp->type == k_bone_collider_box ){ + vg_line_boxf_transformed( rp->rb.to_world, + rp->inf.box, rp->colour ); + } + } } if( k_ragdoll_debug_constraints ){ @@ -453,38 +473,40 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ * SOLVE CONSTRAINTS & Integrate */ if( run_sim ){ - for( int i=0; i<12; i++ ){ - rb_solve_contacts( rb_contact_buffer, rb_contact_count ); - rb_solve_swingtwist_constraints( rd->cone_constraints, - rd->cone_constraints_count ); + /* the solver is not very quickly converging so... */ + for( int i=0; i<40; i++ ){ + if( i<20 ){ + rb_solve_contacts( rb_contact_buffer, rb_contact_count ); + rb_solve_swingtwist_constraints( rd->cone_constraints, + rd->cone_constraints_count ); + rb_postsolve_swingtwist_constraints( rd->cone_constraints, + rd->cone_constraints_count ); + } rb_solve_position_constraints( rd->position_constraints, rd->position_constraints_count ); } + rb_correct_position_constraints( rd->position_constraints, + rd->position_constraints_count, + k_ragdoll_correction * 0.5f ); + rb_correct_swingtwist_constraints( rd->cone_constraints, + rd->cone_constraints_count, + k_ragdoll_correction * 0.25f ); + for( int i=0; ipart_count; i++ ){ - rb_iter( &rd->parts[i].obj.rb ); + rb_iter( &rd->parts[i].rb ); v3f w; - v3_copy( rd->parts[i].obj.rb.w, w ); + v3_copy( rd->parts[i].rb.w, w ); if( v3_length2( w ) > 0.00001f ){ v3_normalize( w ); - v3_muladds( rd->parts[i].obj.rb.w, w, -k_ragdoll_angular_drag, - rd->parts[i].obj.rb.w ); + v3_muladds( rd->parts[i].rb.w, w, -k_ragdoll_angular_drag, + rd->parts[i].rb.w ); } } for( int i=0; ipart_count; i++ ) - rb_update_transform( &rd->parts[i].obj.rb ); - - for( int i=0; i<5; i ++ ){ - rb_correct_swingtwist_constraints( rd->cone_constraints, - rd->cone_constraints_count, - k_ragdoll_correction * 0.25f ); - - rb_correct_position_constraints( rd->position_constraints, - rd->position_constraints_count, - k_ragdoll_correction * 0.5f ); - } + rb_update_matrices( &rd->parts[i].rb ); } rb_ct *stress = NULL; @@ -583,5 +605,3 @@ static void player_ragdoll_iter( struct player_ragdoll *rd ){ audio_unlock(); } } - -#endif /* PLAYER_RAGDOLL_C */ diff --git a/player_ragdoll.h b/player_ragdoll.h index 6f5a331..25b6fad 100644 --- a/player_ragdoll.h +++ b/player_ragdoll.h @@ -1,9 +1,15 @@ -#ifndef PLAYER_RAGDOLL_H -#define PLAYER_RAGDOLL_H +#pragma once + +/* + * Copyright (C) 2021-2024 Mt.ZERO Software - All Rights Reserved + * + * Ragdoll system + */ #include "player_api.h" #include "skeleton.h" -#include "rigidbody.h" +#include "vg/vg_rigidbody.h" +#include "vg/vg_rigidbody_constraints.h" #include "player_render.h" struct player_ragdoll{ @@ -20,9 +26,17 @@ struct player_ragdoll{ u32 use_limits; v3f limits[2]; - rb_object obj; u32 parent; u32 colour; + + rigidbody rb; + enum bone_collider type; + + union { + rb_capsule capsule; + boxf box; + } + inf; } parts[32]; u32 part_count; @@ -70,5 +84,3 @@ static void copy_localplayer_to_ragdoll( struct player_ragdoll *rd, static void player_debug_ragdoll(void); static void player_ragdoll_iter( struct player_ragdoll *rd ); - -#endif /* PLAYER_RAGDOLL_H */ diff --git a/player_remote.c b/player_remote.c index e910170..ba07abe 100644 --- a/player_remote.c +++ b/player_remote.c @@ -7,6 +7,7 @@ #include "gui.h" #include "ent_miniworld.h" #include "ent_region.h" +#include "shaders/model_entity.h" static i32 k_show_own_name = 0; @@ -169,7 +170,7 @@ static void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ){ dest->active = 1; dest->subsystem = frame->subsystem; - dest->instance_id = frame->instance_id; + dest->flags = frame->flags; bitpack_ctx ctx = { .mode = k_bitpack_decompress, @@ -188,13 +189,11 @@ static void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ){ struct player_subsystem_interface *sys = player_subsystems[ frame->subsystem ]; - if( sys->network_animator_exchange ){ - memset( &dest->data, 0, sys->animator_size ); + memset( &dest->data, 0, sys->animator_size ); + if( sys->network_animator_exchange ) sys->network_animator_exchange( &ctx, &dest->data ); - } - else { + else bitpack_bytes( &ctx, sys->animator_size, sys->animator_data ); - } /* sfx * -------------------------------------------------------------*/ @@ -229,6 +228,15 @@ static void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ){ } } + /* glider + * -------------------------------------------------------------*/ + + memset( &dest->data_glider, 0, sizeof(struct remote_glider_animator) ); + if( dest->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER| + NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){ + player_glide_remote_animator_exchange( &ctx, &dest->data_glider ); + } + player->subsystem = frame->subsystem; player->down_bytes += msg->m_cbSize; } @@ -320,7 +328,7 @@ static void remote_player_send_playerframe(void){ frame->inetmsg_id = k_inetmsg_playerframe; frame->client = 0xff; frame->subsystem = localplayer.subsystem; - frame->instance_id = world_static.active_instance; + frame->flags = world_static.active_instance; bitpack_ctx ctx = { .mode = k_bitpack_compress, @@ -346,6 +354,25 @@ static void remote_player_send_playerframe(void){ for( u32 i=0; iflags |= NETMSG_PLAYERFRAME_HAVE_GLIDER; + } + + if( localplayer.glider_orphan ) + frame->flags |= NETMSG_PLAYERFRAME_GLIDER_ORPHAN; + + if( frame->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER| + NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){ + player_glide_remote_animator_exchange( &ctx, + &player_glide.remote_animator ); + } + + /* ------- */ + u32 wire_size = base_size + ctx.bytes; netplayers.up_bytes += wire_size; @@ -490,9 +517,11 @@ static void pose_remote_player( u32 index, u8 instance_id = 0; + f32 t = 0.0f; + if( f1 ){ - f32 t = (buf->t - f0->timestamp) / (f1->timestamp - f0->timestamp); - t = vg_clampf( t, 0.0f, 1.0f ); + t = (buf->t - f0->timestamp) / (f1->timestamp - f0->timestamp); + t = vg_clampf( t, 0.0f, 1.0f ); sys1 = player_subsystems[f1->subsystem]; sys1->pose( &f1->data, &pose1 ); @@ -507,7 +536,7 @@ static void pose_remote_player( u32 index, t = 1.0f; } - instance_id = f1->instance_id; + instance_id = f1->flags & NETMSG_PLAYERFRAME_INSTANCE_ID; lerp_player_pose( &pose0, &pose1, t, &posed ); effect_blink_apply( &player->effect_data.blink, &posed, vg.time_delta ); @@ -525,7 +554,7 @@ static void pose_remote_player( u32 index, memcpy( board_pose, &posed.board, sizeof(*board_pose) ); } else { - instance_id = f0->instance_id; + instance_id = f0->flags & NETMSG_PLAYERFRAME_INSTANCE_ID; effect_blink_apply( &player->effect_data.blink, &pose0, vg.time_delta ); apply_full_skeleton_pose( sk, &pose0, final_mtx ); if( sys0->effects ) @@ -533,6 +562,33 @@ static void pose_remote_player( u32 index, memcpy( board_pose, &pose0.board, sizeof(*board_pose) ); } + if( f0->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER| + NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){ + player->render_glider = 1; + + v3f co; + v4f q; + f32 s; + + if( f1 ){ + v3_lerp( f0->data_glider.root_co, f1->data_glider.root_co, t, co ); + q_nlerp( f0->data_glider.root_q, f1->data_glider.root_q, t, q ); + s = vg_lerpf( f0->data_glider.s, f1->data_glider.s, t ); + } + else { + v3_copy( f0->data_glider.root_co, co ); + v4_copy( f0->data_glider.root_q, q ); + s = f0->data_glider.s; + } + + v3f *mtx = netplayers.glider_mtx[ index ]; + q_m3x3( q, mtx ); + m3x3_scalef( mtx, s ); + v3_copy( co, mtx[3] ); + } + else + player->render_glider = 0; + if( player->world_match[ instance_id ] ) player->active_world = &world_static.instances[ instance_id ]; } @@ -609,8 +665,9 @@ static void animate_remote_players(void){ * Draw remote players */ static void render_remote_players( world_instance *world, camera *cam ){ - SDL_AtomicLock( &addon_system.sl_cache_using_resources ); - struct skeleton *sk = &localplayer.skeleton; + u32 draw_list[ NETWORK_MAX_PLAYERS ], + draw_list_count = 0, + gliders = 0; for( u32 i=0; iactive_world != world ) continue; if( !player->isfriend && (world-world_static.instances == k_world_purpose_hub)) continue; - - m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_count*i ]; + + draw_list[draw_list_count ++] = i; + + if( player->render_glider ) + gliders ++; + } + + struct skeleton *sk = &localplayer.skeleton; + + SDL_AtomicLock( &addon_system.sl_cache_using_resources ); + + for( u32 j=0; jbone_count*index ]; struct player_model *model = addon_cache_item_if_loaded( k_addon_type_player, @@ -632,10 +703,36 @@ static void render_remote_players( world_instance *world, camera *cam ){ addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); render_board( cam, world, board, final_mtx[localplayer.id_board], - &netplayers.board_poses[ i ], k_board_shader_player ); + &netplayers.board_poses[ index ], k_board_shader_player ); } SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + + if( !gliders ) + return; + + /* TODO: we really, really only need to do all this once. at some point + * PLEASE figure out a good place to do this once per frame! + */ + + 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; jrender_glider ) continue; + + if( player->render_glider ){ + v3f *glider_mtx = netplayers.glider_mtx[ index ]; + render_glider_model( cam, world, glider_mtx, k_board_shader_entity ); + } + } } static int remote_players_randomize( int argc, const char *argv[] ){ diff --git a/player_remote.h b/player_remote.h index 265409c..2d6200b 100644 --- a/player_remote.h +++ b/player_remote.h @@ -36,6 +36,7 @@ struct { f32 down_kbs; struct player_effects_data effect_data; + bool render_glider; } list[ NETWORK_MAX_PLAYERS ]; @@ -46,7 +47,7 @@ struct { f64 timestamp; enum player_subsystem subsystem; - u8 instance_id; + u8 flags; u16 boundary_hash; union interp_animdata { @@ -58,6 +59,8 @@ struct { struct player_basic_info_animator __basic; } data; + + struct remote_glider_animator data_glider; } frames[ NETWORK_BUFFERFRAMES ]; @@ -67,7 +70,8 @@ struct { struct net_sfx sfx_queue[ NETWORK_SFX_QUEUE_LENGTH ]; - m4x3f *final_mtx; + m4x3f *final_mtx, + *glider_mtx; struct player_board_pose board_poses[ NETWORK_MAX_PLAYERS ]; u32 up_bytes; diff --git a/player_render.c b/player_render.c index 1ade3d7..40c2f9a 100644 --- a/player_render.c +++ b/player_render.c @@ -17,6 +17,7 @@ #include "network.h" #include "player_remote.h" +#include "player_glide.h" static void player_load_animation_reference( const char *path ){ mdl_context *meta = &localplayer.skeleton_meta; @@ -83,6 +84,8 @@ static void player_load_animation_reference( const char *path ){ localplayer.final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size ); netplayers.final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size*NETWORK_MAX_PLAYERS ); + netplayers.glider_mtx = vg_linear_alloc( vg_mem.rtmemory, + sizeof(m4x3f)*NETWORK_MAX_PLAYERS ); } /* TODO: Standard model load */ @@ -331,8 +334,14 @@ static void player__animate_from_replay( replay_buffer *replay ){ *sys0 = player_subsystems[frame->system]; void *a0 = replay_frame_data( frame, k_replay_framedata_animator ); + struct replay_glider_data + *g0 = replay_frame_data( frame, k_replay_framedata_glider ), + *g1; + + f32 t = 0.0f; + if( next ){ - f32 t = replay_subframe_time( replay ); + t = replay_subframe_time( replay ); player_pose pose0, pose1; @@ -344,14 +353,44 @@ static void player__animate_from_replay( replay_buffer *replay ){ sys1->pose( a1, &pose1 ); lerp_player_pose( &pose0, &pose1, t, &localplayer.pose ); + g1 = replay_frame_data( next, k_replay_framedata_glider ); } else{ sys0->pose( a0, &localplayer.pose ); + g1 = NULL; } player__observe_system( frame->system ); if( sys0->sfx_comp ) sys0->sfx_comp( a0 ); + + if( g0 ){ + if( g0->glider_orphan ){ + if( g1 ){ + v3_lerp( g0->co, g1->co, t, player_glide.rb.co ); + q_nlerp( g0->q, g1->q, t, player_glide.rb.q ); + } + else { + v3_copy( g0->co, player_glide.rb.co ); + v4_copy( g0->q, player_glide.rb.q ); + } + + rb_update_matrices( &player_glide.rb ); + } + + if( g1 ) + player_glide.t = vg_lerpf( g0->t, g1->t, t ); + else + player_glide.t = g0->t; + + localplayer.have_glider = g0->have_glider; + localplayer.glider_orphan = g0->glider_orphan; + } + else /* no glider data in g1, or edge case we dont care about */ { + localplayer.have_glider = 0; + localplayer.glider_orphan = 0; + player_glide.t = 0.0f; + } } else return; @@ -555,6 +594,10 @@ static void player__render( camera *cam ){ &localplayer.pose.board, k_board_shader_player ); SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + + glEnable( GL_CULL_FACE ); + player_glide_render( cam, world, &localplayer.pose ); + glDisable( GL_CULL_FACE ); } static void player_mirror_pose( mdl_keyframe pose[32], diff --git a/player_replay.c b/player_replay.c index 586b733..f5a2ab9 100644 --- a/player_replay.c +++ b/player_replay.c @@ -16,14 +16,18 @@ static void replay_clear( replay_buffer *replay ){ static void * replay_frame_data( replay_frame *frame, enum replay_framedata type ){ + if( frame->data_table[type][1] == 0 ) + return NULL; + void *baseptr = frame; return baseptr + frame->data_table[type][0]; } -static u16 -replay_frame_calculate_data_offsets( u16 data_table[4][2] ){ +static u16 replay_frame_calculate_data_offsets( + u16 data_table[k_replay_framedata_rows][2] ){ + u32 total = vg_align8( sizeof(replay_frame) ); - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; idata; - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; idata_table[i][0] = data_table[i][0]; frame->data_table[i][1] = data_table[i][1]; } @@ -256,13 +267,19 @@ static void skaterift_record_frame( replay_buffer *replay, k_gamestate_rate = 0.5; int save_frame = 0, - save_state = 0; + save_state = 0, + save_glider = 0; if( force_gamestate ) save_state = 1; if( statedelta > k_gamestate_rate ) save_state = 1; if( delta > k_replay_rate ) save_frame = 1; if( save_state ) save_frame = 1; + if( localplayer.have_glider || localplayer.glider_orphan || + localplayer.subsystem == k_player_subsystem_glide ){ + save_glider = 1; + } + if( !save_frame ) return; u16 gamestate_size = 0; @@ -274,6 +291,7 @@ static void skaterift_record_frame( replay_buffer *replay, [k_player_subsystem_skate] = sizeof(struct player_skate_state), [k_player_subsystem_dead ] = localplayer.ragdoll.part_count * sizeof(struct replay_rb), + [k_player_subsystem_glide] = sizeof(struct replay_rb), }[ localplayer.subsystem ]; } @@ -281,7 +299,8 @@ static void skaterift_record_frame( replay_buffer *replay, replay_frame *frame = replay_newframe( replay, animator_size, gamestate_size, - localplayer.local_sfx_buffer_count ); + localplayer.local_sfx_buffer_count, + save_glider ); frame->system = localplayer.subsystem; if( save_state ){ @@ -290,6 +309,7 @@ static void skaterift_record_frame( replay_buffer *replay, /* permanent block */ memcpy( &gs->rb, &localplayer.rb, sizeof(rigidbody) ); + memcpy( &gs->glider_rb, &player_glide.rb, sizeof(rigidbody) ); memcpy( &gs->cam_control, &localplayer.cam_control, sizeof(struct player_cam_controller) ); v3_copy( localplayer.angles, gs->angles ); @@ -304,13 +324,32 @@ static void skaterift_record_frame( replay_buffer *replay, else if( localplayer.subsystem == k_player_subsystem_dead ){ struct replay_rb *arr = dst; for( u32 i=0; ico, arr[i].co ); v3_copy( rb->w, arr[i].w ); v3_copy( rb->v, arr[i].v ); v4_copy( rb->q, arr[i].q ); } } + else if( localplayer.subsystem == k_player_subsystem_glide ){ + struct replay_rb *arr = dst; + rigidbody *rb = &player_glide.rb; + v3_copy( rb->co, arr[0].co ); + v3_copy( rb->w, arr[0].w ); + v3_copy( rb->v, arr[0].v ); + v4_copy( rb->q, arr[0].q ); + } + } + + if( save_glider ){ + struct replay_glider_data *inf = + replay_frame_data( frame, k_replay_framedata_glider ); + + inf->have_glider = localplayer.have_glider; + inf->glider_orphan = localplayer.glider_orphan; + inf->t = player_glide.t; + v3_copy( player_glide.rb.co, inf->co ); + v4_copy( player_glide.rb.q, inf->q ); } replay->cursor = vg.time; @@ -334,15 +373,9 @@ static void skaterift_record_frame( replay_buffer *replay, frame->cam_fov = localplayer.cam.fov; /* animator */ - void *dst = replay_frame_data( frame, k_replay_framedata_animator ); - - if( localplayer.subsystem == k_player_subsystem_walk ) - memcpy( dst, &player_walk.animator, animator_size ); - else if( localplayer.subsystem == k_player_subsystem_skate ) - memcpy( dst, &player_skate.animator, animator_size ); - else if( localplayer.subsystem == k_player_subsystem_dead ){ - memcpy( dst, &player_dead.animator, animator_size ); - } + void *dst = replay_frame_data( frame, k_replay_framedata_animator ), + *src = player_subsystems[localplayer.subsystem]->animator_data; + memcpy( dst, src, animator_size ); /* sound effects */ memcpy( replay_frame_data( frame, k_replay_framedata_sfx ), @@ -372,7 +405,7 @@ void skaterift_restore_frame( replay_frame *frame ){ for( u32 i=0; iobj.rb; + rigidbody *rb = &part->rb; v3_copy( arr[i].co, rb->co ); v3_copy( arr[i].w, rb->w ); @@ -381,12 +414,38 @@ void skaterift_restore_frame( replay_frame *frame ){ v3_copy( arr[i].co, part->prev_co ); v4_copy( arr[i].q, part->prev_q ); + rb_update_matrices( rb ); } } + else if( frame->system == k_player_subsystem_glide ){ + struct replay_rb *arr = src; + rigidbody *rb = &player_glide.rb; + v3_copy( arr[0].co, rb->co ); + v3_copy( arr[0].w, rb->w ); + v3_copy( arr[0].v, rb->v ); + v4_copy( arr[0].q, rb->q ); + rb_update_matrices( rb ); + } localplayer.subsystem = frame->system; + /* restore the seperated glider data if we have it */ + if( frame->data_table[ k_replay_framedata_glider ][1] ){ + struct replay_glider_data *inf = + replay_frame_data( frame, k_replay_framedata_glider ); + + localplayer.have_glider = inf->have_glider; + localplayer.glider_orphan = inf->glider_orphan; + player_glide.t = inf->t; + } + else { + localplayer.have_glider = 0; + localplayer.glider_orphan = 0; + player_glide.t = 0.0f; + } + memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) ); + memcpy( &player_glide.rb, &gs->glider_rb, sizeof(rigidbody) ); v3_copy( gs->angles, localplayer.angles ); v3_copy( frame->cam_pos, localplayer.cam.pos ); @@ -510,8 +569,10 @@ static void skaterift_replay_update_helpers(void){ } static void skaterift_replay_post_render(void){ +#ifndef SR_ALLOW_REWIND_HUB if( world_static.active_instance != k_world_purpose_client ) return; +#endif /* capture the current resume frame at the very last point */ if( button_down( k_srbind_reset ) ){ @@ -546,21 +607,6 @@ static void skaterift_replay_post_render(void){ } } -#if 0 -static void skaterift_get_replay_camera( camera *cam ){ - if( skaterift.freecam ){ - cam->nearz = 0.1f; - cam->farz = 100.0f; - v3_copy( skaterift.replay_freecam.pos, cam->pos ); - v3_copy( skaterift.replay_freecam.angles, cam->angles ); - cam->fov = skaterift.replay_freecam.fov; - } - else{ - replay_get_camera( &skaterift.replay, &skaterift.cam ); - } -} -#endif - static void skaterift_replay_debug_info(void){ player__debugtext( 2, "replay info" ); @@ -574,7 +620,7 @@ static void skaterift_replay_debug_info(void){ player__debugtext( 1, "head @%u | tail @%u\n", head, tail ); if( replay->statehead ){ - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; istatehead->data_table[i][0], replay->statehead->data_table[i][1] ); diff --git a/player_replay.h b/player_replay.h index 38599d4..42d9bf7 100644 --- a/player_replay.h +++ b/player_replay.h @@ -23,7 +23,9 @@ enum replay_framedata{ k_replay_framedata_animator, k_replay_framedata_gamestate, k_replay_framedata_internal_gamestate, - k_replay_framedata_sfx + k_replay_framedata_sfx, + k_replay_framedata_glider, + k_replay_framedata_rows }; struct replay_frame { @@ -35,23 +37,28 @@ struct replay_frame { enum player_subsystem system; u16 total_size; - u16 data_table[4][2]; + u16 data_table[k_replay_framedata_rows][2]; }; struct replay_gamestate { - rigidbody rb; + rigidbody rb, glider_rb; /* TODO: these don't need to be saved with their + full matrices */ v3f angles; struct player_cam_controller cam_control; }; +/* we save this per-anim-frame. if there glider is existing in any state */ +struct replay_glider_data { + bool have_glider, glider_orphan; + f32 t; + v3f co; + v4f q; +}; + struct replay_sfx { u32 none; }; -static replay_frame *replay_newframe( replay_buffer *replay, - u16 animator_size, - u16 gamestate_size, - u16 sfx_count ); static int replay_seek( replay_buffer *replay, f64 t ); static replay_frame *replay_find_recent_stateframe( replay_buffer *replay ); diff --git a/player_skate.c b/player_skate.c index 9356aa6..cbf43ec 100644 --- a/player_skate.c +++ b/player_skate.c @@ -9,10 +9,13 @@ #include "addon.h" #include "ent_tornado.c" +#include "vg/vg_rigidbody.h" +#include "scene_rigidbody.h" +#include "player_glide.h" static void player__skate_bind(void){ struct skeleton *sk = &localplayer.skeleton; - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); struct { struct skeleton_anim **anim; const char *name; } bindings[] = { @@ -61,11 +64,11 @@ static void player__skate_kill_audio(void){ * Does collision detection on a sphere vs world, and applies some smoothing * filters to the manifold afterwards */ -static int skate_collide_smooth( m4x3f mtx, rb_sphere *sphere, rb_ct *man ){ +static int skate_collide_smooth( m4x3f mtx, f32 r, rb_ct *man ){ world_instance *world = world_current_instance(); int len = 0; - len = rb_sphere__scene( mtx, sphere, NULL, &world->rb_geo.inf.scene, man, + len = rb_sphere__scene( mtx, r, NULL, world->geo_bh, man, k_material_flag_walking ); for( int i=0; itrick_vel) >= 0.0001f ) && state->trick_time > 0.2f) { + vg_info( "player fell off due to lack of skill\n" ); player__dead_transition( k_player_die_type_feet ); } @@ -1146,7 +1150,7 @@ static void skate_integrate(void){ localplayer.rb.w ); state->flip_time += state->flip_rate * k_rb_delta; - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); } static enum trick_type player_skate_trick_input(void){ @@ -1209,7 +1213,7 @@ static void player__skate_pre_update(void){ m3x3_q( transfer, qtransfer ); q_mul( qtransfer, state->store_smoothed, state->smoothed_rotation ); q_normalize( state->smoothed_rotation ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); if( end ){ state->activity = k_skate_activity_air; @@ -1219,6 +1223,13 @@ static void player__skate_pre_update(void){ if( button_down(k_srbind_use) && (v3_length2(state->trick_vel) < 0.01f) ){ localplayer.subsystem = k_player_subsystem_walk; + + if( (state->activity <= k_skate_activity_air_to_grind) && + localplayer.have_glider ){ + player_glide_transition(); + return; + } + v3f angles; v3_copy( localplayer.cam.angles, localplayer.angles ); localplayer.angles[2] = 0.0f; @@ -2198,6 +2209,7 @@ static void player__skate_update(void){ if( world->water.enabled ){ if( localplayer.rb.co[1]+0.25f < world->water.height ){ + vg_info( "player fell off due to being in water\n" ); player__networked_sfx( k_player_subsystem_walk, 32, k_player_walk_soundeffect_splash, localplayer.rb.co, 1.0f ); @@ -2459,7 +2471,7 @@ begin_collision:; v3_add( localplayer.rb.co, cg_offset, localplayer.rb.co ); } - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); localplayer.rb.v[1] += -state->gravity_bias * player_skate.substep_delta; player_skate.substep -= player_skate.substep_delta; @@ -2479,8 +2491,9 @@ begin_collision:; k_material_flag_walking ) != -1) ) { v3_lerp( start_co, localplayer.rb.co, t, localplayer.rb.co ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); + vg_info( "player fell of due to hitting head\n" ); player__dead_transition( k_player_die_type_head ); return; } @@ -2497,12 +2510,10 @@ begin_collision:; m4x3f mtx; m3x3_identity( mtx ); m4x3_mulv( localplayer.rb.to_world, wheels[i].pos, mtx[3] ); - - rb_sphere collider = { .radius = wheels[i].radius }; rb_ct *man = &manifold[ manifold_len ]; - int l = skate_collide_smooth( mtx, &collider, man ); + int l = skate_collide_smooth( mtx, wheels[i].radius, man ); if( l ) wheels[i].state = k_collider_state_colliding; @@ -2510,8 +2521,8 @@ begin_collision:; } float grind_radius = k_board_radius * 0.75f; - rb_capsule capsule = { .height = (k_board_length+0.2f)*2.0f, - .radius=grind_radius }; + rb_capsule capsule = { .h = (k_board_length+0.2f)*2.0f, + .r = grind_radius }; m4x3f mtx; v3_muls( localplayer.rb.to_world[0], 1.0f, mtx[0] ); v3_muls( localplayer.rb.to_world[2], -1.0f, mtx[1] ); @@ -2521,7 +2532,7 @@ begin_collision:; rb_ct *cman = &manifold[manifold_len]; - int l = rb_capsule__scene( mtx, &capsule, NULL, &world->rb_geo.inf.scene, + int l = rb_capsule__scene( mtx, &capsule, NULL, world->geo_bh, cman, k_material_flag_walking ); /* weld joints */ @@ -2533,7 +2544,7 @@ begin_collision:; manifold_len += l; if( vg_lines.draw ) - vg_line_capsule( mtx, capsule.radius, capsule.height, VG__WHITE ); + vg_line_capsule( mtx, capsule.r, capsule.h, VG__WHITE ); /* add limits */ if( state->activity >= k_skate_activity_grind_any ){ @@ -2594,7 +2605,7 @@ begin_collision:; * regular dance; calculate velocity & total mass, apply impulse. */ - struct contact *ct = &manifold[i]; + rb_ct *ct = &manifold[i]; v3f rv, delta; v3_sub( ct->co, world_cog, delta ); @@ -2633,7 +2644,7 @@ begin_collision:; v3f dt; rb_depenetrate( manifold, manifold_len, dt ); v3_add( dt, localplayer.rb.co, localplayer.rb.co ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); substep_count ++; @@ -2648,6 +2659,7 @@ begin_collision:; f32 nforce = v3_length(normal_total); if( nforce > 4.0f ){ if( nforce > 17.6f ){ + vg_info( "player fell off due to hitting ground too hard\n" ); v3_muladds( localplayer.rb.v, normal_total, -1.0f, localplayer.rb.v ); player__dead_transition( k_player_die_type_feet ); return; @@ -2703,7 +2715,9 @@ begin_collision:; q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q ); q_mul( transport_rotation, state->smoothed_rotation, state->smoothed_rotation ); - rb_update_transform( &localplayer.rb ); + q_normalize( localplayer.rb.q ); + q_normalize( state->smoothed_rotation ); + rb_update_matrices( &localplayer.rb ); player__pass_gate( id ); } @@ -3493,6 +3507,7 @@ static void player__skate_effects( void *_animator, m4x3f *final_mtx, static void player__skate_post_animate(void){ struct player_skate_state *state = &player_skate.state; localplayer.cam_velocity_influence = 1.0f; + localplayer.cam_dist = 1.8f; v3f head = { 0.0f, 1.8f, 0.0f }; m4x3_mulv( localplayer.final_mtx[ localplayer.id_head ], diff --git a/player_walk.c b/player_walk.c index 9c23978..560d61a 100644 --- a/player_walk.c +++ b/player_walk.c @@ -1,5 +1,7 @@ -#ifndef PLAYER_WALK_C -#define PLAYER_WALK_C +#pragma once + +#include "vg/vg_rigidbody_collision.h" +#include "scene_rigidbody.h" #include "player.h" #include "input.h" @@ -47,7 +49,7 @@ static void player_walk_generic_to_skate( enum skate_activity init, f32 yaw ){ player__begin_holdout( (v3f){0.0f,0.0f,0.0f} ); player__skate_reset_animator(); player__skate_clear_mechanics(); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); v3_copy( (v3f){yaw,0.0f,0.0f}, player_skate.state.trick_euler ); if( init == k_skate_activity_air ) @@ -68,7 +70,7 @@ static void player_walk_drop_in_to_skate(void){ v3f init_velocity; player_walk_drop_in_vector( init_velocity ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); v3_muladds( localplayer.rb.co, localplayer.rb.to_world[1], 1.0f, player_skate.state.cog ); v3_copy( init_velocity, player_skate.state.cog_v ); @@ -450,6 +452,7 @@ static void player_walk_update_generic(void){ player__networked_sfx( k_player_subsystem_walk, 32, k_player_walk_soundeffect_splash, localplayer.rb.co, 1.0f ); + vg_info( "player fell of due to walking into walker\n" ); player__dead_transition( k_player_die_type_generic ); return; } @@ -457,14 +460,14 @@ static void player_walk_update_generic(void){ enum walk_activity prev_state = w->state.activity; - w->collider.height = 2.0f; - w->collider.radius = 0.3f; + w->collider.h = 2.0f; + w->collider.r = 0.3f; m4x3f mtx; m3x3_copy( localplayer.rb.to_world, mtx ); v3_add( localplayer.rb.co, (v3f){0,1,0}, mtx[3] ); - vg_line_capsule( mtx, w->collider.radius, w->collider.height, VG__WHITE ); + vg_line_capsule( mtx, w->collider.r, w->collider.h, VG__WHITE ); rb_ct manifold[64]; int len; @@ -479,7 +482,7 @@ static void player_walk_update_generic(void){ */ len = rb_capsule__scene( mtx, &w->collider, NULL, - &world->rb_geo.inf.scene, manifold, 0 ); + world->geo_bh, manifold, 0 ); player_walk_custom_filter( world, manifold, len, 0.01f ); len = rb_manifold_apply_filtered( manifold, len ); @@ -489,7 +492,7 @@ static void player_walk_update_generic(void){ w->surface = k_surface_prop_concrete; for( int i=0; in ) ){ @@ -562,7 +565,7 @@ static void player_walk_update_generic(void){ */ for( int j=0; j<5; j++ ){ for( int i=0; in ); @@ -582,17 +585,17 @@ static void player_walk_update_generic(void){ v3f pa, pb; v3_copy( localplayer.rb.co, pa ); - pa[1] += w->collider.radius + max_dist; + pa[1] += w->collider.r + max_dist; v3_add( pa, (v3f){0, -max_dist * 2.0f, 0}, pb ); vg_line( pa, pb, 0xff000000 ); v3f n; float t; if( spherecast_world( world, pa, pb, - w->collider.radius, &t, n, 0 ) != -1 ){ + w->collider.r, &t, n, 0 ) != -1 ){ if( player_walk_normal_standable(n) ){ v3_lerp( pa, pb, t, localplayer.rb.co ); - localplayer.rb.co[1] += -w->collider.radius - k_penetration_slop; + localplayer.rb.co[1] += -w->collider.r - k_penetration_slop; w->state.activity = k_walk_activity_ground; float d = -v3_dot(n,localplayer.rb.v); @@ -622,7 +625,7 @@ static void player_walk_update_generic(void){ v3_muladds( localplayer.rb.co, localplayer.rb.v, k_rb_delta, localplayer.rb.co ); v3_add( localplayer.rb.co, (v3f){0,1,0}, mtx[3] ); - vg_line_capsule( mtx, w->collider.radius, w->collider.height, VG__GREEN ); + vg_line_capsule( mtx, w->collider.r, w->collider.h, VG__GREEN ); /* * CCD routine @@ -631,7 +634,7 @@ static void player_walk_update_generic(void){ */ v3f lwr_prev, lwr_now, - lwr_offs = { 0.0f, w->collider.radius, 0.0f }; + lwr_offs = { 0.0f, w->collider.r, 0.0f }; v3_add( lwr_offs, w->state.prev_pos, lwr_prev ); v3_add( lwr_offs, localplayer.rb.co, lwr_now ); @@ -642,16 +645,15 @@ static void player_walk_update_generic(void){ float movedist = v3_length( movedelta ); if( movedist > 0.3f ){ - float t, sr = w->collider.radius-0.04f; + float t, sr = w->collider.r-0.04f; v3f n; if( spherecast_world( world, lwr_prev, lwr_now, sr, &t, n, 0 ) != -1 ){ v3_lerp( lwr_prev, lwr_now, vg_maxf(0.01f,t), localplayer.rb.co ); - localplayer.rb.co[1] -= w->collider.radius; - rb_update_transform( &localplayer.rb ); - + localplayer.rb.co[1] -= w->collider.r; + rb_update_matrices( &localplayer.rb ); v3_add( localplayer.rb.co, (v3f){0,1,0}, mtx[3] ); - vg_line_capsule( mtx, w->collider.radius, w->collider.height, VG__RED); + vg_line_capsule( mtx, w->collider.r, w->collider.h, VG__RED); } } @@ -664,11 +666,11 @@ static void player_walk_update_generic(void){ v4f transport_rotation; m3x3_q( gate->transport, transport_rotation ); q_mul( transport_rotation, localplayer.rb.q, localplayer.rb.q ); - - rb_update_transform( &localplayer.rb ); + q_normalize( localplayer.rb.q ); + rb_update_matrices( &localplayer.rb ); player__pass_gate( id ); } - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); if( (prev_state == k_walk_activity_oregular) || (prev_state == k_walk_activity_oair) || @@ -690,7 +692,7 @@ static void player__walk_post_update(void){ float substep = vg.time_fixed_extrapolate; v3_muladds( mtx[3], localplayer.rb.v, k_rb_delta*substep, mtx[3] ); - vg_line_capsule( mtx, w->collider.radius, w->collider.height, VG__YELOW ); + vg_line_capsule( mtx, w->collider.r, w->collider.h, VG__YELOW ); /* Calculate header */ v3f v; @@ -797,7 +799,7 @@ static void player_walk_animate_drop_in(void){ v3_lerp( localplayer.rb.co, final_co, animator->transition_t, localplayer.rb.co ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); v3_muladds( localplayer.rb.co, localplayer.rb.to_world[1], -0.1f*animator->transition_t, localplayer.rb.co ); @@ -893,6 +895,13 @@ static void player__walk_animate(void){ } else localplayer.cam_velocity_influence = 0.0f; + + if( w->state.activity == k_walk_activity_sit ){ + localplayer.cam_dist = 3.8f; + } + else { + localplayer.cam_dist = 1.8f; + } } static void player_walk_pose_sit( struct player_walk_animator *animator, @@ -1132,7 +1141,7 @@ static void player__walk_transition( bool grounded, f32 board_yaw ){ w->state.walk_timer = 0.0f; w->state.step_phase = 0; w->animator.board_yaw = fmodf( board_yaw, 2.0f ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); } static void player__walk_reset(void){ @@ -1145,7 +1154,7 @@ static void player__walk_reset(void){ q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f}, atan2f(fwd[0], fwd[2]) ); - rb_update_transform( &localplayer.rb ); + rb_update_matrices( &localplayer.rb ); } static void player__walk_animator_exchange( bitpack_ctx *ctx, void *data ){ @@ -1182,4 +1191,3 @@ static void player__walk_sfx_oneshot( u8 id, v3f pos, f32 volume ){ audio_unlock(); } -#endif /* PLAYER_DEVICE_WALK_H */ diff --git a/player_walk.h b/player_walk.h index 1cab8eb..95ce2a0 100644 --- a/player_walk.h +++ b/player_walk.h @@ -3,7 +3,7 @@ #include "player.h" #include "player_api.h" -#include "rigidbody.h" +#include "vg/vg_rigidbody.h" #define PLAYER_JUMP_EPSILON 0.1 /* 100ms jump allowance */ diff --git a/rigidbody.h b/rigidbody.h deleted file mode 100644 index a69e4af..0000000 --- a/rigidbody.h +++ /dev/null @@ -1,1868 +0,0 @@ -/* - * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved - */ - -/* - * Resources: Box2D - Erin Catto - * qu3e - Randy Gaul - */ - -#include "vg/vg_console.h" -#include "bvh.h" -#include "scene.h" - -#include - -static bh_system bh_system_rigidbodies; - -#ifndef RIGIDBODY_H -#define RIGIDBODY_H - -/* - * ----------------------------------------------------------------------------- - * (K)onstants - * ----------------------------------------------------------------------------- - */ - -static const float - k_rb_rate = (1.0/VG_TIMESTEP_FIXED), - k_rb_delta = (1.0/k_rb_rate), - k_friction = 0.4f, - k_damp_linear = 0.1f, /* scale velocity 1/(1+x) */ - k_damp_angular = 0.1f, /* scale angular 1/(1+x) */ - k_penetration_slop = 0.01f, - k_inertia_scale = 8.0f, - k_phys_baumgarte = 0.2f, - k_gravity = 9.6f; - -static float - k_limit_bias = 0.02f, - k_joint_correction = 0.01f, - k_joint_impulse = 1.0f, - k_joint_bias = 0.08f; /* positional joints */ - -static void rb_register_cvar(void){ - VG_VAR_F32( k_limit_bias, flags=VG_VAR_CHEAT ); - VG_VAR_F32( k_joint_bias, flags=VG_VAR_CHEAT ); - VG_VAR_F32( k_joint_correction, flags=VG_VAR_CHEAT ); - VG_VAR_F32( k_joint_impulse, flags=VG_VAR_CHEAT ); -} - -/* - * ----------------------------------------------------------------------------- - * structure definitions - * ----------------------------------------------------------------------------- - */ - -typedef struct rigidbody rigidbody; -typedef struct rb_object rb_object; -typedef struct contact rb_ct; -typedef struct rb_sphere rb_sphere; -typedef struct rb_capsule rb_capsule; -typedef struct rb_scene rb_scene; - -struct rb_sphere{ - float radius; -}; - -struct rb_capsule{ - float height, radius; -}; - -struct rb_scene{ - bh_tree *bh_scene; -}; - -struct rigidbody{ - v3f co, v, w; - v4f q; - - boxf bbx, bbx_world; - float inv_mass; - - /* inertia model and inverse world tensor */ - v3f I; - m3x3f iI, iIw; - m4x3f to_world, to_local; -}; - -/* simple objects */ -struct rb_object{ - rigidbody rb; - enum rb_shape{ - k_rb_shape_box = 0, - k_rb_shape_sphere = 1, - k_rb_shape_capsule = 2, - k_rb_shape_scene = 3 - } - type; - - union{ - struct rb_sphere sphere; - struct rb_capsule capsule; - struct rb_scene scene; - } - inf; -}; - -static struct contact{ - rigidbody *rba, *rbb; - v3f co, n; - v3f t[2]; - float p, bias, norm_impulse, tangent_impulse[2], - normal_mass, tangent_mass[2]; - - u32 element_id; - - enum contact_type type; -} -rb_contact_buffer[256]; -static int rb_contact_count = 0; - -typedef struct rb_constr_pos rb_constr_pos; -typedef struct rb_constr_swingtwist rb_constr_swingtwist; - -struct rb_constr_pos{ - rigidbody *rba, *rbb; - v3f lca, lcb; -}; - -struct rb_constr_swingtwist{ - rigidbody *rba, *rbb; - - v4f conevx, conevy; /* relative to rba */ - v3f view_offset, /* relative to rba */ - coneva, conevxb;/* relative to rbb */ - - int tangent_violation, axis_violation; - v3f axis, tangent_axis, tangent_target, axis_target; - - float conet; - float tangent_mass, axis_mass; -}; - -/* - * ----------------------------------------------------------------------------- - * Debugging - * ----------------------------------------------------------------------------- - */ - -static void rb_debug_contact( rb_ct *ct ){ - v3f p1; - v3_muladds( ct->co, ct->n, 0.05f, p1 ); - - if( ct->type == k_contact_type_default ){ - vg_line_point( ct->co, 0.0125f, 0xff0000ff ); - vg_line( ct->co, p1, 0xffffffff ); - } - else if( ct->type == k_contact_type_edge ){ - vg_line_point( ct->co, 0.0125f, 0xff00ffc0 ); - vg_line( ct->co, p1, 0xffffffff ); - } -} - - -static void rb_object_debug( rb_object *obj, u32 colour ){ - if( obj->type == k_rb_shape_box ){ - v3f *box = obj->rb.bbx; - vg_line_boxf_transformed( obj->rb.to_world, obj->rb.bbx, colour ); - } - else if( obj->type == k_rb_shape_sphere ){ - vg_line_sphere( obj->rb.to_world, obj->inf.sphere.radius, colour ); - } - else if( obj->type == k_rb_shape_capsule ){ - m4x3f m0, m1; - float h = obj->inf.capsule.height, - r = obj->inf.capsule.radius; - - vg_line_capsule( obj->rb.to_world, r, h, colour ); - } - else if( obj->type == k_rb_shape_scene ){ - vg_line_boxf( obj->rb.bbx, colour ); - } -} - -/* - * ----------------------------------------------------------------------------- - * Integration - * ----------------------------------------------------------------------------- - */ - -/* - * Update world space bounding box based on local one - */ -static void rb_update_bounds( rigidbody *rb ){ - box_init_inf( rb->bbx_world ); - m4x3_expand_aabb_aabb( rb->to_world, rb->bbx_world, rb->bbx ); -} - -/* - * Commit transform to rigidbody. Updates matrices - */ -static void rb_update_transform( rigidbody *rb ) -{ - q_normalize( rb->q ); - q_m3x3( rb->q, rb->to_world ); - v3_copy( rb->co, rb->to_world[3] ); - - m4x3_invert_affine( rb->to_world, rb->to_local ); - m3x3_mul( rb->iI, rb->to_local, rb->iIw ); - m3x3_mul( rb->to_world, rb->iIw, rb->iIw ); - - rb_update_bounds( rb ); -} - -/* - * Extrapolate rigidbody into a transform based on vg accumulator. - * Useful for rendering - */ -static void rb_extrapolate( rigidbody *rb, v3f co, v4f q ) -{ - float substep = vg.time_fixed_extrapolate; - v3_muladds( rb->co, rb->v, k_rb_delta*substep, co ); - - if( v3_length2( rb->w ) > 0.0f ){ - v4f rotation; - v3f axis; - v3_copy( rb->w, axis ); - - float mag = v3_length( axis ); - v3_divs( axis, mag, axis ); - q_axis_angle( rotation, axis, mag*k_rb_delta*substep ); - q_mul( rotation, rb->q, q ); - q_normalize( q ); - } - else{ - v4_copy( rb->q, q ); - } -} - -/* - * Initialize rigidbody and calculate masses, inertia - */ -static void rb_init_object( rb_object *obj ){ - float volume = 1.0f; - int inert = 0; - - if( obj->type == k_rb_shape_box ){ - v3f dims; - v3_sub( obj->rb.bbx[1], obj->rb.bbx[0], dims ); - volume = dims[0]*dims[1]*dims[2]; - } - else if( obj->type == k_rb_shape_sphere ){ - volume = vg_sphere_volume( obj->inf.sphere.radius ); - v3_fill( obj->rb.bbx[0], -obj->inf.sphere.radius ); - v3_fill( obj->rb.bbx[1], obj->inf.sphere.radius ); - } - else if( obj->type == k_rb_shape_capsule ){ - float r = obj->inf.capsule.radius, - h = obj->inf.capsule.height; - volume = vg_sphere_volume( r ) + VG_PIf * r*r * (h - r*2.0f); - - v3_fill( obj->rb.bbx[0], -r ); - v3_fill( obj->rb.bbx[1], r ); - obj->rb.bbx[0][1] = -h; - obj->rb.bbx[1][1] = h; - } - else if( obj->type == k_rb_shape_scene ){ - inert = 1; - box_copy( obj->inf.scene.bh_scene->nodes[0].bbx, obj->rb.bbx ); - } - - if( inert ){ - obj->rb.inv_mass = 0.0f; - v3_zero( obj->rb.I ); - m3x3_zero( obj->rb.iI ); - } - else{ - float mass = 2.0f*volume; - obj->rb.inv_mass = 1.0f/mass; - - v3f extent; - v3_sub( obj->rb.bbx[1], obj->rb.bbx[0], extent ); - v3_muls( extent, 0.5f, extent ); - - /* local intertia tensor */ - float scale = k_inertia_scale; - float ex2 = scale*extent[0]*extent[0], - ey2 = scale*extent[1]*extent[1], - ez2 = scale*extent[2]*extent[2]; - - obj->rb.I[0] = ((1.0f/12.0f) * mass * (ey2+ez2)); - obj->rb.I[1] = ((1.0f/12.0f) * mass * (ex2+ez2)); - obj->rb.I[2] = ((1.0f/12.0f) * mass * (ex2+ey2)); - - m3x3_identity( obj->rb.iI ); - obj->rb.iI[0][0] = obj->rb.I[0]; - obj->rb.iI[1][1] = obj->rb.I[1]; - obj->rb.iI[2][2] = obj->rb.I[2]; - m3x3_inv( obj->rb.iI, obj->rb.iI ); - } - - rb_update_transform( &obj->rb ); -} - -static void rb_iter( rigidbody *rb ){ - if( !vg_validf( rb->v[0] ) || - !vg_validf( rb->v[1] ) || - !vg_validf( rb->v[2] ) ) - { - vg_fatal_error( "NaN velocity" ); - } - - v3f gravity = { 0.0f, -9.8f, 0.0f }; - v3_muladds( rb->v, gravity, k_rb_delta, rb->v ); - - /* intergrate velocity */ - v3_muladds( rb->co, rb->v, k_rb_delta, rb->co ); - v3_lerp( rb->w, (v3f){0.0f,0.0f,0.0f}, 0.0025f, rb->w ); - - /* inegrate inertia */ - if( v3_length2( rb->w ) > 0.0f ) - { - v4f rotation; - v3f axis; - v3_copy( rb->w, axis ); - - float mag = v3_length( axis ); - v3_divs( axis, mag, axis ); - q_axis_angle( rotation, axis, mag*k_rb_delta ); - q_mul( rotation, rb->q, rb->q ); - } - -#if 0 - /* damping */ - v3_muls( rb->v, 1.0f/(1.0f+k_rb_delta*k_damp_linear), rb->v ); - v3_muls( rb->w, 1.0f/(1.0f+k_rb_delta*k_damp_angular), rb->w ); -#endif -} - - -/* - * ----------------------------------------------------------------------------- - * Boolean shape overlap functions - * ----------------------------------------------------------------------------- - */ - -/* - * Project AABB, and triangle interval onto axis to check if they overlap - */ -static int rb_box_triangle_interval( v3f extent, v3f axis, v3f tri[3] ){ - float - - r = extent[0] * fabsf(axis[0]) + - extent[1] * fabsf(axis[1]) + - extent[2] * fabsf(axis[2]), - - p0 = v3_dot( axis, tri[0] ), - p1 = v3_dot( axis, tri[1] ), - p2 = v3_dot( axis, tri[2] ), - - e = vg_maxf(-vg_maxf(p0,vg_maxf(p1,p2)), vg_minf(p0,vg_minf(p1,p2))); - - if( e > r ) return 0; - else return 1; -} - -/* - * Seperating axis test box vs triangle - */ -static int rb_box_triangle_sat( v3f extent, v3f center, - m4x3f to_local, v3f tri_src[3] ){ - v3f tri[3]; - - for( int i=0; i<3; i++ ){ - m4x3_mulv( to_local, tri_src[i], tri[i] ); - v3_sub( tri[i], center, tri[i] ); - } - - v3f f0,f1,f2,n; - v3_sub( tri[1], tri[0], f0 ); - v3_sub( tri[2], tri[1], f1 ); - v3_sub( tri[0], tri[2], f2 ); - - - v3f axis[9]; - v3_cross( (v3f){1.0f,0.0f,0.0f}, f0, axis[0] ); - v3_cross( (v3f){1.0f,0.0f,0.0f}, f1, axis[1] ); - v3_cross( (v3f){1.0f,0.0f,0.0f}, f2, axis[2] ); - v3_cross( (v3f){0.0f,1.0f,0.0f}, f0, axis[3] ); - v3_cross( (v3f){0.0f,1.0f,0.0f}, f1, axis[4] ); - v3_cross( (v3f){0.0f,1.0f,0.0f}, f2, axis[5] ); - v3_cross( (v3f){0.0f,0.0f,1.0f}, f0, axis[6] ); - v3_cross( (v3f){0.0f,0.0f,1.0f}, f1, axis[7] ); - v3_cross( (v3f){0.0f,0.0f,1.0f}, f2, axis[8] ); - - for( int i=0; i<9; i++ ) - if(!rb_box_triangle_interval( extent, axis[i], tri )) return 0; - - /* u0, u1, u2 */ - if(!rb_box_triangle_interval( extent, (v3f){1.0f,0.0f,0.0f}, tri )) return 0; - if(!rb_box_triangle_interval( extent, (v3f){0.0f,1.0f,0.0f}, tri )) return 0; - if(!rb_box_triangle_interval( extent, (v3f){0.0f,0.0f,1.0f}, tri )) return 0; - - /* normal */ - v3_cross( f0, f1, n ); - if(!rb_box_triangle_interval( extent, n, tri )) return 0; - - return 1; -} - -/* - * ----------------------------------------------------------------------------- - * Manifold - * ----------------------------------------------------------------------------- - */ - -static int rb_manifold_apply_filtered( rb_ct *man, int len ){ - int k = 0; - - for( int i=0; itype == k_contact_type_disabled ) - continue; - - man[k ++] = man[i]; - } - - return k; -} - -/* - * Merge two contacts if they are within radius(r) of eachother - */ -static void rb_manifold_contact_weld( rb_ct *ci, rb_ct *cj, float r ){ - if( v3_dist2( ci->co, cj->co ) < r*r ){ - cj->type = k_contact_type_disabled; - ci->p = (ci->p + cj->p) * 0.5f; - - v3_add( ci->co, cj->co, ci->co ); - v3_muls( ci->co, 0.5f, ci->co ); - - v3f delta; - v3_sub( ci->rba->co, ci->co, delta ); - - float c0 = v3_dot( ci->n, delta ), - c1 = v3_dot( cj->n, delta ); - - if( c0 < 0.0f || c1 < 0.0f ){ - /* error */ - ci->type = k_contact_type_disabled; - } - else{ - v3f n; - v3_muls( ci->n, c0, n ); - v3_muladds( n, cj->n, c1, n ); - v3_normalize( n ); - v3_copy( n, ci->n ); - } - } -} - -/* - * - */ -static void rb_manifold_filter_joint_edges( rb_ct *man, int len, float r ){ - for( int i=0; itype != k_contact_type_edge ) - continue; - - for( int j=i+1; jtype != k_contact_type_edge ) - continue; - - rb_manifold_contact_weld( ci, cj, r ); - } - } -} - -/* - * Resolve overlapping pairs - */ -static void rb_manifold_filter_pairs( rb_ct *man, int len, float r ){ - for( int i=0; itype == k_contact_type_disabled ) continue; - - for( int j=i+1; jtype == k_contact_type_disabled ) continue; - - if( v3_dist2( ci->co, cj->co ) < r*r ){ - cj->type = k_contact_type_disabled; - v3_add( cj->n, ci->n, ci->n ); - ci->p += cj->p; - similar ++; - } - } - - if( similar ){ - float n = 1.0f/((float)similar+1.0f); - v3_muls( ci->n, n, ci->n ); - ci->p *= n; - - if( v3_length2(ci->n) < 0.1f*0.1f ) - ci->type = k_contact_type_disabled; - else - v3_normalize( ci->n ); - } - } -} - -/* - * Remove contacts that are facing away from A - */ -static void rb_manifold_filter_backface( rb_ct *man, int len ){ - for( int i=0; itype == k_contact_type_disabled ) - continue; - - v3f delta; - v3_sub( ct->co, ct->rba->co, delta ); - - if( v3_dot( delta, ct->n ) > -0.001f ) - ct->type = k_contact_type_disabled; - } -} - -/* - * Filter out duplicate coplanar results. Good for spheres. - */ -static void rb_manifold_filter_coplanar( rb_ct *man, int len, float w ){ - for( int i=0; itype == k_contact_type_disabled || - ci->type == k_contact_type_edge ) - continue; - - float d1 = v3_dot( ci->co, ci->n ); - - for( int j=0; jtype == k_contact_type_disabled ) - continue; - - float d2 = v3_dot( cj->co, ci->n ), - d = d2-d1; - - if( fabsf( d ) <= w ){ - cj->type = k_contact_type_disabled; - } - } - } -} - -/* - * ----------------------------------------------------------------------------- - * Collision matrix - * ----------------------------------------------------------------------------- - */ - -/* - * Contact generators - * - * These do not automatically allocate contacts, an appropriately sized - * buffer must be supplied. The function returns the size of the manifold - * which was generated. - * - * The values set on the contacts are: n, co, p, rba, rbb - */ - -/* - * By collecting the minimum(time) and maximum(time) pairs of points, we - * build a reduced and stable exact manifold. - * - * tx: time at point - * rx: minimum distance of these points - * dx: the delta between the two points - * - * pairs will only ammend these if they are creating a collision - */ -typedef struct capsule_manifold capsule_manifold; -struct capsule_manifold{ - float t0, t1; - float r0, r1; - v3f d0, d1; -}; - -/* - * Expand a line manifold with a new pair. t value is the time along segment - * on the oriented object which created this pair. - */ -static void rb_capsule_manifold( v3f pa, v3f pb, float t, float r, - capsule_manifold *manifold ){ - v3f delta; - v3_sub( pa, pb, delta ); - - if( v3_length2(delta) < r*r ){ - if( t < manifold->t0 ){ - v3_copy( delta, manifold->d0 ); - manifold->t0 = t; - manifold->r0 = r; - } - - if( t > manifold->t1 ){ - v3_copy( delta, manifold->d1 ); - manifold->t1 = t; - manifold->r1 = r; - } - } -} - -static void rb_capsule_manifold_init( capsule_manifold *manifold ){ - manifold->t0 = INFINITY; - manifold->t1 = -INFINITY; -} - -static int rb_capsule__manifold_done( m4x3f mtx, rb_capsule *c, - capsule_manifold *manifold, - rb_ct *buf ){ - v3f p0, p1; - v3_muladds( mtx[3], mtx[1], -c->height*0.5f+c->radius, p0 ); - v3_muladds( mtx[3], mtx[1], c->height*0.5f-c->radius, p1 ); - - int count = 0; - if( manifold->t0 <= 1.0f ){ - rb_ct *ct = buf; - - v3f pa; - v3_muls( p0, 1.0f-manifold->t0, pa ); - v3_muladds( pa, p1, manifold->t0, pa ); - - float d = v3_length( manifold->d0 ); - v3_muls( manifold->d0, 1.0f/d, ct->n ); - v3_muladds( pa, ct->n, -c->radius, ct->co ); - - ct->p = manifold->r0 - d; - ct->type = k_contact_type_default; - count ++; - } - - if( (manifold->t1 >= 0.0f) && (manifold->t0 != manifold->t1) ){ - rb_ct *ct = buf+count; - - v3f pa; - v3_muls( p0, 1.0f-manifold->t1, pa ); - v3_muladds( pa, p1, manifold->t1, pa ); - - float d = v3_length( manifold->d1 ); - v3_muls( manifold->d1, 1.0f/d, ct->n ); - v3_muladds( pa, ct->n, -c->radius, ct->co ); - - ct->p = manifold->r1 - d; - ct->type = k_contact_type_default; - - count ++; - } - - /* - * Debugging - */ - - if( count == 2 ) - vg_line( buf[0].co, buf[1].co, 0xff0000ff ); - - return count; -} - -static int rb_capsule_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){ - rigidbody *rba = &obja->rb, *rbb = &objb->rb; - float h = obja->inf.capsule.height, - ra = obja->inf.capsule.radius, - rb = objb->inf.sphere.radius; - - v3f p0, p1; - v3_muladds( rba->co, rba->to_world[1], -h*0.5f+ra, p0 ); - v3_muladds( rba->co, rba->to_world[1], h*0.5f-ra, p1 ); - - v3f c, delta; - closest_point_segment( p0, p1, rbb->co, c ); - v3_sub( c, rbb->co, delta ); - - float d2 = v3_length2(delta), - r = ra + rb; - - if( d2 < r*r ){ - float d = sqrtf(d2); - - rb_ct *ct = buf; - v3_muls( delta, 1.0f/d, ct->n ); - ct->p = r-d; - - v3f p0, p1; - v3_muladds( c, ct->n, -ra, p0 ); - v3_muladds( rbb->co, ct->n, rb, p1 ); - v3_add( p0, p1, ct->co ); - v3_muls( ct->co, 0.5f, ct->co ); - - ct->rba = rba; - ct->rbb = rbb; - ct->type = k_contact_type_default; - - return 1; - } - - return 0; -} - -static int rb_capsule__capsule( m4x3f mtxA, rb_capsule *ca, - m4x3f mtxB, rb_capsule *cb, rb_ct *buf ){ - float ha = ca->height, - hb = cb->height, - ra = ca->radius, - rb = cb->radius, - r = ra+rb; - - v3f p0, p1, p2, p3; - v3_muladds( mtxA[3], mtxA[1], -ha*0.5f+ra, p0 ); - v3_muladds( mtxA[3], mtxA[1], ha*0.5f-ra, p1 ); - v3_muladds( mtxB[3], mtxB[1], -hb*0.5f+rb, p2 ); - v3_muladds( mtxB[3], mtxB[1], hb*0.5f-rb, p3 ); - - capsule_manifold manifold; - rb_capsule_manifold_init( &manifold ); - - v3f pa, pb; - float ta, tb; - closest_segment_segment( p0, p1, p2, p3, &ta, &tb, pa, pb ); - rb_capsule_manifold( pa, pb, ta, r, &manifold ); - - ta = closest_point_segment( p0, p1, p2, pa ); - tb = closest_point_segment( p0, p1, p3, pb ); - rb_capsule_manifold( pa, p2, ta, r, &manifold ); - rb_capsule_manifold( pb, p3, tb, r, &manifold ); - - closest_point_segment( p2, p3, p0, pa ); - closest_point_segment( p2, p3, p1, pb ); - rb_capsule_manifold( p0, pa, 0.0f, r, &manifold ); - rb_capsule_manifold( p1, pb, 1.0f, r, &manifold ); - - return rb_capsule__manifold_done( mtxA, ca, &manifold, buf ); -} - -static int rb_sphere_box( rb_object *obja, rb_object *objb, rb_ct *buf ){ - v3f co, delta; - rigidbody *rba = &obja->rb, *rbb = &objb->rb; - - closest_point_obb( rba->co, rbb->bbx, rbb->to_world, rbb->to_local, co ); - v3_sub( rba->co, co, delta ); - - float d2 = v3_length2(delta), - r = obja->inf.sphere.radius; - - if( d2 <= r*r ){ - float d; - - rb_ct *ct = buf; - if( d2 <= 0.0001f ){ - v3_sub( rba->co, rbb->co, delta ); - - /* - * some extra testing is required to find the best axis to push the - * object back outside the box. Since there isnt a clear seperating - * vector already, especially on really high aspect boxes. - */ - float lx = v3_dot( rbb->to_world[0], delta ), - ly = v3_dot( rbb->to_world[1], delta ), - lz = v3_dot( rbb->to_world[2], delta ), - px = rbb->bbx[1][0] - fabsf(lx), - py = rbb->bbx[1][1] - fabsf(ly), - pz = rbb->bbx[1][2] - fabsf(lz); - - if( px < py && px < pz ) - v3_muls( rbb->to_world[0], vg_signf(lx), ct->n ); - else if( py < pz ) - v3_muls( rbb->to_world[1], vg_signf(ly), ct->n ); - else - v3_muls( rbb->to_world[2], vg_signf(lz), ct->n ); - - v3_muladds( rba->co, ct->n, -r, ct->co ); - ct->p = r; - } - else{ - d = sqrtf(d2); - v3_muls( delta, 1.0f/d, ct->n ); - ct->p = r-d; - v3_copy( co, ct->co ); - } - - ct->rba = rba; - ct->rbb = rbb; - ct->type = k_contact_type_default; - return 1; - } - - return 0; -} - -static int rb_sphere_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){ - rigidbody *rba = &obja->rb, *rbb = &objb->rb; - v3f delta; - v3_sub( rba->co, rbb->co, delta ); - - float d2 = v3_length2(delta), - r = obja->inf.sphere.radius + objb->inf.sphere.radius; - - if( d2 < r*r ){ - float d = sqrtf(d2); - - rb_ct *ct = buf; - v3_muls( delta, 1.0f/d, ct->n ); - - v3f p0, p1; - v3_muladds( rba->co, ct->n,-obja->inf.sphere.radius, p0 ); - v3_muladds( rbb->co, ct->n, objb->inf.sphere.radius, p1 ); - v3_add( p0, p1, ct->co ); - v3_muls( ct->co, 0.5f, ct->co ); - ct->type = k_contact_type_default; - ct->p = r-d; - ct->rba = rba; - ct->rbb = rbb; - return 1; - } - - return 0; -} - -static int rb_sphere__triangle( m4x3f mtxA, rb_sphere *b, - v3f tri[3], rb_ct *buf ){ - v3f delta, co; - enum contact_type type = closest_on_triangle_1( mtxA[3], tri, co ); - - v3_sub( mtxA[3], co, delta ); - - float d2 = v3_length2( delta ), - r = b->radius; - - if( d2 <= r*r ){ - rb_ct *ct = buf; - - v3f ab, ac, tn; - v3_sub( tri[2], tri[0], ab ); - v3_sub( tri[1], tri[0], ac ); - v3_cross( ac, ab, tn ); - v3_copy( tn, ct->n ); - - if( v3_length2( ct->n ) <= 0.00001f ){ -#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING - vg_error( "Zero area triangle!\n" ); -#endif - return 0; - } - - v3_normalize( ct->n ); - - float d = sqrtf(d2); - - v3_copy( co, ct->co ); - ct->type = type; - ct->p = r-d; - return 1; - } - - return 0; -} - -static int rb_sphere__scene( m4x3f mtxA, rb_sphere *b, - m4x3f mtxB, rb_scene *s, rb_ct *buf, - u16 ignore ){ - scene_context *sc = s->bh_scene->user; - - int count = 0; - - float r = b->radius + 0.1f; - boxf box; - v3_sub( mtxA[3], (v3f){ r,r,r }, box[0] ); - v3_add( mtxA[3], (v3f){ r,r,r }, box[1] ); - - bh_iter it; - i32 idx; - bh_iter_init_box( 0, &it, box ); - - while( bh_next( s->bh_scene, &it, &idx ) ){ - u32 *ptri = &sc->arrindices[ idx*3 ]; - v3f tri[3]; - - if( sc->arrvertices[ptri[0]].flags & ignore ) continue; - - for( int j=0; j<3; j++ ) - v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); - - buf[ count ].element_id = ptri[0]; - - vg_line( tri[0],tri[1],0x70ff6000 ); - vg_line( tri[1],tri[2],0x70ff6000 ); - vg_line( tri[2],tri[0],0x70ff6000 ); - - int contact = rb_sphere__triangle( mtxA, b, tri, &buf[count] ); - count += contact; - - if( count == 16 ){ - vg_warn( "Exceeding sphere_vs_scene capacity. Geometry too dense!\n" ); - return count; - } - } - - return count; -} - -static int rb_box__scene( m4x3f mtxA, boxf bbx, - m4x3f mtxB, rb_scene *s, rb_ct *buf, u16 ignore ){ - scene_context *sc = s->bh_scene->user; - v3f tri[3]; - - v3f extent, center; - v3_sub( bbx[1], bbx[0], extent ); - v3_muls( extent, 0.5f, extent ); - v3_add( bbx[0], extent, center ); - - float r = v3_length(extent); - boxf world_bbx; - v3_fill( world_bbx[0], -r ); - v3_fill( world_bbx[1], r ); - for( int i=0; i<2; i++ ){ - v3_add( center, world_bbx[i], world_bbx[i] ); - v3_add( mtxA[3], world_bbx[i], world_bbx[i] ); - } - - m4x3f to_local; - m4x3_invert_affine( mtxA, to_local ); - - bh_iter it; - bh_iter_init_box( 0, &it, world_bbx ); - int idx; - int count = 0; - - vg_line_boxf( world_bbx, VG__RED ); - - while( bh_next( s->bh_scene, &it, &idx ) ){ - u32 *ptri = &sc->arrindices[ idx*3 ]; - if( sc->arrvertices[ptri[0]].flags & ignore ) continue; - - for( int j=0; j<3; j++ ) - v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); - - if( rb_box_triangle_sat( extent, center, to_local, tri ) ){ - vg_line(tri[0],tri[1],0xff50ff00 ); - vg_line(tri[1],tri[2],0xff50ff00 ); - vg_line(tri[2],tri[0],0xff50ff00 ); - } - else{ - vg_line(tri[0],tri[1],0xff0000ff ); - vg_line(tri[1],tri[2],0xff0000ff ); - vg_line(tri[2],tri[0],0xff0000ff ); - continue; - } - - v3f v0,v1,n; - v3_sub( tri[1], tri[0], v0 ); - v3_sub( tri[2], tri[0], v1 ); - v3_cross( v0, v1, n ); - - if( v3_length2( n ) <= 0.00001f ){ -#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING - vg_error( "Zero area triangle!\n" ); -#endif - return 0; - } - - v3_normalize( n ); - - /* find best feature */ - float best = v3_dot( mtxA[0], n ); - int axis = 0; - - for( int i=1; i<3; i++ ){ - float c = v3_dot( mtxA[i], n ); - - if( fabsf(c) > fabsf(best) ){ - best = c; - axis = i; - } - } - - v3f manifold[4]; - - if( axis == 0 ){ - float px = best > 0.0f? bbx[0][0]: bbx[1][0]; - manifold[0][0] = px; - manifold[0][1] = bbx[0][1]; - manifold[0][2] = bbx[0][2]; - manifold[1][0] = px; - manifold[1][1] = bbx[1][1]; - manifold[1][2] = bbx[0][2]; - manifold[2][0] = px; - manifold[2][1] = bbx[1][1]; - manifold[2][2] = bbx[1][2]; - manifold[3][0] = px; - manifold[3][1] = bbx[0][1]; - manifold[3][2] = bbx[1][2]; - } - else if( axis == 1 ){ - float py = best > 0.0f? bbx[0][1]: bbx[1][1]; - manifold[0][0] = bbx[0][0]; - manifold[0][1] = py; - manifold[0][2] = bbx[0][2]; - manifold[1][0] = bbx[1][0]; - manifold[1][1] = py; - manifold[1][2] = bbx[0][2]; - manifold[2][0] = bbx[1][0]; - manifold[2][1] = py; - manifold[2][2] = bbx[1][2]; - manifold[3][0] = bbx[0][0]; - manifold[3][1] = py; - manifold[3][2] = bbx[1][2]; - } - else{ - float pz = best > 0.0f? bbx[0][2]: bbx[1][2]; - manifold[0][0] = bbx[0][0]; - manifold[0][1] = bbx[0][1]; - manifold[0][2] = pz; - manifold[1][0] = bbx[1][0]; - manifold[1][1] = bbx[0][1]; - manifold[1][2] = pz; - manifold[2][0] = bbx[1][0]; - manifold[2][1] = bbx[1][1]; - manifold[2][2] = pz; - manifold[3][0] = bbx[0][0]; - manifold[3][1] = bbx[1][1]; - manifold[3][2] = pz; - } - - for( int j=0; j<4; j++ ) - m4x3_mulv( mtxA, manifold[j], manifold[j] ); - - vg_line( manifold[0], manifold[1], 0xffffffff ); - vg_line( manifold[1], manifold[2], 0xffffffff ); - vg_line( manifold[2], manifold[3], 0xffffffff ); - vg_line( manifold[3], manifold[0], 0xffffffff ); - - for( int j=0; j<4; j++ ){ - rb_ct *ct = buf+count; - - v3_copy( manifold[j], ct->co ); - v3_copy( n, ct->n ); - - float l0 = v3_dot( tri[0], n ), - l1 = v3_dot( manifold[j], n ); - - ct->p = (l0-l1)*0.5f; - if( ct->p < 0.0f ) - continue; - - ct->type = k_contact_type_default; - count ++; - - if( count >= 12 ) - return count; - } - } - return count; -} - -static int rb_capsule__triangle( m4x3f mtxA, rb_capsule *c, - v3f tri[3], rb_ct *buf ){ - v3f pc, p0w, p1w; - v3_muladds( mtxA[3], mtxA[1], -c->height*0.5f+c->radius, p0w ); - v3_muladds( mtxA[3], mtxA[1], c->height*0.5f-c->radius, p1w ); - - capsule_manifold manifold; - rb_capsule_manifold_init( &manifold ); - - v3f v0, v1, n; - v3_sub( tri[1], tri[0], v0 ); - v3_sub( tri[2], tri[0], v1 ); - v3_cross( v0, v1, n ); - - if( v3_length2( n ) <= 0.00001f ){ -#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING - vg_error( "Zero area triangle!\n" ); -#endif - return 0; - } - - v3_normalize( n ); - -#if 1 - /* deep penetration recovery. for when we clip through the triangles. so its - * not very 'correct' */ - f32 dist; - if( ray_tri( tri, p0w, mtxA[1], &dist, 1 ) ){ - f32 l = c->height - c->radius*2.0f; - if( (dist >= 0.0f) && (dist < l) ){ - v3f co; - v3_muladds( p0w, mtxA[1], dist, co ); - vg_line_point( co, 0.02f, 0xffffff00 ); - - v3f d0, d1; - v3_sub( p0w, co, d0 ); - v3_sub( p1w, co, d1 ); - - f32 p = vg_minf( v3_dot( n, d0 ), v3_dot( n, d1 ) ) - c->radius; - - rb_ct *ct = buf; - ct->p = -p; - ct->type = k_contact_type_default; - v3_copy( n, ct->n ); - v3_muladds( co, n, p, ct->co ); - - return 1; - } - } -#endif - - v3f c0, c1; - closest_on_triangle_1( p0w, tri, c0 ); - closest_on_triangle_1( p1w, tri, c1 ); - - v3f d0, d1, da; - v3_sub( c0, p0w, d0 ); - v3_sub( c1, p1w, d1 ); - v3_sub( p1w, p0w, da ); - - v3_normalize(d0); - v3_normalize(d1); - v3_normalize(da); - - /* the two balls at the ends */ - if( v3_dot( da, d0 ) <= 0.01f ) - rb_capsule_manifold( p0w, c0, 0.0f, c->radius, &manifold ); - if( v3_dot( da, d1 ) >= -0.01f ) - rb_capsule_manifold( p1w, c1, 1.0f, c->radius, &manifold ); - - /* the edges to edges */ - for( int i=0; i<3; i++ ){ - int i0 = i, - i1 = (i+1)%3; - - v3f ca, cb; - float ta, tb; - closest_segment_segment( p0w, p1w, tri[i0], tri[i1], &ta, &tb, ca, cb ); - rb_capsule_manifold( ca, cb, ta, c->radius, &manifold ); - } - - int count = rb_capsule__manifold_done( mtxA, c, &manifold, buf ); - for( int i=0; iheight, c->height, c->height }, bbx[0] ); - v3_add( mtxA[3], (v3f){ c->height, c->height, c->height }, bbx[1] ); - - scene_context *sc = s->bh_scene->user; - - bh_iter it; - bh_iter_init_box( 0, &it, bbx ); - i32 idx; - while( bh_next( s->bh_scene, &it, &idx ) ){ - u32 *ptri = &sc->arrindices[ idx*3 ]; - if( sc->arrvertices[ptri[0]].flags & ignore ) continue; - - v3f tri[3]; - for( int j=0; j<3; j++ ) - v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); - - buf[ count ].element_id = ptri[0]; - - int contact = rb_capsule__triangle( mtxA, c, tri, &buf[count] ); - count += contact; - - if( count >= 16 ){ - vg_warn("Exceeding capsule_vs_scene capacity. Geometry too dense!\n"); - return count; - } - } - - return count; -} - -static int rb_global_has_space( void ){ - if( rb_contact_count + 16 > vg_list_size(rb_contact_buffer) ) - return 0; - - return 1; -} - -static rb_ct *rb_global_buffer( void ){ - return &rb_contact_buffer[ rb_contact_count ]; -} - -/* - * ----------------------------------------------------------------------------- - * Dynamics - * ----------------------------------------------------------------------------- - */ - -static void rb_solver_reset(void){ - rb_contact_count = 0; -} - -static rb_ct *rb_global_ct(void){ - return rb_contact_buffer + rb_contact_count; -} - -static void rb_prepare_contact( rb_ct *ct, float timestep ){ - ct->bias = -0.2f * (timestep*3600.0f) - * vg_minf( 0.0f, -ct->p+k_penetration_slop ); - - v3_tangent_basis( ct->n, ct->t[0], ct->t[1] ); - ct->norm_impulse = 0.0f; - ct->tangent_impulse[0] = 0.0f; - ct->tangent_impulse[1] = 0.0f; -} - -/* calculate total move. manifold should belong to ONE object only */ -static void rb_depenetrate( rb_ct *manifold, int len, v3f dt ){ - v3_zero( dt ); - - for( int j=0; j<7; j++ ) - { - for( int i=0; in, dt ), - remaining = (ct->p-k_penetration_slop) - resolved_amt, - apply = vg_maxf( remaining, 0.0f ) * 0.4f; - - v3_muladds( dt, ct->n, apply, dt ); - } - } -} - -/* - * Initializing things like tangent vectors - */ -static void rb_presolve_contacts( rb_ct *buffer, int len ){ - for( int i=0; ico, ct->rba->co, ra ); - v3_sub( ct->co, ct->rbb->co, rb ); - v3_cross( ra, ct->n, raCn ); - v3_cross( rb, ct->n, rbCn ); - - /* orient inverse inertia tensors */ - v3f raCnI, rbCnI; - m3x3_mulv( ct->rba->iIw, raCn, raCnI ); - m3x3_mulv( ct->rbb->iIw, rbCn, rbCnI ); - - ct->normal_mass = ct->rba->inv_mass + ct->rbb->inv_mass; - ct->normal_mass += v3_dot( raCn, raCnI ); - ct->normal_mass += v3_dot( rbCn, rbCnI ); - ct->normal_mass = 1.0f/ct->normal_mass; - - for( int j=0; j<2; j++ ){ - v3f raCtI, rbCtI; - v3_cross( ct->t[j], ra, raCt ); - v3_cross( ct->t[j], rb, rbCt ); - m3x3_mulv( ct->rba->iIw, raCt, raCtI ); - m3x3_mulv( ct->rbb->iIw, rbCt, rbCtI ); - - ct->tangent_mass[j] = ct->rba->inv_mass + ct->rbb->inv_mass; - ct->tangent_mass[j] += v3_dot( raCt, raCtI ); - ct->tangent_mass[j] += v3_dot( rbCt, rbCtI ); - ct->tangent_mass[j] = 1.0f/ct->tangent_mass[j]; - } - - rb_debug_contact( ct ); - } -} - -/* - * Creates relative contact velocity vector - */ -static void rb_rcv( rigidbody *rba, rigidbody *rbb, v3f ra, v3f rb, v3f rv ){ - v3f rva, rvb; - v3_cross( rba->w, ra, rva ); - v3_add( rba->v, rva, rva ); - v3_cross( rbb->w, rb, rvb ); - v3_add( rbb->v, rvb, rvb ); - - v3_sub( rva, rvb, rv ); -} - -static void rb_contact_restitution( rb_ct *ct, float cr ){ - v3f rv, ra, rb; - v3_sub( ct->co, ct->rba->co, ra ); - v3_sub( ct->co, ct->rbb->co, rb ); - rb_rcv( ct->rba, ct->rbb, ra, rb, rv ); - - float v = v3_dot( rv, ct->n ); - - if( v < -1.0f ){ - ct->bias += -cr * v; - } -} - -/* - * Apply impulse to object - */ -static void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse ){ - /* linear */ - v3_muladds( rb->v, impulse, rb->inv_mass, rb->v ); - - /* Angular velocity */ - v3f wa; - v3_cross( delta, impulse, wa ); - - m3x3_mulv( rb->iIw, wa, wa ); - v3_add( rb->w, wa, rb->w ); -} - -/* - * One iteration to solve the contact constraint - */ -static void rb_solve_contacts( rb_ct *buf, int len ){ - for( int i=0; ico, ct->rba->co, ra ); - v3_sub( ct->co, ct->rbb->co, rb ); - rb_rcv( ct->rba, ct->rbb, ra, rb, rv ); - - /* Friction */ - for( int j=0; j<2; j++ ){ - float f = k_friction * ct->norm_impulse, - vt = v3_dot( rv, ct->t[j] ), - lambda = ct->tangent_mass[j] * -vt; - - float temp = ct->tangent_impulse[j]; - ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f ); - lambda = ct->tangent_impulse[j] - temp; - - v3f impulse; - v3_muls( ct->t[j], lambda, impulse ); - rb_linear_impulse( ct->rba, ra, impulse ); - - v3_muls( ct->t[j], -lambda, impulse ); - rb_linear_impulse( ct->rbb, rb, impulse ); - } - - /* Normal */ - rb_rcv( ct->rba, ct->rbb, ra, rb, rv ); - float vn = v3_dot( rv, ct->n ), - lambda = ct->normal_mass * (-vn + ct->bias); - - float temp = ct->norm_impulse; - ct->norm_impulse = vg_maxf( temp + lambda, 0.0f ); - lambda = ct->norm_impulse - temp; - - v3f impulse; - v3_muls( ct->n, lambda, impulse ); - rb_linear_impulse( ct->rba, ra, impulse ); - - v3_muls( ct->n, -lambda, impulse ); - rb_linear_impulse( ct->rbb, rb, impulse ); - } -} - -/* - * ----------------------------------------------------------------------------- - * Constraints - * ----------------------------------------------------------------------------- - */ - -static void rb_debug_position_constraints( rb_constr_pos *buffer, int len ){ - for( int i=0; irba, *rbb = constr->rbb; - - v3f wca, wcb; - m3x3_mulv( rba->to_world, constr->lca, wca ); - m3x3_mulv( rbb->to_world, constr->lcb, wcb ); - - v3f p0, p1; - v3_add( wca, rba->co, p0 ); - v3_add( wcb, rbb->co, p1 ); - vg_line_point( p0, 0.0025f, 0xff000000 ); - vg_line_point( p1, 0.0025f, 0xffffffff ); - vg_line2( p0, p1, 0xff000000, 0xffffffff ); - } -} - -static void rb_presolve_swingtwist_constraints( rb_constr_swingtwist *buf, - int len ){ - float size = 0.12f; - - for( int i=0; irba->to_world, st->conevx, vx ); - m3x3_mulv( st->rbb->to_world, st->conevxb, vxb ); - m3x3_mulv( st->rba->to_world, st->conevy, vy ); - m3x3_mulv( st->rbb->to_world, st->coneva, va ); - m4x3_mulv( st->rba->to_world, st->view_offset, center ); - v3_cross( vy, vx, axis ); - - /* Constraint violated ? */ - float fx = v3_dot( vx, va ), /* projection world */ - fy = v3_dot( vy, va ), - fn = v3_dot( va, axis ), - - rx = st->conevx[3], /* elipse radii */ - ry = st->conevy[3], - - lx = fx/rx, /* projection local (fn==lz) */ - ly = fy/ry; - - st->tangent_violation = ((lx*lx + ly*ly) > fn*fn) || (fn <= 0.0f); - if( st->tangent_violation ){ - /* Calculate a good position and the axis to solve on */ - v2f closest, tangent, - p = { fx/fabsf(fn), fy/fabsf(fn) }; - - closest_point_elipse( p, (v2f){rx,ry}, closest ); - tangent[0] = -closest[1] / (ry*ry); - tangent[1] = closest[0] / (rx*rx); - v2_normalize( tangent ); - - v3f v0, v1; - v3_muladds( axis, vx, closest[0], v0 ); - v3_muladds( v0, vy, closest[1], v0 ); - v3_normalize( v0 ); - - v3_muls( vx, tangent[0], v1 ); - v3_muladds( v1, vy, tangent[1], v1 ); - - v3_copy( v0, st->tangent_target ); - v3_copy( v1, st->tangent_axis ); - - /* calculate mass */ - v3f aIw, bIw; - m3x3_mulv( st->rba->iIw, st->tangent_axis, aIw ); - m3x3_mulv( st->rbb->iIw, st->tangent_axis, bIw ); - st->tangent_mass = 1.0f / (v3_dot( st->tangent_axis, aIw ) + - v3_dot( st->tangent_axis, bIw )); - - float angle = v3_dot( va, st->tangent_target ); - } - - v3f refaxis; - v3_cross( vy, va, refaxis ); /* our default rotation */ - v3_normalize( refaxis ); - - float angle = v3_dot( refaxis, vxb ); - st->axis_violation = fabsf(angle) < st->conet; - - if( st->axis_violation ){ - v3f dir_test; - v3_cross( refaxis, vxb, dir_test ); - - if( v3_dot(dir_test, va) < 0.0f ) - st->axis_violation = -st->axis_violation; - - float newang = (float)st->axis_violation * acosf(st->conet-0.0001f); - - v3f refaxis_up; - v3_cross( va, refaxis, refaxis_up ); - v3_muls( refaxis_up, sinf(newang), st->axis_target ); - v3_muladds( st->axis_target, refaxis, -cosf(newang), st->axis_target ); - - /* calculate mass */ - v3_copy( va, st->axis ); - v3f aIw, bIw; - m3x3_mulv( st->rba->iIw, st->axis, aIw ); - m3x3_mulv( st->rbb->iIw, st->axis, bIw ); - st->axis_mass = 1.0f / (v3_dot( st->axis, aIw ) + - v3_dot( st->axis, bIw )); - } - } -} - -static void rb_debug_swingtwist_constraints( rb_constr_swingtwist *buf, - int len ){ - float size = 0.12f; - - for( int i=0; irba->to_world, st->conevx, vx ); - m3x3_mulv( st->rbb->to_world, st->conevxb, vxb ); - m3x3_mulv( st->rba->to_world, st->conevy, vy ); - m3x3_mulv( st->rbb->to_world, st->coneva, va ); - m4x3_mulv( st->rba->to_world, st->view_offset, center ); - v3_cross( vy, vx, axis ); - - float rx = st->conevx[3], /* elipse radii */ - ry = st->conevy[3]; - - v3f p0, p1; - v3_muladds( center, va, size, p1 ); - vg_line( center, p1, 0xffffffff ); - vg_line_point( p1, 0.00025f, 0xffffffff ); - - if( st->tangent_violation ){ - v3_muladds( center, st->tangent_target, size, p0 ); - - vg_line( center, p0, 0xff00ff00 ); - vg_line_point( p0, 0.00025f, 0xff00ff00 ); - vg_line( p1, p0, 0xff000000 ); - } - - for( int x=0; x<32; x++ ){ - float t0 = ((float)x * (1.0f/32.0f)) * VG_TAUf, - t1 = (((float)x+1.0f) * (1.0f/32.0f)) * VG_TAUf, - c0 = cosf( t0 ), - s0 = sinf( t0 ), - c1 = cosf( t1 ), - s1 = sinf( t1 ); - - v3f v0, v1; - v3_muladds( axis, vx, c0*rx, v0 ); - v3_muladds( v0, vy, s0*ry, v0 ); - v3_muladds( axis, vx, c1*rx, v1 ); - v3_muladds( v1, vy, s1*ry, v1 ); - - v3_normalize( v0 ); - v3_normalize( v1 ); - - v3_muladds( center, v0, size, p0 ); - v3_muladds( center, v1, size, p1 ); - - u32 col0r = fabsf(c0) * 255.0f, - col0g = fabsf(s0) * 255.0f, - col1r = fabsf(c1) * 255.0f, - col1g = fabsf(s1) * 255.0f, - col = st->tangent_violation? 0xff0000ff: 0xff000000, - col0 = col | (col0r<<16) | (col0g << 8), - col1 = col | (col1r<<16) | (col1g << 8); - - vg_line2( center, p0, VG__NONE, col0 ); - vg_line2( p0, p1, col0, col1 ); - } - - /* Draw twist */ - v3_muladds( center, va, size, p0 ); - v3_muladds( p0, vxb, size, p1 ); - - vg_line( p0, p1, 0xff0000ff ); - - if( st->axis_violation ){ - v3_muladds( p0, st->axis_target, size*1.25f, p1 ); - vg_line( p0, p1, 0xffffff00 ); - vg_line_point( p1, 0.0025f, 0xffffff80 ); - } - - v3f refaxis; - v3_cross( vy, va, refaxis ); /* our default rotation */ - v3_normalize( refaxis ); - v3f refaxis_up; - v3_cross( va, refaxis, refaxis_up ); - float newang = acosf(st->conet-0.0001f); - - v3_muladds( p0, refaxis_up, sinf(newang)*size, p1 ); - v3_muladds( p1, refaxis, -cosf(newang)*size, p1 ); - vg_line( p0, p1, 0xff000000 ); - - v3_muladds( p0, refaxis_up, sinf(-newang)*size, p1 ); - v3_muladds( p1, refaxis, -cosf(-newang)*size, p1 ); - vg_line( p0, p1, 0xff404040 ); - } -} - -/* - * Solve a list of positional constraints - */ -static void rb_solve_position_constraints( rb_constr_pos *buf, int len ){ - for( int i=0; irba, *rbb = constr->rbb; - - v3f wa, wb; - m3x3_mulv( rba->to_world, constr->lca, wa ); - m3x3_mulv( rbb->to_world, constr->lcb, wb ); - - m3x3f ssra, ssrat, ssrb, ssrbt; - - m3x3_skew_symetric( ssrat, wa ); - m3x3_skew_symetric( ssrbt, wb ); - m3x3_transpose( ssrat, ssra ); - m3x3_transpose( ssrbt, ssrb ); - - v3f b, b_wa, b_wb, b_a, b_b; - m3x3_mulv( ssra, rba->w, b_wa ); - m3x3_mulv( ssrb, rbb->w, b_wb ); - v3_add( rba->v, b_wa, b ); - v3_sub( b, rbb->v, b ); - v3_sub( b, b_wb, b ); - v3_muls( b, -1.0f, b ); - - m3x3f invMa, invMb; - m3x3_diagonal( invMa, rba->inv_mass ); - m3x3_diagonal( invMb, rbb->inv_mass ); - - m3x3f ia, ib; - m3x3_mul( ssra, rba->iIw, ia ); - m3x3_mul( ia, ssrat, ia ); - m3x3_mul( ssrb, rbb->iIw, ib ); - m3x3_mul( ib, ssrbt, ib ); - - m3x3f cma, cmb; - m3x3_add( invMa, ia, cma ); - m3x3_add( invMb, ib, cmb ); - - m3x3f A; - m3x3_add( cma, cmb, A ); - - /* Solve Ax = b ( A^-1*b = x ) */ - v3f impulse; - m3x3f invA; - m3x3_inv( A, invA ); - m3x3_mulv( invA, b, impulse ); - - v3f delta_va, delta_wa, delta_vb, delta_wb; - m3x3f iwa, iwb; - m3x3_mul( rba->iIw, ssrat, iwa ); - m3x3_mul( rbb->iIw, ssrbt, iwb ); - - m3x3_mulv( invMa, impulse, delta_va ); - m3x3_mulv( invMb, impulse, delta_vb ); - m3x3_mulv( iwa, impulse, delta_wa ); - m3x3_mulv( iwb, impulse, delta_wb ); - - v3_add( rba->v, delta_va, rba->v ); - v3_add( rba->w, delta_wa, rba->w ); - v3_sub( rbb->v, delta_vb, rbb->v ); - v3_sub( rbb->w, delta_wb, rbb->w ); - } -} - -static void rb_solve_swingtwist_constraints( rb_constr_swingtwist *buf, - int len ){ - float size = 0.12f; - - for( int i=0; iaxis_violation ) - continue; - - float rv = v3_dot( st->axis, st->rbb->w ) - - v3_dot( st->axis, st->rba->w ); - - if( rv * (float)st->axis_violation > 0.0f ) - continue; - - v3f impulse, wa, wb; - v3_muls( st->axis, rv*st->axis_mass, impulse ); - m3x3_mulv( st->rba->iIw, impulse, wa ); - v3_add( st->rba->w, wa, st->rba->w ); - - v3_muls( impulse, -1.0f, impulse ); - m3x3_mulv( st->rbb->iIw, impulse, wb ); - v3_add( st->rbb->w, wb, st->rbb->w ); - - float rv2 = v3_dot( st->axis, st->rbb->w ) - - v3_dot( st->axis, st->rba->w ); - } - - for( int i=0; itangent_violation ) - continue; - - float rv = v3_dot( st->tangent_axis, st->rbb->w ) - - v3_dot( st->tangent_axis, st->rba->w ); - - if( rv > 0.0f ) - continue; - - v3f impulse, wa, wb; - v3_muls( st->tangent_axis, rv*st->tangent_mass, impulse ); - m3x3_mulv( st->rba->iIw, impulse, wa ); - v3_add( st->rba->w, wa, st->rba->w ); - - v3_muls( impulse, -1.0f, impulse ); - m3x3_mulv( st->rbb->iIw, impulse, wb ); - v3_add( st->rbb->w, wb, st->rbb->w ); - - float rv2 = v3_dot( st->tangent_axis, st->rbb->w ) - - v3_dot( st->tangent_axis, st->rba->w ); - } -} - -static void rb_solve_constr_angle( rigidbody *rba, rigidbody *rbb, - v3f ra, v3f rb ){ - m3x3f ssra, ssrb, ssrat, ssrbt; - m3x3f cma, cmb; - - m3x3_skew_symetric( ssrat, ra ); - m3x3_skew_symetric( ssrbt, rb ); - m3x3_transpose( ssrat, ssra ); - m3x3_transpose( ssrbt, ssrb ); - - m3x3_mul( ssra, rba->iIw, cma ); - m3x3_mul( cma, ssrat, cma ); - m3x3_mul( ssrb, rbb->iIw, cmb ); - m3x3_mul( cmb, ssrbt, cmb ); - - m3x3f A, invA; - m3x3_add( cma, cmb, A ); - m3x3_inv( A, invA ); - - v3f b_wa, b_wb, b; - m3x3_mulv( ssra, rba->w, b_wa ); - m3x3_mulv( ssrb, rbb->w, b_wb ); - v3_add( b_wa, b_wb, b ); - v3_negate( b, b ); - - v3f impulse; - m3x3_mulv( invA, b, impulse ); - - v3f delta_wa, delta_wb; - m3x3f iwa, iwb; - m3x3_mul( rba->iIw, ssrat, iwa ); - m3x3_mul( rbb->iIw, ssrbt, iwb ); - m3x3_mulv( iwa, impulse, delta_wa ); - m3x3_mulv( iwb, impulse, delta_wb ); - v3_add( rba->w, delta_wa, rba->w ); - v3_sub( rbb->w, delta_wb, rbb->w ); -} - -/* - * Correct position constraint drift errors - * [ 0.0 <= amt <= 1.0 ]: the correction amount - */ -static void rb_correct_position_constraints( rb_constr_pos *buf, int len, - float amt ){ - for( int i=0; irba, *rbb = constr->rbb; - - v3f p0, p1, d; - m3x3_mulv( rba->to_world, constr->lca, p0 ); - m3x3_mulv( rbb->to_world, constr->lcb, p1 ); - v3_add( rba->co, p0, p0 ); - v3_add( rbb->co, p1, p1 ); - v3_sub( p1, p0, d ); - - v3_muladds( rbb->co, d, -1.0f * amt, rbb->co ); - rb_update_transform( rbb ); - } -} - -static void rb_correct_swingtwist_constraints( rb_constr_swingtwist *buf, - int len, float amt ){ - for( int i=0; itangent_violation ) - continue; - - v3f va; - m3x3_mulv( st->rbb->to_world, st->coneva, va ); - - float angle = v3_dot( va, st->tangent_target ); - - if( fabsf(angle) < 0.9999f ){ - v3f axis; - v3_cross( va, st->tangent_target, axis ); - - v4f correction; - q_axis_angle( correction, axis, acosf(angle) * amt ); - q_mul( correction, st->rbb->q, st->rbb->q ); - rb_update_transform( st->rbb ); - } - } - - for( int i=0; iaxis_violation ) - continue; - - v3f vxb; - m3x3_mulv( st->rbb->to_world, st->conevxb, vxb ); - - float angle = v3_dot( vxb, st->axis_target ); - - if( fabsf(angle) < 0.9999f ){ - v3f axis; - v3_cross( vxb, st->axis_target, axis ); - - v4f correction; - q_axis_angle( correction, axis, acosf(angle) * amt ); - q_mul( correction, st->rbb->q, st->rbb->q ); - rb_update_transform( st->rbb ); - } - } -} - -static void rb_correct_contact_constraints( rb_ct *buf, int len, float amt ){ - for( int i=0; irba, - *rbb = ct->rbb; - - f32 mass_total = 1.0f / (rba->inv_mass + rbb->inv_mass), - d = ct->p*mass_total*amt; - - v3_muladds( rba->co, ct->n, -d * rba->inv_mass, rba->co ); - v3_muladds( rbb->co, ct->n, d * rbb->inv_mass, rbb->co ); - } -} - - -/* - * Effectors - */ - -static void rb_effect_simple_bouyency( rigidbody *ra, v4f plane, - float amt, float drag ){ - /* float */ - float depth = v3_dot( plane, ra->co ) - plane[3], - lambda = vg_clampf( -depth, 0.0f, 1.0f ) * amt; - - v3_muladds( ra->v, plane, lambda * k_rb_delta, ra->v ); - - if( depth < 0.0f ) - v3_muls( ra->v, 1.0f-(drag*k_rb_delta), ra->v ); -} - -/* apply a spring&dampener force to match ra(worldspace) on rigidbody, to - * rt(worldspace) - */ -static void rb_effect_spring_target_vector( rigidbody *rba, v3f ra, v3f rt, - float spring, float dampening, - float timestep ){ - float d = v3_dot( rt, ra ); - float a = acosf( vg_clampf( d, -1.0f, 1.0f ) ); - - v3f axis; - v3_cross( rt, ra, axis ); - - float Fs = -a * spring, - Fd = -v3_dot( rba->w, axis ) * dampening; - - v3_muladds( rba->w, axis, (Fs+Fd) * timestep, rba->w ); -} - -#endif /* RIGIDBODY_H */ diff --git a/scene_rigidbody.h b/scene_rigidbody.h new file mode 100644 index 0000000..57ff1ff --- /dev/null +++ b/scene_rigidbody.h @@ -0,0 +1,247 @@ +#pragma once + +/* + * Copyright (C) 2021-2024 Mt.ZERO Software - All Rights Reserved + * + * Describes intereactions between vg rigidbody objects and skaterift's scene + * description + */ + +#include "scene.h" +#include "vg/vg_rigidbody.h" +#include "vg/vg_rigidbody_collision.h" + +static int rb_sphere__scene( m4x3f mtxA, f32 r, + m4x3f mtxB, bh_tree *scene_bh, rb_ct *buf, + u16 ignore ){ + scene_context *sc = scene_bh->user; + + int count = 0; + + boxf box; + v3_sub( mtxA[3], (v3f){ r,r,r }, box[0] ); + v3_add( mtxA[3], (v3f){ r,r,r }, box[1] ); + + bh_iter it; + i32 idx; + bh_iter_init_box( 0, &it, box ); + + while( bh_next( scene_bh, &it, &idx ) ){ + u32 *ptri = &sc->arrindices[ idx*3 ]; + v3f tri[3]; + + if( sc->arrvertices[ptri[0]].flags & ignore ) continue; + + for( int j=0; j<3; j++ ) + v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); + + buf[ count ].element_id = ptri[0]; + + vg_line( tri[0],tri[1],0x70ff6000 ); + vg_line( tri[1],tri[2],0x70ff6000 ); + vg_line( tri[2],tri[0],0x70ff6000 ); + + int contact = rb_sphere__triangle( mtxA, r, tri, &buf[count] ); + count += contact; + + if( count == 16 ){ + vg_warn( "Exceeding sphere_vs_scene capacity. Geometry too dense!\n" ); + return count; + } + } + + return count; +} + +static int rb_box__scene( m4x3f mtxA, boxf bbx, + m4x3f mtxB, bh_tree *scene_bh, + rb_ct *buf, u16 ignore ){ + scene_context *sc = scene_bh->user; + v3f tri[3]; + + v3f extent, center; + v3_sub( bbx[1], bbx[0], extent ); + v3_muls( extent, 0.5f, extent ); + v3_add( bbx[0], extent, center ); + + f32 r = v3_length(extent); + boxf world_bbx; + v3_fill( world_bbx[0], -r ); + v3_fill( world_bbx[1], r ); + for( int i=0; i<2; i++ ){ + v3_add( center, world_bbx[i], world_bbx[i] ); + v3_add( mtxA[3], world_bbx[i], world_bbx[i] ); + } + + m4x3f to_local; + m4x3_invert_affine( mtxA, to_local ); + + bh_iter it; + bh_iter_init_box( 0, &it, world_bbx ); + int idx; + int count = 0; + + vg_line_boxf( world_bbx, VG__RED ); + + while( bh_next( scene_bh, &it, &idx ) ){ + u32 *ptri = &sc->arrindices[ idx*3 ]; + if( sc->arrvertices[ptri[0]].flags & ignore ) continue; + + for( int j=0; j<3; j++ ) + v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); + + if( rb_box_triangle_sat( extent, center, to_local, tri ) ){ + vg_line(tri[0],tri[1],0xff50ff00 ); + vg_line(tri[1],tri[2],0xff50ff00 ); + vg_line(tri[2],tri[0],0xff50ff00 ); + } + else{ + vg_line(tri[0],tri[1],0xff0000ff ); + vg_line(tri[1],tri[2],0xff0000ff ); + vg_line(tri[2],tri[0],0xff0000ff ); + continue; + } + + v3f v0,v1,n; + v3_sub( tri[1], tri[0], v0 ); + v3_sub( tri[2], tri[0], v1 ); + v3_cross( v0, v1, n ); + + if( v3_length2( n ) <= 0.00001f ){ +#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING + vg_error( "Zero area triangle!\n" ); +#endif + return 0; + } + + v3_normalize( n ); + + /* find best feature */ + f32 best = v3_dot( mtxA[0], n ); + int axis = 0; + + for( int i=1; i<3; i++ ){ + f32 c = v3_dot( mtxA[i], n ); + + if( fabsf(c) > fabsf(best) ){ + best = c; + axis = i; + } + } + + v3f manifold[4]; + + if( axis == 0 ){ + f32 px = best > 0.0f? bbx[0][0]: bbx[1][0]; + manifold[0][0] = px; + manifold[0][1] = bbx[0][1]; + manifold[0][2] = bbx[0][2]; + manifold[1][0] = px; + manifold[1][1] = bbx[1][1]; + manifold[1][2] = bbx[0][2]; + manifold[2][0] = px; + manifold[2][1] = bbx[1][1]; + manifold[2][2] = bbx[1][2]; + manifold[3][0] = px; + manifold[3][1] = bbx[0][1]; + manifold[3][2] = bbx[1][2]; + } + else if( axis == 1 ){ + f32 py = best > 0.0f? bbx[0][1]: bbx[1][1]; + manifold[0][0] = bbx[0][0]; + manifold[0][1] = py; + manifold[0][2] = bbx[0][2]; + manifold[1][0] = bbx[1][0]; + manifold[1][1] = py; + manifold[1][2] = bbx[0][2]; + manifold[2][0] = bbx[1][0]; + manifold[2][1] = py; + manifold[2][2] = bbx[1][2]; + manifold[3][0] = bbx[0][0]; + manifold[3][1] = py; + manifold[3][2] = bbx[1][2]; + } + else{ + f32 pz = best > 0.0f? bbx[0][2]: bbx[1][2]; + manifold[0][0] = bbx[0][0]; + manifold[0][1] = bbx[0][1]; + manifold[0][2] = pz; + manifold[1][0] = bbx[1][0]; + manifold[1][1] = bbx[0][1]; + manifold[1][2] = pz; + manifold[2][0] = bbx[1][0]; + manifold[2][1] = bbx[1][1]; + manifold[2][2] = pz; + manifold[3][0] = bbx[0][0]; + manifold[3][1] = bbx[1][1]; + manifold[3][2] = pz; + } + + for( int j=0; j<4; j++ ) + m4x3_mulv( mtxA, manifold[j], manifold[j] ); + + vg_line( manifold[0], manifold[1], 0xffffffff ); + vg_line( manifold[1], manifold[2], 0xffffffff ); + vg_line( manifold[2], manifold[3], 0xffffffff ); + vg_line( manifold[3], manifold[0], 0xffffffff ); + + for( int j=0; j<4; j++ ){ + rb_ct *ct = buf+count; + + v3_copy( manifold[j], ct->co ); + v3_copy( n, ct->n ); + + f32 l0 = v3_dot( tri[0], n ), + l1 = v3_dot( manifold[j], n ); + + ct->p = (l0-l1)*0.5f; + if( ct->p < 0.0f ) + continue; + + ct->type = k_contact_type_default; + count ++; + + if( count >= 12 ) + return count; + } + } + return count; +} + +/* mtxB is defined only for tradition; it is not used currently */ +static int rb_capsule__scene( m4x3f mtxA, rb_capsule *c, + m4x3f mtxB, bh_tree *scene_bh, + rb_ct *buf, u16 ignore ){ + int count = 0; + + boxf bbx; + v3_sub( mtxA[3], (v3f){ c->h, c->h, c->h }, bbx[0] ); + v3_add( mtxA[3], (v3f){ c->h, c->h, c->h }, bbx[1] ); + + scene_context *sc = scene_bh->user; + + bh_iter it; + bh_iter_init_box( 0, &it, bbx ); + i32 idx; + while( bh_next( scene_bh, &it, &idx ) ){ + u32 *ptri = &sc->arrindices[ idx*3 ]; + if( sc->arrvertices[ptri[0]].flags & ignore ) continue; + + v3f tri[3]; + for( int j=0; j<3; j++ ) + v3_copy( sc->arrvertices[ptri[j]].co, tri[j] ); + + buf[ count ].element_id = ptri[0]; + + int contact = rb_capsule__triangle( mtxA, c, tri, &buf[count] ); + count += contact; + + if( count >= 16 ){ + vg_warn("Exceeding capsule_vs_scene capacity. Geometry too dense!\n"); + return count; + } + } + + return count; +} + diff --git a/shaders/model.vs b/shaders/model.vs index 350a821..a23529d 100644 --- a/shaders/model.vs +++ b/shaders/model.vs @@ -29,6 +29,6 @@ void main() aWorldCo = world_pos0; aColour = a_colour; aUv = a_uv; - aNorm = mat3(uMdl) * a_norm; + aNorm = normalize( mat3(uMdl) * a_norm ); aCo = a_co; } diff --git a/shaders/model_board_view.h b/shaders/model_board_view.h index a50e9d5..abbc42f 100644 --- a/shaders/model_board_view.h +++ b/shaders/model_board_view.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_board_view = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_entity.h b/shaders/model_entity.h index 8b842ae..a970038 100644 --- a/shaders/model_entity.h +++ b/shaders/model_entity.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_entity = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_gate.h b/shaders/model_gate.h index 31329ab..1bc558f 100644 --- a/shaders/model_gate.h +++ b/shaders/model_gate.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_gate = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_gate_unlinked.h b/shaders/model_gate_unlinked.h index 1fda296..449322a 100644 --- a/shaders/model_gate_unlinked.h +++ b/shaders/model_gate_unlinked.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_gate_unlinked = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_menu.h b/shaders/model_menu.h index 1d17aa0..0ee6ad3 100644 --- a/shaders/model_menu.h +++ b/shaders/model_menu.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_menu = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_sky.h b/shaders/model_sky.h index f8f88d3..88612b2 100644 --- a/shaders/model_sky.h +++ b/shaders/model_sky.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_sky = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/model_sky_space.h b/shaders/model_sky_space.h index 8309550..c96f2c4 100644 --- a/shaders/model_sky_space.h +++ b/shaders/model_sky_space.h @@ -58,7 +58,7 @@ static struct vg_shader _shader_model_sky_space = { " aWorldCo = world_pos0;\n" " aColour = a_colour;\n" " aUv = a_uv;\n" -" aNorm = mat3(uMdl) * a_norm;\n" +" aNorm = normalize( mat3(uMdl) * a_norm );\n" " aCo = a_co;\n" "}\n" ""}, diff --git a/shaders/scene_water_fast.fs b/shaders/scene_water_fast.fs index 77aa569..f89cee5 100644 --- a/shaders/scene_water_fast.fs +++ b/shaders/scene_water_fast.fs @@ -55,4 +55,5 @@ void main() vec4 vsurface = water_surf( halfview, surfnorm, depthvalue ); vsurface.a -= fdist; oColour = mix( vsurface, vec4(1.0,1.0,1.0,0.5), fband ); + oColour.rgb = scene_compute_lighting( oColour.rgb, aNorm.xyz, aWorldCo ); } diff --git a/shaders/scene_water_fast.h b/shaders/scene_water_fast.h index 7c5a079..ac987dd 100644 --- a/shaders/scene_water_fast.h +++ b/shaders/scene_water_fast.h @@ -510,6 +510,7 @@ static struct vg_shader _shader_scene_water_fast = { " vec4 vsurface = water_surf( halfview, surfnorm, depthvalue );\n" " vsurface.a -= fdist;\n" " oColour = mix( vsurface, vec4(1.0,1.0,1.0,0.5), fband );\n" +" oColour.rgb = scene_compute_lighting( oColour.rgb, aNorm.xyz, aWorldCo );\n" "}\n" ""}, }; diff --git a/shaders/trail.fs b/shaders/trail.fs new file mode 100644 index 0000000..e6ba5a8 --- /dev/null +++ b/shaders/trail.fs @@ -0,0 +1,18 @@ +layout (location = 0) out vec4 oColour; +in float aAlpha; +uniform vec4 uColour; + +#include "motion_vectors_fs.glsl" + +void main(){ + compute_motion_vectors(); + + vec2 ssuv = gl_FragCoord.xy; + vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) ); + float dither = fract( vDither.g / 71.0 ) - 0.5; + + if( aAlpha+dither<0.5 ) + discard; + + oColour = vec4( uColour.rgb, uColour.a * aAlpha ); +} diff --git a/shaders/trail.h b/shaders/trail.h new file mode 100644 index 0000000..ee6090c --- /dev/null +++ b/shaders/trail.h @@ -0,0 +1,113 @@ +#ifndef SHADER_trail_H +#define SHADER_trail_H +static void shader_trail_link(void); +static void shader_trail_register(void); +static struct vg_shader _shader_trail = { + .name = "trail", + .link = shader_trail_link, + .vs = +{ +.orig_file = "shaders/trail.vs", +.static_src = +"layout (location=0) in vec4 a_co;\n" +"\n" +"#line 1 1 \n" +"const float k_motion_lerp_amount = 0.01;\n" +"\n" +"#line 2 0 \n" +"\n" +"out vec3 aMotionVec0;\n" +"out vec3 aMotionVec1;\n" +"\n" +"void vs_motion_out( vec4 vproj0, vec4 vproj1 )\n" +"{\n" +" // This magically solves some artifacting errors!\n" +" //\n" +" vproj1 = vproj0*(1.0-k_motion_lerp_amount) + vproj1*k_motion_lerp_amount;\n" +"\n" +" aMotionVec0 = vec3( vproj0.xy, vproj0.w );\n" +" aMotionVec1 = vec3( vproj1.xy, vproj1.w );\n" +"}\n" +"\n" +"#line 4 0 \n" +"\n" +"uniform mat4 uPv;\n" +"uniform mat4 uPvPrev;\n" +"\n" +"out float aAlpha;\n" +"\n" +"void main(){\n" +" vec4 vproj0 = uPv * vec4( a_co.xyz, 1.0 );\n" +" vec4 vproj1 = uPvPrev * vec4( a_co.xyz, 1.0 );\n" +" vs_motion_out( vproj0, vproj1 );\n" +"\n" +" gl_Position = vproj0;\n" +" aAlpha = a_co.w;\n" +"}\n" +""}, + .fs = +{ +.orig_file = "shaders/trail.fs", +.static_src = +"layout (location = 0) out vec4 oColour;\n" +"in float aAlpha;\n" +"uniform vec4 uColour;\n" +"\n" +"#line 1 1 \n" +"const float k_motion_lerp_amount = 0.01;\n" +"\n" +"#line 2 0 \n" +"\n" +"layout (location = 1) out vec2 oMotionVec;\n" +"\n" +"in vec3 aMotionVec0;\n" +"in vec3 aMotionVec1;\n" +"\n" +"void compute_motion_vectors()\n" +"{\n" +" // Write motion vectors\n" +" vec2 vmotion0 = aMotionVec0.xy / aMotionVec0.z;\n" +" vec2 vmotion1 = aMotionVec1.xy / aMotionVec1.z;\n" +"\n" +" oMotionVec = (vmotion1-vmotion0) * (1.0/k_motion_lerp_amount);\n" +"}\n" +"\n" +"#line 6 0 \n" +"\n" +"void main(){\n" +" compute_motion_vectors();\n" +"\n" +" vec2 ssuv = gl_FragCoord.xy;\n" +" vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) );\n" +" float dither = fract( vDither.g / 71.0 ) - 0.5;\n" +"\n" +" if( aAlpha+dither<0.5 )\n" +" discard;\n" +"\n" +" oColour = vec4( uColour.rgb, uColour.a * aAlpha );\n" +"}\n" +""}, +}; + +static GLuint _uniform_trail_uPv; +static GLuint _uniform_trail_uPvPrev; +static GLuint _uniform_trail_uColour; +static void shader_trail_uPv(m4x4f m){ + glUniformMatrix4fv(_uniform_trail_uPv,1,GL_FALSE,(float*)m); +} +static void shader_trail_uPvPrev(m4x4f m){ + glUniformMatrix4fv(_uniform_trail_uPvPrev,1,GL_FALSE,(float*)m); +} +static void shader_trail_uColour(v4f v){ + glUniform4fv(_uniform_trail_uColour,1,v); +} +static void shader_trail_register(void){ + vg_shader_register( &_shader_trail ); +} +static void shader_trail_use(void){ glUseProgram(_shader_trail.id); } +static void shader_trail_link(void){ + _uniform_trail_uPv = glGetUniformLocation( _shader_trail.id, "uPv" ); + _uniform_trail_uPvPrev = glGetUniformLocation( _shader_trail.id, "uPvPrev" ); + _uniform_trail_uColour = glGetUniformLocation( _shader_trail.id, "uColour" ); +} +#endif /* SHADER_trail_H */ diff --git a/shaders/trail.vs b/shaders/trail.vs new file mode 100644 index 0000000..827d160 --- /dev/null +++ b/shaders/trail.vs @@ -0,0 +1,17 @@ +layout (location=0) in vec4 a_co; + +#include "motion_vectors_vs.glsl" + +uniform mat4 uPv; +uniform mat4 uPvPrev; + +out float aAlpha; + +void main(){ + vec4 vproj0 = uPv * vec4( a_co.xyz, 1.0 ); + vec4 vproj1 = uPvPrev * vec4( a_co.xyz, 1.0 ); + vs_motion_out( vproj0, vproj1 ); + + gl_Position = vproj0; + aAlpha = a_co.w; +} diff --git a/skaterift.c b/skaterift.c index be4084e..eec32d5 100644 --- a/skaterift.c +++ b/skaterift.c @@ -2,7 +2,7 @@ * ============================================================================= * * Copyright . . . -----, ,----- ,---. .---. - * 2021-2023 |\ /| | / | | | | /| + * 2021-2024 |\ /| | / | | | | /| * | \ / | +-- / +----- +---' | / | * | \ / | | / | | \ | / | * | \/ | | / | | \ | / | @@ -14,6 +14,7 @@ #define SR_NETWORKED #define VG_AUDIO_FORCE_COMPRESSED #define SDL_MAIN_HANDLED +#define SR_ALLOW_REWIND_HUB #ifndef VG_RELEASE #define VG_DEVWINDOW @@ -57,6 +58,9 @@ #include "particle.c" #include "player_effects.c" #include "freecam.c" +#include "testing.c" +#include "trail.h" +#include "trail.c" static int k_tools_mode = 0; @@ -230,6 +234,9 @@ static void vg_load(void){ vg_loader_step( workshop_init, NULL ); vg_loader_step( skateshop_init, NULL ); vg_loader_step( ent_tornado_init, NULL ); + vg_loader_step( testing_init, NULL ); + vg_loader_step( trail_init, NULL ); + vg_loader_step( particle_init, NULL ); vg_loader_step( skaterift_load_player_content, NULL ); @@ -325,6 +332,7 @@ static void vg_fixed_update(void){ world_routes_fixedupdate( world_current_instance() ); player__update(); vehicle_update_fixed(); + testing_update(); } static void vg_post_update(void){ @@ -509,6 +517,8 @@ static void render_scene(void){ particle_system_prerender( &particles_env ); particle_system_render( &particles_env, &skaterift.cam ); + player_glide_render_effects( &skaterift.cam ); + /* * render transition */ @@ -679,6 +689,35 @@ static void vg_render(void){ static void vg_gui(void){ if( skaterift.op == k_async_op_clientloading ) return; + if( k_tools_mode ){ + ui_rect null; + ui_rect screen = { 0, 0, vg.window_x, vg.window_y }; + ui_rect panel = { 0, 0, 1000, 700 }; + ui_rect_center( screen, panel ); + vg_ui.wants_mouse = 1; + + ui_fill( panel, ui_colour( k_ui_bg+1 ) ); + ui_outline( panel, 1, ui_colour( k_ui_bg+7 ), 0 ); + + ui_rect_pad( panel, (ui_px[2]){ 8, 8 } ); + + static i32 example; + ui_enum( panel, "example enum", vg_settings_vsync_enum, 3, &example ); + + static f32 val = 30.0f; + ui_slider( panel, "Slider", 0.0f, 100.0f, &val, NULL ); + + ui_dev_colourview(); + + static v4f colour = { 0.5f, 0.6f, 0.1f, 1.0f }; + ui_colourpicker( panel, "hello", colour ); + + return; + } + + if( k_light_editor ) + imgui_world_light_edit( world_current_instance() ); + vg_ui.tex_bg = gpipeline.fb_main->attachments[0].id; render_fb_inverse_ratio( gpipeline.fb_main, vg_ui.bg_inverse_ratio ); diff --git a/skaterift.h b/skaterift.h index a21e6ab..ebab767 100644 --- a/skaterift.h +++ b/skaterift.h @@ -11,6 +11,7 @@ #include "vg/vg.h" #include "world.h" #include "addon.h" +#include "trail.h" enum skaterift_rt { k_skaterift_rt_workshop_preview, @@ -69,10 +70,15 @@ struct{ audio_channel *aud_air; const char *hub_world; + + struct trail_system test_trail; } static skaterift = { .op = k_async_op_clientloading, .time_rate = 1.0f, .demo_mode = 1, - .hub_world = "maps/dev_hub" + .hub_world = "maps/dev_hub", + .test_trail = { + .max = 80 + } }; /* Skaterift api */ diff --git a/testing.c b/testing.c new file mode 100644 index 0000000..103f62c --- /dev/null +++ b/testing.c @@ -0,0 +1,63 @@ +#pragma once +#include "vg/vg_m.h" +#include "vg/vg_rigidbody.h" +#include "scene_rigidbody.h" + +struct { + rigidbody rb; + boxf box; +} +static baller = { + .rb.q = { 0,0,0,1 }, + .box = {{ -0.1f, -0.2f, -0.1f }, + { 0.1f, 1.0f, 0.1f }}, +}; + +static void testing_update(void){ + if( !vg_console.cheats ) + return; + + if( vg_getkey( SDLK_9 ) ){ + v3_add( localplayer.rb.co, (v3f){0,1,0}, baller.rb.co ); + v3_zero( baller.rb.w ); + v3_zero( baller.rb.v ); + q_identity( baller.rb.q ); + rb_update_matrices( &baller.rb ); + } + + if( vg_getkey( SDLK_8 ) ){ + localplayer.have_glider = 1; + localplayer.glider_orphan = 0; + player_glide.t = -1.0f; + } + + vg_line_boxf_transformed( baller.rb.to_world, baller.box, VG__RED ); + + world_instance *world = world_current_instance(); + + rigidbody _null = {0}; + _null.inv_mass = 0.0f; + m3x3_zero( _null.iI ); + + rb_solver_reset(); + rb_ct *buf = rb_global_buffer(); + rb_contact_count += rb_box__scene( baller.rb.to_world, baller.box, + NULL, world->geo_bh, buf, + k_material_flag_ghosts ); + for( u32 j=0; jhead ++; + + if( sys->head == sys->max ) + sys->head = 0; + + /* undesirable effect: will remove active points if out of space! */ + if( sys->count < sys->max ) + sys->count ++; +} + +static void trail_system_update( trail_system *sys, f32 dt, + v3f co, v3f normal, f32 alpha ){ + /* update existing points and clip dead ones */ + bool clip_allowed = 1; + for( i32 i=0; icount; i ++ ){ + i32 i0 = sys->head - sys->count + i; + if( i0 < 0 ) i0 += sys->max; + + trail_point *p0 = &sys->array[i0]; + p0->alpha -= dt/sys->lifetime; + + if( clip_allowed ){ + if( p0->alpha <= 0.0f ) + sys->count --; + else + clip_allowed = 0; + } + } + + i32 icur = sys->head -1, + iprev = sys->head -2, + ihead = sys->head; + + if( icur < 0 ) icur += sys->max; + if( iprev < 0 ) iprev += sys->max; + + trail_point *pcur = &sys->array[ icur ], + *pprev = &sys->array[ iprev ], + *phead = &sys->array[ ihead ], + *pdest = NULL; + v3f dir; + + f32 k_min = 0.001f; + + if( sys->count == 0 ){ + trail_increment( sys ); + v3_copy( (v3f){0,0,-1}, dir ); + pdest = phead; + } + else if( sys->count == 1 ){ + if( v3_dist2( pcur->co, co ) < k_min*k_min ) + return; + + trail_increment( sys ); + pdest = phead; + v3_sub( co, pcur->co, dir ); + } + else { + if( v3_dist2( pprev->co, co ) < k_min*k_min ) + return; + + if( v3_dist2( pprev->co, co ) > sys->min_dist*sys->min_dist ){ + trail_increment( sys ); + pdest = phead; + } + else + pdest = pcur; + + v3_sub( co, pprev->co, dir ); + } + + v3_cross( dir, normal, pdest->right ); + v3_normalize( pdest->right ); + v3_copy( co, pdest->co ); + v3_copy( normal, pdest->normal ); + pdest->alpha = alpha; +} + +static void trail_system_debug( trail_system *sys ){ + for( i32 i=0; icount; i ++ ){ + i32 i0 = sys->head - sys->count + i; + if( i0 < 0 ) i0 += sys->max; + + trail_point *p0 = &sys->array[i0]; + vg_line_point( p0->co, 0.04f, 0xff000000 | (u32)(p0->alpha*255.0f) ); + vg_line_arrow( p0->co, p0->right, 0.3f, VG__GREEN ); + + if( i == sys->count-1 ) break; + + i32 i1 = i0+1; + if( i1 == sys->max ) i1 = 0; + + trail_point *p1 = &sys->array[i1]; + vg_line( p0->co, p1->co, VG__RED ); + } +} + +struct trail_init_args { + trail_system *sys; +}; + +static void async_trail_init( void *payload, u32 size ){ + struct trail_init_args *args = payload; + trail_system *sys = args->sys; + + glGenVertexArrays( 1, &sys->vao ); + glGenBuffers( 1, &sys->vbo ); + glBindVertexArray( sys->vao ); + + size_t stride = sizeof(trail_vert); + + glBindBuffer( GL_ARRAY_BUFFER, sys->vbo ); + glBufferData( GL_ARRAY_BUFFER, sys->max*stride*2, NULL, GL_DYNAMIC_DRAW ); + + /* 0: coordinates */ + glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, stride, (void*)0 ); + glEnableVertexAttribArray( 0 ); + + VG_CHECK_GL_ERR(); +} + +static void trail_alloc( trail_system *sys, u32 max ){ + size_t stride = sizeof(trail_vert); + sys->max = max; + sys->array = vg_linear_alloc( vg_mem.rtmemory, max*sizeof(trail_point) ); + sys->vertices = vg_linear_alloc( vg_mem.rtmemory, max*stride*2 ); + + vg_async_item *call = vg_async_alloc( sizeof(struct trail_init_args) ); + + struct trail_init_args *init = call->payload; + init->sys = sys; + vg_async_dispatch( call, async_trail_init ); +} + +static void trail_system_prerender( trail_system *sys ){ + if( sys->count < 2 ) return; + + for( i32 i=0; icount; i ++ ){ + i32 i0 = sys->head - sys->count + i; + if( i0 < 0 ) i0 += sys->max; + + trail_point *p0 = &sys->array[i0]; + trail_vert *v0 = &sys->vertices[i*2+0], + *v1 = &sys->vertices[i*2+1]; + + v3_muladds( p0->co, p0->right, -sys->width, v0->co ); + v3_muladds( p0->co, p0->right, sys->width, v1->co ); + v0->co[3] = p0->alpha; + v1->co[3] = p0->alpha; + } + + glBindVertexArray( sys->vao ); + + size_t stride = sizeof(trail_vert); + glBindBuffer( GL_ARRAY_BUFFER, sys->vbo ); + glBufferSubData( GL_ARRAY_BUFFER, 0, sys->count*stride*2, sys->vertices ); +} + +static void trail_system_render( trail_system *sys, camera *cam ){ + if( sys->count < 2 ) return; + glDisable( GL_CULL_FACE ); + glEnable( GL_DEPTH_TEST ); + + shader_trail_use(); + shader_trail_uPv( cam->mtx.pv ); + shader_trail_uPvPrev( cam->mtx_prev.pv ); + shader_trail_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} ); + + glBindVertexArray( sys->vao ); + glDrawArrays( GL_TRIANGLE_STRIP, 0, sys->count*2 ); +} + +static void trail_init( void ){ + shader_trail_register(); +} diff --git a/trail.h b/trail.h new file mode 100644 index 0000000..58bcb93 --- /dev/null +++ b/trail.h @@ -0,0 +1,38 @@ +#ifndef TRAIL_H +#define TRAIL_H + +#include "skaterift.h" + +typedef struct trail_system trail_system; +typedef struct trail_point trail_point; +typedef struct trail_vert trail_vert; + +struct trail_system { + struct trail_point { + v3f co, normal, right; + f32 alpha; + } + *array; + +#pragma pack(push,1) + struct trail_vert { + v4f co; /* xyz: position, w: alpha */ + } + *vertices; +#pragma pack(pop) + + i32 head, count, max; + GLuint vao, vbo; + + /* render settings */ + f32 width, lifetime, min_dist; +}; + +static void trail_alloc( trail_system *sys, u32 max ); +static void trail_system_update( trail_system *sys, f32 dt, + v3f co, v3f normal, f32 alpha ); +static void trail_system_debug( trail_system *sys ); +static void trail_system_prerender( trail_system *sys ); +static void trail_system_render( trail_system *sys, camera *cam ); + +#endif /* TRAIL_H */ diff --git a/vehicle.c b/vehicle.c index 3d68d11..3555a97 100644 --- a/vehicle.c +++ b/vehicle.c @@ -9,16 +9,16 @@ static int spawn_car( int argc, const char *argv[] ){ v3_muladds( ra, skaterift.cam.transform[2], -10.0f, rb ); float t; - if( spherecast_world( world_current_instance(), ra, rb, - gzoomer.obj.inf.sphere.radius, &t, rx, 0 ) != -1 ) + if( spherecast_world( world_current_instance(), + ra, rb, 1.0f, &t, rx, 0 ) != -1 ) { - v3_lerp( ra, rb, t, gzoomer.obj.rb.co ); - gzoomer.obj.rb.co[1] += 4.0f; - q_axis_angle( gzoomer.obj.rb.q, (v3f){1.0f,0.0f,0.0f}, 0.001f ); - v3_zero( gzoomer.obj.rb.v ); - v3_zero( gzoomer.obj.rb.w ); + v3_lerp( ra, rb, t, gzoomer.rb.co ); + gzoomer.rb.co[1] += 4.0f; + q_axis_angle( gzoomer.rb.q, (v3f){1.0f,0.0f,0.0f}, 0.001f ); + v3_zero( gzoomer.rb.v ); + v3_zero( gzoomer.rb.w ); - rb_update_transform( &gzoomer.obj.rb ); + rb_update_matrices( &gzoomer.rb ); gzoomer.alive = 1; vg_success( "Spawned car\n" ); @@ -30,13 +30,12 @@ static int spawn_car( int argc, const char *argv[] ){ return 0; } -static void vehicle_init(void) -{ - q_identity( gzoomer.obj.rb.q ); - v3_zero( gzoomer.obj.rb.w ); - v3_zero( gzoomer.obj.rb.v ); - v3_zero( gzoomer.obj.rb.co ); - rb_init_object( &gzoomer.obj ); +static void vehicle_init(void){ + q_identity( gzoomer.rb.q ); + v3_zero( gzoomer.rb.w ); + v3_zero( gzoomer.rb.v ); + v3_zero( gzoomer.rb.co ); + rb_setbody_sphere( &gzoomer.rb, 1.0f, 8.0f, 1.0f ); VG_VAR_F32( k_car_spring, flags=VG_VAR_PERSISTENT ); VG_VAR_F32( k_car_spring_damp, flags=VG_VAR_PERSISTENT ); @@ -58,11 +57,10 @@ static void vehicle_init(void) v3_copy((v3f){ 1.0f, -0.25f, 1.5f }, gzoomer.wheels_local[3] ); } -static void vehicle_wheel_force( int index ) -{ +static void vehicle_wheel_force( int index ){ v3f pa, pb, n; - m4x3_mulv( gzoomer.obj.rb.to_world, gzoomer.wheels_local[index], pa ); - v3_muladds( pa, gzoomer.obj.rb.to_world[1], -k_car_spring_length, pb ); + m4x3_mulv( gzoomer.rb.to_world, gzoomer.wheels_local[index], pa ); + v3_muladds( pa, gzoomer.rb.to_world[1], -k_car_spring_length, pb ); #if 1 @@ -89,7 +87,7 @@ static void vehicle_wheel_force( int index ) v3_lerp( pa, pb, t, pc ); m4x3f mtx; - m3x3_copy( gzoomer.obj.rb.to_world, mtx ); + m3x3_copy( gzoomer.rb.to_world, mtx ); v3_copy( pc, mtx[3] ); vg_line_sphere( mtx, k_car_wheel_radius, VG__BLACK ); vg_line( pa, pc, VG__WHITE ); @@ -100,22 +98,20 @@ static void vehicle_wheel_force( int index ) float Fv = (1.0f-t) * k_car_spring*k_rb_delta; v3f delta; - v3_sub( pa, gzoomer.obj.rb.co, delta ); + v3_sub( pa, gzoomer.rb.co, delta ); v3f rv; - v3_cross( gzoomer.obj.rb.w, delta, rv ); - v3_add( gzoomer.obj.rb.v, rv, rv ); + v3_cross( gzoomer.rb.w, delta, rv ); + v3_add( gzoomer.rb.v, rv, rv ); - Fv += v3_dot( rv, gzoomer.obj.rb.to_world[1] ) - * -k_car_spring_damp*k_rb_delta; + Fv += v3_dot(rv, gzoomer.rb.to_world[1]) * -k_car_spring_damp*k_rb_delta; /* scale by normal incident */ - Fv *= v3_dot( n, gzoomer.obj.rb.to_world[1] ); + Fv *= v3_dot( n, gzoomer.rb.to_world[1] ); v3f F; - v3_muls( gzoomer.obj.rb.to_world[1], Fv, F ); - - rb_linear_impulse( &gzoomer.obj.rb, delta, F ); + v3_muls( gzoomer.rb.to_world[1], Fv, F ); + rb_linear_impulse( &gzoomer.rb, delta, F ); /* friction vectors * -------------------------------------------------------------*/ @@ -124,7 +120,7 @@ static void vehicle_wheel_force( int index ) if( index <= 1 ) v3_cross( gzoomer.steerv, n, tx ); else - v3_cross( n, gzoomer.obj.rb.to_world[2], tx ); + v3_cross( n, gzoomer.rb.to_world[2], tx ); v3_cross( tx, n, ty ); v3_copy( tx, gzoomer.tangent_vectors[ index ][0] ); @@ -136,26 +132,26 @@ static void vehicle_wheel_force( int index ) /* orient inverse inertia tensors */ v3f raW; - m3x3_mulv( gzoomer.obj.rb.to_world, gzoomer.wheels_local[index], raW ); + m3x3_mulv( gzoomer.rb.to_world, gzoomer.wheels_local[index], raW ); v3f raCtx, raCtxI, raCty, raCtyI; v3_cross( tx, raW, raCtx ); v3_cross( ty, raW, raCty ); - m3x3_mulv( gzoomer.obj.rb.iIw, raCtx, raCtxI ); - m3x3_mulv( gzoomer.obj.rb.iIw, raCty, raCtyI ); + m3x3_mulv( gzoomer.rb.iIw, raCtx, raCtxI ); + m3x3_mulv( gzoomer.rb.iIw, raCty, raCtyI ); - gzoomer.tangent_mass[index][0] = gzoomer.obj.rb.inv_mass; + gzoomer.tangent_mass[index][0] = gzoomer.rb.inv_mass; gzoomer.tangent_mass[index][0] += v3_dot( raCtx, raCtxI ); gzoomer.tangent_mass[index][0] = 1.0f/gzoomer.tangent_mass[index][0]; - gzoomer.tangent_mass[index][1] = gzoomer.obj.rb.inv_mass; + gzoomer.tangent_mass[index][1] = gzoomer.rb.inv_mass; gzoomer.tangent_mass[index][1] += v3_dot( raCty, raCtyI ); gzoomer.tangent_mass[index][1] = 1.0f/gzoomer.tangent_mass[index][1]; /* apply drive force */ if( index >= 2 ){ v3_muls( ty, -gzoomer.drive * k_car_drive_force * k_rb_delta, F ); - rb_linear_impulse( &gzoomer.obj.rb, raW, F ); + rb_linear_impulse( &gzoomer.rb, raW, F ); } } else{ @@ -165,9 +161,8 @@ static void vehicle_wheel_force( int index ) } } -static void vehicle_solve_friction(void) -{ - rigidbody *rb = &gzoomer.obj.rb; +static void vehicle_solve_friction(void){ + rigidbody *rb = &gzoomer.rb; for( int i=0; i<4; i++ ){ v3f raW; m3x3_mulv( rb->to_world, gzoomer.wheels_local[i], raW ); @@ -203,7 +198,7 @@ static void vehicle_update_fixed(void) if( !gzoomer.alive ) return; - rigidbody *rb = &gzoomer.obj.rb; + rigidbody *rb = &gzoomer.rb; v3_muls( rb->to_world[2], -cosf(gzoomer.steer), gzoomer.steerv ); v3_muladds( gzoomer.steerv, rb->to_world[0], @@ -222,13 +217,17 @@ static void vehicle_update_fixed(void) for( int i=0; i<4; i++ ) vehicle_wheel_force( i ); + rigidbody _null = {0}; + _null.inv_mass = 0.0f; + m3x3_zero( _null.iI ); + rb_ct manifold[64]; - int len = rb_sphere__scene( rb->to_world, &gzoomer.obj.inf.sphere, NULL, - &world_current_instance()->rb_geo.inf.scene, + int len = rb_sphere__scene( rb->to_world, 1.0f, NULL, + world_current_instance()->geo_bh, manifold, 0 ); for( int j=0; jrb_geo.rb; + manifold[j].rbb = &_null; } rb_manifold_filter_coplanar( manifold, len, 0.05f ); @@ -246,15 +245,14 @@ static void vehicle_update_fixed(void) } rb_iter( rb ); - rb_update_transform( rb ); + rb_update_matrices( rb ); } -static void vehicle_update_post(void) -{ +static void vehicle_update_post(void){ if( !gzoomer.alive ) return; - rb_object_debug( &gzoomer.obj, VG__WHITE ); + vg_line_sphere( gzoomer.rb.to_world, 1.0f, VG__WHITE ); /* draw friction vectors */ v3f p0, px, py; diff --git a/vehicle.h b/vehicle.h index bdd12f0..d61d1e3 100644 --- a/vehicle.h +++ b/vehicle.h @@ -2,7 +2,7 @@ #define VEHICLE_H #include "skaterift.h" -#include "rigidbody.h" +#include "vg/vg_rigidbody.h" #include "player.h" #include "world.h" #include "world_physics.h" @@ -21,7 +21,7 @@ typedef struct drivable_vehicle drivable_vehicle; struct drivable_vehicle { int alive, inside; - rb_object obj; + rigidbody rb; v3f wheels[4]; @@ -37,8 +37,7 @@ struct drivable_vehicle } static gzoomer = { - .obj = { .type = k_rb_shape_sphere, .inf.sphere.radius = 1.0f, - .rb.co = {-2000,-2000,-2000}} + .rb.co = {-2000,-2000,-2000} }; static int spawn_car( int argc, const char *argv[] ); diff --git a/world.h b/world.h index 642e757..6ff3afb 100644 --- a/world.h +++ b/world.h @@ -56,7 +56,8 @@ static void skaterift_world_get_save_path( enum world_purpose which, static f32 k_day_length = 30.0f; /* minutes */ static i32 k_debug_light_indices = 0, k_debug_light_complexity= 0, - k_light_preview = 0; + k_light_preview = 0, + k_light_editor = 0; #define WORLD_SURFACE_HAS_TRAFFIC 0x1 #define WORLD_SURFACE_HAS_PROPS 0x2 @@ -187,7 +188,8 @@ struct world_instance { ent_cubemap, ent_miniworld, ent_prop, - ent_region; + ent_region, + ent_glider; enum skybox { k_skybox_default, @@ -217,8 +219,6 @@ struct world_instance { mesh_water; u32 cubemap_cooldown, cubemap_side; - rb_object rb_geo; - /* leaderboards */ struct leaderboard_cache *leaderboard_cache; diff --git a/world_entity.c b/world_entity.c index 4402291..831e7eb 100644 --- a/world_entity.c +++ b/world_entity.c @@ -12,6 +12,7 @@ #include "ent_skateshop.h" #include "ent_route.h" #include "ent_traffic.h" +#include "ent_glider.h" static void world_entity_focus( u32 entity_id ){ localplayer.immobile = 1; @@ -194,7 +195,8 @@ static void world_gen_entities_init( world_instance *world ){ { k_ent_gate, &world->ent_gate }, { k_ent_objective, &world->ent_objective }, { k_ent_volume, &world->ent_volume }, - { k_ent_challenge, &world->ent_challenge } + { k_ent_challenge, &world->ent_challenge }, + { k_ent_glider, &world->ent_glider } }; for( u32 i=0; itransform, transform ); m4x3_expand_aabb_aabb( transform, bound, box ); } + else if( type == k_ent_glider ){ + ent_glider *glider = mdl_arritm( &world->ent_glider, index ); + m4x3f transform; + mdl_transform_m4x3( &glider->transform, transform ); + m4x3_expand_aabb_aabb( transform, bound, + (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} ); + } else{ vg_fatal_error( "Programming error\n" ); } @@ -510,6 +519,10 @@ static float entity_bh_centroid( void *user, u32 item_index, int axis ){ ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index ); return challenge->transform.co[axis]; } + else if( type == k_ent_glider ){ + ent_glider *glider = mdl_arritm( &world->ent_glider, index ); + return glider->transform.co[axis]; + } else { vg_fatal_error( "Programming error\n" ); return INFINITY; diff --git a/world_gen.c b/world_gen.c index e2ec14a..1bbc2ca 100644 --- a/world_gen.c +++ b/world_gen.c @@ -109,7 +109,8 @@ static void world_apply_procedural_foliage( world_instance *world, scene_context *scene, struct world_surface *mat ) { - if( vg.quality_profile == k_quality_profile_low ) + if( (vg.quality_profile == k_quality_profile_low) || + (vg.quality_profile == k_quality_profile_min) ) return; vg_info( "Applying foliage (%u)\n", mat->info.pstr_name ); @@ -266,19 +267,8 @@ static void world_gen_generate_meshes( world_instance *world ){ /* need send off the memory to the gpu before we can create the bvh. */ vg_async_stall(); vg_info( "creating bvh\n" ); - - /* setup spacial mapping and rigidbody */ world->geo_bh = scene_bh_create( world->heap, &world->scene_geo ); - v3_zero( world->rb_geo.rb.co ); - v3_zero( world->rb_geo.rb.v ); - q_identity( world->rb_geo.rb.q ); - v3_zero( world->rb_geo.rb.w ); - - world->rb_geo.type = k_rb_shape_scene; - world->rb_geo.inf.scene.bh_scene = world->geo_bh; - rb_init_object( &world->rb_geo ); - /* * Generate scene: non-collidable geometry * ---------------------------------------------------------------- diff --git a/world_load.c b/world_load.c index 6055d88..ece9297 100644 --- a/world_load.c +++ b/world_load.c @@ -69,6 +69,7 @@ static void world_instance_load_mdl( u32 instance_id, const char *path ){ MDL_LOAD_ARRAY( meta, &world->ent_miniworld, ent_miniworld, heap ); 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_array_ptr infos; MDL_LOAD_ARRAY( meta, &infos, ent_worldinfo, vg_mem.scratch ); diff --git a/world_map.c b/world_map.c index e2fbe7a..c7f6ec8 100644 --- a/world_map.c +++ b/world_map.c @@ -173,6 +173,18 @@ static void world_map_pre_update(void){ respawn_map_draw_icon( cam, k_gui_icon_rift_run_2d, route->board_transform[3] ); } + + for( u32 i=0; ient_glider); i ++ ){ + ent_glider *glider = mdl_arritm( &world->ent_glider, i ); + + v4f colour = { 1,1,1,1 }; + + if( !(glider->flags & 0x1) ) + v3_muls( colour, 0.5f, colour ); + gui_icon_setcolour( colour ); + + respawn_map_draw_icon( cam, k_gui_icon_glider, glider->transform.co ); + } } static void world_map_enter(void){ diff --git a/world_physics.h b/world_physics.h index 6cc8c5d..8531b8f 100644 --- a/world_physics.h +++ b/world_physics.h @@ -2,7 +2,8 @@ #define WORLD_PHYSICS_H #include "world.h" -#include "rigidbody.h" +#include "vg/vg_rigidbody.h" +#include "vg/vg_rigidbody_collision.h" static 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 9eac40e..2355e20 100644 --- a/world_render.c +++ b/world_render.c @@ -13,6 +13,7 @@ #include "ent_miniworld.h" #include "player_remote.h" #include "ent_skateshop.h" +#include "shaders/model_entity.h" static int ccmd_set_time( int argc, const char *argv[] ){ world_instance *world = world_current_instance(); @@ -70,6 +71,7 @@ static void world_render_init(void){ VG_VAR_I32( k_debug_light_indices ); VG_VAR_I32( k_debug_light_complexity ); VG_VAR_I32( k_light_preview ); + VG_VAR_I32( k_light_editor ); vg_console_reg_cmd( "set_time", ccmd_set_time, NULL ); world_render.sky_rate = 1.0; @@ -858,6 +860,13 @@ static void world_prerender( world_instance *world ){ state->g_debug_complexity = k_debug_light_complexity; state->g_time_of_day = vg_fractf( world->time ); + if( vg.quality_profile == k_quality_profile_high ) + state->g_shadow_samples = 8; + else if( vg.quality_profile == k_quality_profile_low ) + state->g_shadow_samples = 2; + else + state->g_shadow_samples = 0; + state->g_day_phase = cosf( state->g_time_of_day * VG_PIf * 2.0f ); state->g_sunset_phase= cosf( state->g_time_of_day * VG_PIf * 4.0f + VG_PIf ); @@ -883,6 +892,50 @@ static void world_prerender( world_instance *world ){ sizeof(struct ub_world_lighting), &world->ub_lighting ); } +static void render_other_entities( world_instance *world, 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; + + i32 idx; + while( bh_next( world->entity_bh, &it, &idx ) ){ + u32 id = world->entity_list[ idx ], + type = mdl_entity_id_type( id ), + index = mdl_entity_id_id( id ); + + if( type == k_ent_glider ) { + if( glider_count < vg_list_size(glider_list) ) + glider_list[ glider_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) ) + continue; + + m4x3f mdl; + mdl_transform_m4x3( &glider->transform, mdl ); + + f32 dist = v3_dist( glider->transform.co, cam->pos ) * (1.0f/radius), + scale = vg_smoothstepf( vg_clampf( 5.0f-dist*5.0f, 0.0f,1.0f ) ); + m3x3_scalef( mdl, scale ); + + render_glider_model( cam, world, mdl, k_board_shader_entity ); + } +} + static void render_world( world_instance *world, camera *cam, int stenciled, int viewing_from_gate, int with_water, int with_cubemaps ){ @@ -979,6 +1032,7 @@ static void render_world( world_instance *world, camera *cam, } render_remote_players( world, cam ); + render_other_entities( world, cam ); ent_miniworld_render( world, cam ); if( stenciled ){ @@ -1181,4 +1235,48 @@ static void render_world_position( world_instance *world, camera *cam ){ mesh_draw( &world->mesh_geo ); } +struct ui_enum_opt skybox_setting_options[] = { + { 0, "g_daysky_colour" }, + { 1, "g_nightsky_colour" }, + { 2, "g_sunset_colour" }, + { 3, "g_ambient_colour" }, + { 4, "g_sun_colour" }, +}; + +static f32 *skybox_prop_location( world_instance *world, i32 index ){ + switch( index ){ + case 0: return world->ub_lighting.g_daysky_colour; break; + case 1: return world->ub_lighting.g_nightsky_colour; break; + case 2: return world->ub_lighting.g_sunset_colour; break; + case 3: return world->ub_lighting.g_ambient_colour; break; + case 4: return world->ub_lighting.g_sun_colour; break; + default: return NULL; + } +} + +static void imgui_world_light_edit( world_instance *world ){ + ui_rect panel = { vg.window_x-400, 0, 400, vg.window_y }; + ui_fill( panel, ui_colour( k_ui_bg+1 ) ); + ui_outline( panel, 1, ui_colour( k_ui_bg+7 ), 0 ); + ui_rect_pad( panel, (ui_px[2]){ 8, 8 } ); + vg_ui.wants_mouse = 1; + + static i32 option_to_edit = 0; + ui_enum( panel, "option", skybox_setting_options, 5, &option_to_edit ); + ui_colourpicker( panel, "colour", + skybox_prop_location( world, option_to_edit ) ); + + if( ui_button( panel, "save tweaker file ('/tmp/tweaker.txt')\n" ) == 1 ){ + FILE *fp = fopen( "/tmp/tweaker.txt", "w" ); + + for( i32 i=0; i<5; i ++ ){ + struct ui_enum_opt *opt = &skybox_setting_options[i]; + f32 *val = skybox_prop_location( world, i ); + fprintf( fp, "%s = {%.3ff, %.3ff, %.3ff, %.3ff},\n", + opt->alias, val[0], val[1], val[2], val[3] ); + } + fclose( fp ); + } +} + #endif diff --git a/world_render.h b/world_render.h index 540e0be..ecbd3d2 100644 --- a/world_render.h +++ b/world_render.h @@ -51,12 +51,12 @@ struct world_render{ u32 timer_text_count; struct text_particle{ - rb_object obj; + rigidbody rb; m4x3f mlocal; ent_glyph *glyph; v4f colour; - m4x3f mdl; + f32 radius; } text_particles[6*4]; u32 text_particle_count; diff --git a/world_routes.c b/world_routes.c index b80e68e..105e546 100644 --- a/world_routes.c +++ b/world_routes.c @@ -1,10 +1,11 @@ +#pragma once + /* - * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + * Copyright (C) 2021-2024 Mt.ZERO Software - All Rights Reserved + * + * World routes */ -#ifndef ROUTES_C -#define ROUTES_C - #include #include "entity.h" #include "world_routes.h" @@ -21,6 +22,7 @@ #include "shaders/scene_route.h" #include "shaders/routeui.h" #include "ent_region.h" +#include "scene_rigidbody.h" static void world_routes_clear( world_instance *world ) { @@ -666,27 +668,31 @@ static void world_routes_update( world_instance *world ){ for( u32 i=0; iobj, VG__RED ); + //rb_object_debug( &particle->obj, VG__RED ); } } static void world_routes_fixedupdate( world_instance *world ){ rb_solver_reset(); + rigidbody _null = {0}; + _null.inv_mass = 0.0f; + m3x3_zero( _null.iI ); + for( u32 i=0; iobj.rb.to_world, - &particle->obj.inf.sphere, - NULL, &world->rb_geo.inf.scene, buf, + int l = rb_sphere__scene( particle->rb.to_world, + particle->radius, + NULL, world->geo_bh, buf, k_material_flag_ghosts ); for( int j=0; jobj.rb; - buf[j].rbb = &world->rb_geo.rb; + buf[j].rba = &particle->rb; + buf[j].rbb = &_null; } rb_contact_count += l; @@ -705,12 +711,12 @@ static void world_routes_fixedupdate( world_instance *world ){ for( u32 i=0; iobj.rb ); + rb_iter( &particle->rb ); } for( u32 i=0; iobj.rb ); + rb_update_matrices( &particle->rb ); } } @@ -878,26 +884,24 @@ static void world_routes_fracture( world_instance *world, ent_gate *gate, v3_add( offset, origin, world_co ); m4x3_mulv( transform, world_co, world_co ); - float r = vg_maxf( s[0]*glyph->size[0], s[1]*glyph->size[1] )*0.5f; m3x3_identity( particle->mlocal ); m3x3_scale( particle->mlocal, s ); origin[2] *= s[2]; v3_muls( origin, -1.0f, particle->mlocal[3] ); - v3_copy( world_co, particle->obj.rb.co ); - v3_muls( imp_v, 1.0f+vg_randf64(&vg.rand), particle->obj.rb.v ); - particle->obj.rb.v[1] += 2.0f; + v3_copy( world_co, particle->rb.co ); + v3_muls( imp_v, 1.0f+vg_randf64(&vg.rand), particle->rb.v ); + particle->rb.v[1] += 2.0f; - v4_copy( q, particle->obj.rb.q ); - particle->obj.rb.w[0] = vg_randf64(&vg.rand)*2.0f-1.0f; - particle->obj.rb.w[1] = vg_randf64(&vg.rand)*2.0f-1.0f; - particle->obj.rb.w[2] = vg_randf64(&vg.rand)*2.0f-1.0f; + v4_copy( q, particle->rb.q ); + particle->rb.w[0] = vg_randf64(&vg.rand)*2.0f-1.0f; + particle->rb.w[1] = vg_randf64(&vg.rand)*2.0f-1.0f; + particle->rb.w[2] = vg_randf64(&vg.rand)*2.0f-1.0f; - particle->obj.type = k_rb_shape_sphere; - particle->obj.inf.sphere.radius = r*0.6f; - - rb_init_object( &particle->obj ); + f32 r = vg_maxf( s[0]*glyph->size[0], s[1]*glyph->size[1] )*0.5f; + particle->radius = r*0.6f; + rb_setbody_sphere( &particle->rb, particle->radius, 1.0f, 1.0f ); } offset[0] += glyph->size[0]; } @@ -1009,7 +1013,7 @@ static void render_world_routes( world_instance *world, v4f q; m4x3f model; - rb_extrapolate( &particle->obj.rb, model[3], q ); + rb_extrapolate( &particle->rb, model[3], q ); q_m3x3( q, model ); m4x3_mul( model, particle->mlocal, particle->mdl ); @@ -1080,5 +1084,3 @@ static void render_world_routes( world_instance *world, glEnable( GL_CULL_FACE ); glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } ); } - -#endif /* ROUTES_C */ diff --git a/world_sfd.h b/world_sfd.h index 9e8d38c..176425d 100644 --- a/world_sfd.h +++ b/world_sfd.h @@ -7,6 +7,7 @@ #include "world.h" #include "world_routes.h" +#include "scene.h" struct world_sfd{ GLuint tex_scoretex; diff --git a/world_water.c b/world_water.c index 085e252..62e133d 100644 --- a/world_water.c +++ b/world_water.c @@ -170,7 +170,8 @@ static void render_water_surface( world_instance *world, camera *cam ){ glDisable(GL_BLEND); } - else if( vg.quality_profile == k_quality_profile_low ){ + else if( (vg.quality_profile == k_quality_profile_low) || + (vg.quality_profile == k_quality_profile_min) ){ shader_scene_water_fast_use(); glActiveTexture( GL_TEXTURE1 );