Merge all features from glider feature branch rigidbody
authorhgn <hgodden00@gmail.com>
Thu, 8 Feb 2024 03:46:07 +0000 (03:46 +0000)
committerhgn <hgodden00@gmail.com>
Thu, 8 Feb 2024 03:46:07 +0000 (03:46 +0000)
70 files changed:
blender_export.py
build.c
ent_glider.c [new file with mode: 0644]
ent_glider.h [new file with mode: 0644]
ent_region.c
entity.c
entity.h
gameserver.c
gameserver.h
gameserver_db.h
gui.h
maps_src/dev_flatworld/main.mdl
maps_src/mp_mtzero/main.mdl
model.h
models_src/ch_none.mdl
models_src/glider.mdl [new file with mode: 0644]
models_src/rs_icons.mdl
network_msg.h
particle.c
player.c
player.h
player_common.c
player_dead.c
player_drive.c
player_glide.c [new file with mode: 0644]
player_glide.h [new file with mode: 0644]
player_model.h
player_ragdoll.c
player_ragdoll.h
player_remote.c
player_remote.h
player_render.c
player_replay.c
player_replay.h
player_skate.c
player_walk.c
player_walk.h
rigidbody.h [deleted file]
scene_rigidbody.h [new file with mode: 0644]
shaders/model.vs
shaders/model_board_view.h
shaders/model_entity.h
shaders/model_gate.h
shaders/model_gate_unlinked.h
shaders/model_menu.h
shaders/model_sky.h
shaders/model_sky_space.h
shaders/scene_water_fast.fs
shaders/scene_water_fast.h
shaders/trail.fs [new file with mode: 0644]
shaders/trail.h [new file with mode: 0644]
shaders/trail.vs [new file with mode: 0644]
skaterift.c
skaterift.h
testing.c [new file with mode: 0644]
trail.c [new file with mode: 0644]
trail.h [new file with mode: 0644]
vehicle.c
vehicle.h
world.h
world_entity.c
world_gen.c
world_load.c
world_map.c
world_physics.h
world_render.c
world_render.h
world_routes.c
world_sfd.h
world_water.c

index 695fff05d7eee6303b7333e48ffd8b333c0936ed..421cf5d796e75ebbc8e21123f0ef080f80e66759 100644 (file)
@@ -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 5958fa3a9d5e94a1c0923d072b38f8e05a5afec7..cb7a71a26324e40ef1c48388ad34d5e5c71e79b7 100644 (file)
--- 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 (file)
index 0000000..d1e2a36
--- /dev/null
@@ -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 (file)
index 0000000..e61bdd0
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+#include "entity.h"
+
+static void ent_glider_call( world_instance *world, ent_call *call );
index fd24f4e1cd3b6f3dc98bfc6b025475b011de76f7..44d49a096637bff1f4e6777aedb38e7820fcf12f 100644 (file)
@@ -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;
index 2ff0cab4f448241783850eb201a854560acd71a6..ce6d98eb76ecd959f572160c8f6c08f14b66d928 100644 (file)
--- 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) ){
index 1c11f38ce3ec3544d7c82ac935df6f71c09463ca..1adeb7e6095643d31c5e4654e02d60d13438f5e0 100644 (file)
--- 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"
index a93d705abc32204e2c5218f000f6545dec92c94b..5bbb8ae9685dbaace02f02bf1e1430c349c64d27 100644 (file)
@@ -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<vg_list_size(gameserver.clients); i++ ){
       if( i == client_id )
index 07ba7a0d62d3df8e8cf20078517202e74fb485e9..caa4f0d7e49140305f67abf298f9573eddd4aa01 100644 (file)
@@ -10,7 +10,6 @@
 #include "vg/vg_steam_auth.h"
 #include "network_msg.h"
 #include "network_common.h"
-#include "highscores.h"
 #include <sys/socket.h>
 
 #define CLIENT_KNOWLEDGE_SAME_WORLD0 0x1
index 55a4f4775cf80cc7df33d5028d8a05580c64555b..fe39931af843ae30b3715b6192367193dedc489a 100644 (file)
@@ -5,7 +5,6 @@
 #include "vg/vg_mem_queue.h"
 #include "network_common.h"
 #include "dep/sqlite3/sqlite3.h"
-#include "highscores.h"
 #include <pthread.h>
 #include <unistd.h>
 
diff --git a/gui.h b/gui.h
index 1414bd587f6edb2513dba20a2dc0dd7da24c05d0..67f78b5df9829e4b66e7405cb1015e8cac3765cb 100644 (file)
--- 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]=
index 10619c573fcd1a44dd0c286a8c788ef22b577b70..f9c326c23a11b9d075b46f34f7c6a37b42530e59 100644 (file)
Binary files a/maps_src/dev_flatworld/main.mdl and b/maps_src/dev_flatworld/main.mdl differ
index 76139c8b6bbdce0b417c8e8813377a7f68687a42..b743ebe1d3ad25a49fceff01160248b8f02c06da 100644 (file)
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 62438dcf0f84f4924fa0a449677cb9b715ff5f68..ba1c3f228c8cc827176bc9a8b4a92126c82cba8a 100644 (file)
--- 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,
index 5dacbe1085cbb8375cfc519e8d0a0641b2517d0f..760fda43c839fb840baf73cf6802a3f074445bb9 100644 (file)
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 (file)
index 0000000..0a2ca3c
Binary files /dev/null and b/models_src/glider.mdl differ
index e3ee8b5d351052d3637f8ae815721cc0145691d0..7dec1221f5716be7830cc5e05801cbd1ec261b34 100644 (file)
Binary files a/models_src/rs_icons.mdl and b/models_src/rs_icons.mdl differ
index dfd6dabad717bc6676416e8a858aa7114c501fba..5d3beeef1a61ba3329141062ef2d9b28f22fe49b 100644 (file)
@@ -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 */
index d12a87a024e3db67de24a1134810655e884775d2..9c1f87e5e044f8674b4995cc4f6ee53367004c60 100644 (file)
@@ -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;
index 7a099f0fc16847c58d5862f22d0a97f0b5d2880f..1d690a088bab23a0e39225d9561d8ed167f597ae 100644 (file)
--- 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"
index cb37b7ce50576bf57e453dba1d0f442ef1716d15..ad152270328d8f2d633885ee105bd95a098565ea 100644 (file)
--- 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,
 };
 
 /*
index 07b1f5ade900678fbcbd8a0fa1f0f71aa86a57c2..5236ee5b9227c0358d0966f31e394236baa2725c 100644 (file)
@@ -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 );
index 7733c327e2b0668d9a6a7439fd7fda829cdf509b..625ff0e7643cae2dfb8c0d4c5a4cb9dc93cd1789 100644 (file)
@@ -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;
index 38b7e58c8e586102bfd2afbec92a49d5c80aa916..7881302865b1a2bd2fbe0ad474abf98958a50390 100644 (file)
@@ -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 (file)
index 0000000..af44ea6
--- /dev/null
@@ -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; j<l; j ++ ){
+            buf[j].rba = rb;
+            buf[j].rbb = &_null;
+         }
+
+         rb_contact_count += l;
+      }
+   }
+
+   rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
+   for( u32 i=0; i<10; i ++ )
+      rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+   
+   rb_iter( rb );
+   rb_update_matrices( rb );
+
+   return bottom_hit;
+}
+
+static void player_glide_update(void){
+   v2f steer;
+   joystick_state( k_srjoystick_steer, steer );
+
+   if( glider_physics( steer ) ){
+      vg_info( "player fell off due to glider hitting ground\n" );
+      player__dead_transition( k_player_die_type_generic );
+      localplayer.glider_orphan = 1;
+   }
+}
+
+static void player_glide_post_update(void){
+   v3_copy( player_glide.rb.co, localplayer.rb.co );
+   v4_copy( player_glide.rb.q, localplayer.rb.q );
+   v3_copy( player_glide.rb.v, localplayer.rb.v );
+   v3_copy( player_glide.rb.w, localplayer.rb.w );
+   rb_update_matrices( &localplayer.rb );
+}
+
+static void player_glide_animate(void){
+   struct player_glide *g = &player_glide;
+   struct player_glide_animator *animator = &g->animator;
+   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; i<vg_list_size(player_glide.parts); i ++ ){
+      /* create part transform matrix */
+      v4f qp, qy, qr, q;
+      q_axis_angle( qp, (v3f){1,0,0}, player_glide.parts[i].euler[0] );
+      q_axis_angle( qy, (v3f){0,1,0}, player_glide.parts[i].euler[1] );
+      q_axis_angle( qr, (v3f){0,0,1}, player_glide.parts[i].euler[2] );
+
+      q_mul( qr, qy, q );
+      q_mul( q, qp, q );
+
+      q_m3x3( q, player_glide.parts[i].mdl );
+      v3_copy( player_glide.parts[i].co, player_glide.parts[i].mdl[3] );
+
+      /* add it to inertia model */
+
+      if( player_glide.parts[i].shape == k_rb_shape_capsule ){
+         f32 r  = player_glide.parts[i].inf.r,
+             h  = player_glide.parts[i].inf.h,
+             pv = vg_capsule_volume( r, h ),
+             pm = pv * k_density;
+
+         mass += pm;
+
+         m3x3f pI;
+         vg_capsule_inertia( r, h, pm, pI );
+         vg_rotate_inertia( pI, player_glide.parts[i].mdl );
+         vg_translate_inertia( pI, pm, player_glide.parts[i].co );
+         m3x3_add( I, pI, I );
+      }
+      else if( player_glide.parts[i].shape == k_rb_shape_sphere ){
+         f32 r  = player_glide.parts[i].r,
+             pv = vg_sphere_volume( r ),
+             pm = pv * k_density;
+
+         mass += pm;
+         m3x3f pI;
+         vg_sphere_inertia( r, pm, pI );
+         vg_translate_inertia( pI, pm, player_glide.parts[i].co );
+         m3x3_add( I, pI, I );
+      }
+   }
+
+   /* set inverses */
+   m3x3_inv( I, player_glide.rb.iI );
+   player_glide.rb.inv_mass = 1.0f / mass;
+
+   /* resources */
+   struct skeleton *sk = &localplayer.skeleton;
+   player_glide.anim_glide = skeleton_get_anim( sk, "glide_pose" );
+
+   void *alloc = vg_mem.rtmemory;
+   mdl_context *mdl = &player_glide.glider;
+
+   mdl_open( mdl, "models/glider.mdl", alloc );
+   mdl_load_metadata_block( mdl, alloc );
+
+   vg_linear_clear( vg_mem.scratch );
+
+   u32 count = mdl_arrcount( &mdl->textures );
+   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; i<count; i ++ ){
+      vg_linear_clear( vg_mem.scratch );
+      player_glide.glider_textures[i+1] = vg.tex_missing;
+
+      mdl_texture *tex = mdl_arritm( &mdl->textures, 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; i<mdl_arrcount( &markers ); i ++ ){
+      ent_marker *marker = mdl_arritm( &markers, i );
+      v3_copy( marker->transform.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<vg_list_size(trails_glider); i ++ ){
+      trail_alloc( &trails_glider[i], 200 );
+   }
+}
+
+static void player_glide_transition(void){
+   localplayer.subsystem = k_player_subsystem_glide;
+   localplayer.have_glider = 0;
+
+   v3_copy( localplayer.rb.co, player_glide.rb.co );
+
+   f32 dir = v3_dot( localplayer.rb.v, localplayer.rb.to_world[2] );
+
+   if( dir > 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; i<mdl_arrcount(&mdl->meshs); 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; j<mesh->submesh_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; i<player_glide.trail_count; i ++ ){
+      v3f vvert;
+      q_mulv( q, player_glide.trail_positions[i], vvert );
+      v3_add( co, vvert, vvert );
+      
+      trail_system_update( &trails_glider[i], vg.time_delta, vvert,
+                           localplayer.rb.to_world[1], alpha );
+                           
+      trail_system_prerender( &trails_glider[i] );
+      trail_system_render( &trails_glider[i], &skaterift.cam );
+   }
+}
+
+#endif /* PLAYER_GLIDE_C */
diff --git a/player_glide.h b/player_glide.h
new file mode 100644 (file)
index 0000000..f8f6dd9
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef PLAYER_GLIDE_H
+#define PLAYER_GLIDE_H
+
+#include "player.h"
+#include "trail.h"
+
+struct player_glide {
+   struct skeleton_anim *anim_glide;
+
+   struct player_glide_animator {
+      v3f root_co;
+      v4f root_q;
+   }
+   animator;
+
+   /* this sucks */
+   struct remote_glider_animator {
+      v3f root_co;
+      v4f root_q;
+      f32 s;
+   }
+   remote_animator;
+
+   v3f info_lift,
+       info_slip,
+       info_drag;
+
+   u32 ticker;
+
+   rigidbody rb;
+
+   f32 t;
+
+   struct {
+      v3f co, euler;
+      m4x3f mdl;
+      
+      union {
+         rb_capsule inf;
+         f32 r;
+      };
+
+      enum rb_shape shape;
+      bool is_damage;
+   }
+   parts[4];
+
+   u32 trail_count;
+   v3f trail_positions[2];
+
+   mdl_context glider;
+   GLuint *glider_textures;
+   glmesh glider_mesh;
+}
+static player_glide = {
+   .parts = {
+      {
+         .co    = { 1.0f, 0.5f, -1.0f },
+         .euler = { VG_TAUf*0.25f,  VG_TAUf*0.125f, 0.0f },
+         .shape = k_rb_shape_capsule,
+         .inf   = { .h = 2.82842712475f, .r = 0.25f },
+      },
+      {
+         .co    = { -1.0f, 0.5f, -1.0f },
+         .euler = { VG_TAUf*0.25f, -VG_TAUf*0.125f, 0.0f },
+         .shape = k_rb_shape_capsule,
+         .inf   = { .h = 2.82842712475f, .r = 0.25f },
+      },
+      {
+         .co    = {  0.0f, 0.5f, 1.0f },
+         .euler = { VG_TAUf*0.25f, VG_TAUf*0.25f, 0.0f },
+         .shape = k_rb_shape_capsule,
+         .inf   = { .h = 6.0f, .r = 0.25f },
+      },
+      {
+         .co    = {  0.0f, -0.5f, 0.0f },
+         .euler = { VG_TAUf*0.25f, VG_TAUf*0.25f, 0.0f },
+         .shape = k_rb_shape_capsule,
+         .inf   = { .h = 2.0f, .r = 0.25f },
+         .is_damage = 1,
+      },
+
+#if 0
+      {
+         .co    = { 0.0f, 0.0f, 0.0f },
+         .euler = { 0.0f, 0.0f, 0.0f },
+         .shape = k_rb_shape_sphere,
+         .r     = 0.5f
+      }
+#endif
+   }
+};
+
+static trail_system trails_glider[] = {
+{
+   .width = 0.035f,
+   .lifetime = 5.0f,
+   .min_dist = 0.5f
+},
+{
+   .width = 0.035f,
+   .lifetime = 5.0f,
+   .min_dist = 0.5f
+},
+};
+
+static void player_glide_pre_update(void);
+static void player_glide_update(void);
+static void player_glide_post_update(void);
+static void player_glide_animate(void);
+static void player_glide_pose( void *animator, player_pose *pose );
+
+static void player_glide_post_animate(void);
+static void player_glide_im_gui(void);
+static void player_glide_bind(void);
+static void player_glide_transition(void);
+static bool glider_physics( v2f steer );
+static void player_glide_animator_exchange( bitpack_ctx *ctx, void *data );
+static void player_glide_render( camera *cam, world_instance *world,
+                                 player_pose *pose );
+static void render_glider_model( camera *cam, world_instance *world,
+                                 m4x3f mmdl, enum board_shader shader );
+static void 
+player_glide_remote_animator_exchange( bitpack_ctx *ctx, void *data );
+static void player_glide_equip_glider(void);
+
+struct player_subsystem_interface static player_subsystem_glide = {
+   .pre_update = player_glide_pre_update,
+   .update = player_glide_update,
+   .post_update = player_glide_post_update,
+   .animate = player_glide_animate,
+   .pose = player_glide_pose,
+   .post_animate = player_glide_post_animate,
+   .network_animator_exchange = player_glide_animator_exchange,
+   .im_gui = player_glide_im_gui,
+   .bind = player_glide_bind,
+
+   .animator_data = &player_glide.animator,
+   .animator_size = sizeof(player_glide.animator),
+   .name = "Glide"
+};
+
+#endif /* PLAYER_GLIDE_H */
index 1d462f6377485280db4eea7c7323439d60128ea7..576109c51736cf3dc9c2f15a990de928e71a6f15 100644 (file)
@@ -11,7 +11,6 @@
 #include "model.h"
 #include "skeleton.h"
 #include "player_ragdoll.h"
-#include "rigidbody.h"
 
 #include "shaders/model_character_view.h"
 
index e3345808923f0ed5e01fe15622dc2e24dc3b16c2..6dc171d0f28c73944b13254d43fe76f1634cfe9d 100644 (file)
@@ -1,5 +1,8 @@
-#ifndef PLAYER_RAGDOLL_C
-#define PLAYER_RAGDOLL_C
+#pragma once
+#include "vg/vg_rigidbody.h"
+#include "vg/vg_rigidbody_collision.h"
+#include "vg/vg_rigidbody_constraints.h"
+#include "scene_rigidbody.h"
 
 #include "player.h"
 #include "audio.h"
@@ -55,20 +58,24 @@ static void player_ragdoll_init(void){
 static void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
                                                   struct ragdoll_part *rp )
 {
+   f32 k_density = 8.0f,
+       k_inertia_scale = 2.0f;
+
    m4x3_identity( rp->collider_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; i<rd->part_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; j<l; j++ ){
-            buf[j].rba = &rd->parts[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; k<l; k++ ){
-               buf[k].rba = &rd->parts[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; i<rd->part_count; i ++ )
-         rb_object_debug( &rd->parts[i].obj, rd->parts[i].colour );
+      for( u32 i=0; i<rd->part_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; i<rd->part_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; i<rd->part_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 */
index 6f5a33137a8e5a48ef53b21da64d86b1a21555d7..25b6fad7b682e9d8d9625f67f8c6222380e6877d 100644 (file)
@@ -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 */
index e910170ca3f2d4462adc4a9d1b42811fe4bc39ff..ba07abea312a44ebcae783bb00c9c16d792a05d6 100644 (file)
@@ -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; i<localplayer.sfx_buffer_count; i ++ )
          net_sfx_exchange( &ctx, &localplayer.sfx_buffer[i] );
 
+      /* glider
+       * -------------------------------------------------------------*/
+
+      if( localplayer.have_glider || 
+            (localplayer.subsystem == k_player_subsystem_glide) ) {
+         frame->flags |= 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; i<NETWORK_MAX_PLAYERS; i ++ ){
       struct network_player *player = &netplayers.list[i];
@@ -618,8 +675,22 @@ static void render_remote_players( world_instance *world, camera *cam ){
       if( player->active_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; j<draw_list_count; j ++ ){
+      u32 index = draw_list[j];
+
+      struct network_player *player = &netplayers.list[index];
+      m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_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; j<draw_list_count; j ++ ){
+      u32 index = draw_list[j];
+
+      struct network_player *player = &netplayers.list[index];
+      if( !player->render_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[] ){
index 265409c873933cca4a665002c1bd773a1a44f4bd..2d6200b365c6bbfcc39df8932df95f57ec457bef 100644 (file)
@@ -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;
index 1ade3d7d86c1856cfe556c4f2600d42b15675739..40c2f9a73b5d5db2c3a828e9970ee93287ea5224 100644 (file)
@@ -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], 
index 586b7333eafa4d6172ab8440969295789f3896c5..f5a2ab9327ab03c9b7c0705c89596316d758d4ba 100644 (file)
@@ -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; i<k_replay_framedata_rows; i++ ){
       data_table[i][0] = total;
       total += vg_align8(data_table[i][1]);
 
@@ -50,8 +54,9 @@ static void replay_tailpop( replay_buffer *replay ){
 static replay_frame *replay_newframe( replay_buffer *replay, 
                                          u16 animator_size,
                                          u16 gamestate_size,
-                                         u16 sfx_count ){
-   u16 data_table[4][2];
+                                         u16 sfx_count,
+                                         bool save_glider ){
+   u16 data_table[ k_replay_framedata_rows ][2];
    data_table[ k_replay_framedata_animator ][1]  = animator_size;
    data_table[ k_replay_framedata_gamestate ][1] = gamestate_size;
    data_table[ k_replay_framedata_sfx ][1] = sfx_count*sizeof(struct net_sfx);
@@ -61,6 +66,12 @@ static replay_frame *replay_newframe( replay_buffer *replay,
          sizeof( replay_gamestate );
    }
 
+   data_table[ k_replay_framedata_glider ][1] = 0;
+   if( save_glider ){
+      data_table[ k_replay_framedata_glider ][1] = 
+         sizeof(struct replay_glider_data);
+   }
+
    u32 nextsize = replay_frame_calculate_data_offsets( data_table );
 
    replay_frame *frame = NULL;
@@ -99,7 +110,7 @@ check_again:;
    else
       frame = replay->data;
 
-   for( u32 i=0; i<4; i++ ){
+   for( u32 i=0; i<k_replay_framedata_rows; i++ ){
       frame->data_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; i<localplayer.ragdoll.part_count; i ++ ){
-            rigidbody *rb = &localplayer.ragdoll.parts[i].obj.rb;
+            rigidbody *rb = &localplayer.ragdoll.parts[i].rb;
             v3_copy( rb->co, 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; i<localplayer.ragdoll.part_count; i ++ ){
          struct ragdoll_part *part = &localplayer.ragdoll.parts[i];
-         rigidbody *rb = &part->obj.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; i<k_replay_framedata_rows; i++ ){
          player__debugtext( 1, "[%u]: [%hu, %hu]\n", i,
               replay->statehead->data_table[i][0],
               replay->statehead->data_table[i][1] );
index 38599d448a320e48989e2a0634865f5a884a96bf..42d9bf71d768f731964e6195615647acf6e4ecfc 100644 (file)
@@ -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 );
index 9356aa6416b04e54e73fdec941157509699ca597..cbf43ec36fc57837463f29be2d6ffd43195248f3 100644 (file)
@@ -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; i<len; i++ ){
@@ -762,6 +765,7 @@ static void skate_apply_trick_model(void){
       if( (v3_length2(state->trick_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 ], 
index 9c23978a7d463b899bdcc55cda466b778bd75a03..560d61a69a32fec28bf85268523f01e8825e1323 100644 (file)
@@ -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; i<len; i++ ){
-      struct contact *ct = &manifold[i];
+      rb_ct *ct = &manifold[i];
       rb_debug_contact( ct );
 
       if( player_walk_normal_standable( ct->n ) ){
@@ -562,7 +565,7 @@ static void player_walk_update_generic(void){
     */
    for( int j=0; j<5; j++ ){
       for( int i=0; i<len; i++ ){
-         struct contact *ct = &manifold[i];
+         rb_ct *ct = &manifold[i];
          
          /*normal */
          float vn = -v3_dot( localplayer.rb.v, ct->n );
@@ -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 */
index 1cab8eb2ea5d988d8841792bd3f81be17c7fa06b..95ce2a0414e55ee7976e684933ff97575bac40c8 100644 (file)
@@ -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 (file)
index a69e4af..0000000
+++ /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 <math.h>
-
-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; i<len; i++ ){
-      rb_ct *ct = &man[i];
-
-      if( ct->type == 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; i<len-1; i++ ){
-      rb_ct *ci = &man[i];
-      if( ci->type != k_contact_type_edge ) 
-         continue;
-
-      for( int j=i+1; j<len; j++ ){
-         rb_ct *cj = &man[j];
-         if( cj->type != 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; i<len-1; i++ ){
-      rb_ct *ci = &man[i];
-      int similar = 0;
-
-      if( ci->type == k_contact_type_disabled ) continue;
-
-      for( int j=i+1; j<len; j++ ){
-         rb_ct *cj = &man[j];
-
-         if( cj->type == 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; i<len; i++ ){
-      rb_ct *ct = &man[i];
-      if( ct->type == 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; i<len; i++ ){
-      rb_ct *ci = &man[i];
-      if( ci->type == k_contact_type_disabled ||
-          ci->type == k_contact_type_edge ) 
-         continue;
-
-      float d1 = v3_dot( ci->co, ci->n );
-
-      for( int j=0; j<len; j++ ){
-         if( j == i )
-            continue;
-
-         rb_ct *cj = &man[j];
-         if( cj->type == 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; i<count; i++ )
-      v3_copy( n, buf[i].n );
-
-   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, rb_scene *s, 
-                                 rb_ct *buf, u16 ignore ){
-   int count = 0;
-
-   boxf bbx;
-   v3_sub( mtxA[3], (v3f){ c->height, 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; i<len; i++ )
-      {
-         struct contact *ct = &manifold[i];
-
-         float resolved_amt = v3_dot( ct->n, 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; i<len; i++ ){
-      rb_ct *ct = &buffer[i];
-      rb_prepare_contact( ct, k_rb_delta );
-
-      v3f ra, rb, raCn, rbCn, raCt, rbCt;
-      v3_sub( ct->co, 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; i<len; i++ ){
-      struct contact *ct = &buf[i];
-
-      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 );
-      
-      /* 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; i<len; i++ ){
-      rb_constr_pos *constr = &buffer[i];
-      rigidbody *rba = constr->rba, *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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[ i ];
-      
-      v3f vx, vy, va, vxb, axis, center;
-
-      m3x3_mulv( st->rba->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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[ i ];
-      
-      v3f vx, vxb, vy, va, axis, center;
-
-      m3x3_mulv( st->rba->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; i<len; i++ ){
-      rb_constr_pos *constr = &buf[i];
-      rigidbody *rba = constr->rba, *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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[ i ];
-
-      if( !st->axis_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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[ i ];
-
-      if( !st->tangent_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; i<len; i++ ){
-      rb_constr_pos *constr = &buf[i];
-      rigidbody *rba = constr->rba, *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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[i];
-
-      if( !st->tangent_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; i<len; i++ ){
-      rb_constr_swingtwist *st = &buf[i];
-
-      if( !st->axis_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; i<len; i++ ){
-      rb_ct *ct = &buf[i];
-      rigidbody *rba = ct->rba,
-                *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 (file)
index 0000000..57ff1ff
--- /dev/null
@@ -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;
+}
+
index 350a821944bcdf68e824db0af10795c64eecad3f..a23529d839dc7582b9248bb43fe989a287b5591d 100644 (file)
@@ -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;
 }
index a50e9d586e9fb3e71ad7168dd693d882eca51df5..abbc42f1da28b27af31b9c7c65486479e91b7b0e 100644 (file)
@@ -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"
 ""},
index 8b842aee6958e137d847e84d88843a5432a8cba3..a9700388f0e4783c2d9eed4dec3be0423bbb3621 100644 (file)
@@ -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"
 ""},
index 31329abb360c00b1bc97621f47263aa79ace7606..1bc558fd5e0be8bba07fe2603a89a7b4f63a6658 100644 (file)
@@ -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"
 ""},
index 1fda296a7a51e05b7300403adb13c0269be401da..449322aa95a2368792442e9ed59582dd8a6861d5 100644 (file)
@@ -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"
 ""},
index 1d17aa06cae4a10352768d32e89f691012b36d94..0ee6ad39f0aca1044922ebf3f6b28a79991cb35b 100644 (file)
@@ -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"
 ""},
index f8f88d3d1a19ef6e9c205c6006b3d943b56807bc..88612b2bb7ac06a48729f3abd551d95d6175653f 100644 (file)
@@ -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"
 ""},
index 83095509f465aec8ae190b6966ef5fb4230d03a1..c96f2c4bb19531453a7f51ebec84f5018ef97124 100644 (file)
@@ -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"
 ""},
index 77aa569dd3dd7121345bfa1fb6c5ac78eee58720..f89cee55ead0ab3e9da1b8305b43605261f6b854 100644 (file)
@@ -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 );
 }
index 7c5a079106558d9cf0849b517a08098bb36d0582..ac987dd5204c6f15feb79c77bc4d6fff46469757 100644 (file)
@@ -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 (file)
index 0000000..e6ba5a8
--- /dev/null
@@ -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 (file)
index 0000000..ee6090c
--- /dev/null
@@ -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 (file)
index 0000000..827d160
--- /dev/null
@@ -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;
+}
index be4084ef8588c6a2b9b41159a7a8002cc1ca6cab..eec32d5b1725b38c96bf8423fb73c765f1bb4b1d 100644 (file)
@@ -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 );
 
index a21e6ab423642b4b86903b8c7490cff9471df3a6..ebab76734b6d909ca475dc5b4b1dce67b16f9379 100644 (file)
@@ -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 (file)
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; j<rb_contact_count; j++ ){
+      buf[j].rba = &baller.rb;
+      buf[j].rbb = &_null;
+   }
+
+   rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
+
+   for( u32 i=0; i<8; i ++ )
+      rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+
+   rb_iter( &baller.rb );
+   rb_update_matrices( &baller.rb );
+}
+
+static void testing_init(void){
+   rb_setbody_box( &baller.rb, baller.box, 8.0f, 1.0f );
+}
diff --git a/trail.c b/trail.c
new file mode 100644 (file)
index 0000000..3399694
--- /dev/null
+++ b/trail.c
@@ -0,0 +1,181 @@
+#pragma once
+#include "trail.h"
+#include "shaders/particle.h"
+#include "shaders/trail.h"
+
+static void trail_increment( trail_system *sys ){
+   sys->head ++;
+
+   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; i<sys->count; 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; i<sys->count; 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; i<sys->count; 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 (file)
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 */
index 3d68d1152793b2fbaeb29bf48460e12df055d78b..3555a971ed35c140e15a63f252d243d13e7a6620 100644 (file)
--- 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; j<len; j++ ){
       manifold[j].rba = rb;
-      manifold[j].rbb = &world_current_instance()->rb_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;
index bdd12f02329cfe6169912ccb9fe89e3bb5a53de0..d61d1e3adf25117eed0938c69cb99aae94307ab1 100644 (file)
--- 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 642e7578b44a6d32ce042a36ba9be4ff2e4ca97e..6ff3afbc00f295d7a459eec32d0eff75bcc7e719 100644 (file)
--- 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;
 
index 440229102ecc8d548635c70dc1d100b5af76b186..831e7eb674c4c728d0be1b01c17ccf0635370d25 100644 (file)
@@ -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; i<vg_list_size(indexables); i++ )
@@ -482,6 +484,13 @@ entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
       mdl_transform_m4x3( &challenge->transform, 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;
index e2ec14af8edd774ad28c7f441b624d3c66109d4c..1bbc2ca21a7f52a6219e1cef80fdd36bad20480a 100644 (file)
@@ -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
     * ----------------------------------------------------------------
index 6055d887acbe96ed7b7a5d68b53a2fc52765a2b3..ece92972c080625f6390e4252640363355572921 100644 (file)
@@ -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 );
index e2fbe7a2f8dc83f8b76dca47251ff76897743ab6..c7f6ec82ce4dbb249abf9937c06b75f57f4ec2e4 100644 (file)
@@ -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; i<mdl_arrcount(&world->ent_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){
index 6cc8c5df8dfad82512535504541fa425fcbae0a8..8531b8f1b14f0cc4ff1df6fca94b79c6caf8f575 100644 (file)
@@ -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] );
index 9eac40e3eda13e2c8bcc3acb94f48cb007aa8039..2355e20bf204bd942870515167bbe75d2366e502 100644 (file)
@@ -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; j<glider_count; j ++ ){
+      ent_glider *glider = mdl_arritm( &world->ent_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
index 540e0be2aa5bb85f29e0d0ddddd78c6d085f9e5b..ecbd3d22ec093a78cb68dfb128df537b2a3f3340 100644 (file)
@@ -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;
index b80e68e2777209a849d8039cbef257005df08d00..105e5467726d7d9c4b798f255bb48eb02dd80ddd 100644 (file)
@@ -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 <time.h>
 #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; i<world_render.text_particle_count; i++ ){
       struct text_particle *particle = &world_render.text_particles[i];
-      rb_object_debug( &particle->obj, 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; i<world_render.text_particle_count; i++ ){
       struct text_particle *particle = &world_render.text_particles[i];
 
       if( rb_global_has_space() ){
          rb_ct *buf = rb_global_buffer();
 
-         int l = rb_sphere__scene( particle->obj.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; j<l; j++ ){
-            buf[j].rba = &particle->obj.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; i<world_render.text_particle_count; i++ ){
       struct text_particle *particle = &world_render.text_particles[i];
-      rb_iter( &particle->obj.rb );
+      rb_iter( &particle->rb );
    }
 
    for( u32 i=0; i<world_render.text_particle_count; i++ ){
       struct text_particle *particle = &world_render.text_particles[i];
-      rb_update_transform( &particle->obj.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 */
index 9e8d38c14a88ad50d8b9d904eb60bdc93f519479..176425dfea664ba49112f004dc90c993602e64b0 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "world.h"
 #include "world_routes.h"
+#include "scene.h"
 
 struct world_sfd{
    GLuint tex_scoretex;
index 085e25250ac8217ea746c97bdf918c67105f79f6..62e133d72d7cd31181621dae41668e32378a0645 100644 (file)
@@ -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 );