('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 ):
#{
("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),
_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),
("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' }
#}
#}
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 ''}")
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':#{
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))
#}
#}
+class SR_OBJECT_ENT_GLIDER(bpy.types.PropertyGroup):#{
+ nothing: bpy.props.StringProperty()
+#}
class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{
subtype: bpy.props.EnumProperty(
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):#{
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",
#}
#}
+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
#}
#}
#}
+ 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
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 )
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 \
_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" );
--- /dev/null
+#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 );
+ }
+}
--- /dev/null
+#pragma once
+#include "entity.h"
+
+static void ent_glider_call( world_instance *world, ent_call *call );
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;
#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 *);
[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) ){
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,
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 ){
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"
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"
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 )
#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
#include "vg/vg_mem_queue.h"
#include "network_common.h"
#include "dep/sqlite3/sqlite3.h"
-#include "highscores.h"
#include <pthread.h>
#include <unistd.h>
k_gui_icon_player,
k_gui_icon_rift_run_gold,
k_gui_icon_rift_run_silver,
+ k_gui_icon_glider,
k_gui_icon_count,
};
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]=
#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,
#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 };
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 */
#include "particle.h"
+#include "shaders/trail.h"
static void particle_spawn( particle_system *sys,
v3f co, v3f v, f32 lifetime, u32 colour ){
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;
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)
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){
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();
#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"
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
};
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 );
#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"
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
* ---------------------------
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;
[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,
};
/*
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 );
/* 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,
/* 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 );
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 );
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] );
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;
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){}
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
(
--- /dev/null
+#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 */
--- /dev/null
+#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 */
#include "model.h"
#include "skeleton.h"
#include "player_ragdoll.h"
-#include "rigidbody.h"
#include "shaders/model_character_view.h"
-#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"
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;
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] );
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 );
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 );
}
/*
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 );
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 */
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] );
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 );
}
}
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;
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;
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 );
}
* 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 ){
* 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;
audio_unlock();
}
}
-
-#endif /* PLAYER_RAGDOLL_C */
-#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{
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;
static void player_debug_ragdoll(void);
static void player_ragdoll_iter( struct player_ragdoll *rd );
-
-#endif /* PLAYER_RAGDOLL_H */
#include "gui.h"
#include "ent_miniworld.h"
#include "ent_region.h"
+#include "shaders/model_entity.h"
static i32 k_show_own_name = 0;
dest->active = 1;
dest->subsystem = frame->subsystem;
- dest->instance_id = frame->instance_id;
+ dest->flags = frame->flags;
bitpack_ctx ctx = {
.mode = k_bitpack_decompress,
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
* -------------------------------------------------------------*/
}
}
+ /* 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;
}
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,
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;
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 );
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 );
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 )
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 ];
}
* 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];
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,
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[] ){
f32 down_kbs;
struct player_effects_data effect_data;
+ bool render_glider;
}
list[ NETWORK_MAX_PLAYERS ];
f64 timestamp;
enum player_subsystem subsystem;
- u8 instance_id;
+ u8 flags;
u16 boundary_hash;
union interp_animdata {
struct player_basic_info_animator __basic;
}
data;
+
+ struct remote_glider_animator data_glider;
}
frames[ NETWORK_BUFFERFRAMES ];
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;
#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;
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 */
*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;
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;
&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],
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]);
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);
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;
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];
}
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;
[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 ];
}
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 ){
/* 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 );
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;
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 ),
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 );
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 );
}
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 ) ){
}
}
-#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" );
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] );
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 {
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 );
#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[] = {
* 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++ ){
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 );
}
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){
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;
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;
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 );
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;
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;
}
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;
}
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] );
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 */
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 ){
* 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 );
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 ++;
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;
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 );
}
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 ],
-#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"
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 )
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 );
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;
}
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;
*/
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 );
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 ) ){
*/
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 );
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);
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
*/
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 );
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);
}
}
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) ||
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;
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 );
}
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,
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){
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 ){
audio_unlock();
}
-#endif /* PLAYER_DEVICE_WALK_H */
#include "player.h"
#include "player_api.h"
-#include "rigidbody.h"
+#include "vg/vg_rigidbody.h"
#define PLAYER_JUMP_EPSILON 0.1 /* 100ms jump allowance */
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+#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;
+}
+
aWorldCo = world_pos0;
aColour = a_colour;
aUv = a_uv;
- aNorm = mat3(uMdl) * a_norm;
+ aNorm = normalize( mat3(uMdl) * a_norm );
aCo = a_co;
}
" 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"
""},
" 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"
""},
" 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"
""},
" 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"
""},
" 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"
""},
" 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"
""},
" 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"
""},
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 );
}
" 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"
""},
};
--- /dev/null
+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 );
+}
--- /dev/null
+#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 */
--- /dev/null
+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;
+}
* =============================================================================
*
* Copyright . . . -----, ,----- ,---. .---.
- * 2021-2023 |\ /| | / | | | | /|
+ * 2021-2024 |\ /| | / | | | | /|
* | \ / | +-- / +----- +---' | / |
* | \ / | | / | | \ | / |
* | \/ | | / | | \ | / |
#define SR_NETWORKED
#define VG_AUDIO_FORCE_COMPRESSED
#define SDL_MAIN_HANDLED
+#define SR_ALLOW_REWIND_HUB
#ifndef VG_RELEASE
#define VG_DEVWINDOW
#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;
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 );
world_routes_fixedupdate( world_current_instance() );
player__update();
vehicle_update_fixed();
+ testing_update();
}
static void vg_post_update(void){
particle_system_prerender( &particles_env );
particle_system_render( &particles_env, &skaterift.cam );
+ player_glide_render_effects( &skaterift.cam );
+
/*
* render transition
*/
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 );
#include "vg/vg.h"
#include "world.h"
#include "addon.h"
+#include "trail.h"
enum skaterift_rt {
k_skaterift_rt_workshop_preview,
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 */
--- /dev/null
+#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 );
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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 */
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" );
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 );
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
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 );
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
* -------------------------------------------------------------*/
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] );
/* 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{
}
}
-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 );
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],
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 );
}
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;
#define VEHICLE_H
#include "skaterift.h"
-#include "rigidbody.h"
+#include "vg/vg_rigidbody.h"
#include "player.h"
#include "world.h"
#include "world_physics.h"
struct drivable_vehicle
{
int alive, inside;
- rb_object obj;
+ rigidbody rb;
v3f wheels[4];
}
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[] );
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
ent_cubemap,
ent_miniworld,
ent_prop,
- ent_region;
+ ent_region,
+ ent_glider;
enum skybox {
k_skybox_default,
mesh_water;
u32 cubemap_cooldown, cubemap_side;
- rb_object rb_geo;
-
/* leaderboards */
struct leaderboard_cache *leaderboard_cache;
#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;
{ 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++ )
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" );
}
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;
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 );
/* 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
* ----------------------------------------------------------------
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 );
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){
#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] );
#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();
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;
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 );
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 ){
}
render_remote_players( world, cam );
+ render_other_entities( world, cam );
ent_miniworld_render( world, cam );
if( stenciled ){
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
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;
+#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"
#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 )
{
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;
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 );
}
}
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];
}
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 );
glEnable( GL_CULL_FACE );
glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } );
}
-
-#endif /* ROUTES_C */
#include "world.h"
#include "world_routes.h"
+#include "scene.h"
struct world_sfd{
GLuint tex_scoretex;
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 );