From 342fcbf6fda017bdd38d56ce0fa7c9e59e589f3b Mon Sep 17 00:00:00 2001 From: hgn Date: Tue, 23 May 2023 02:29:21 +0100 Subject: [PATCH] the luxuries of a modern C compiler --- ent_skateshop.c | 63 ++- ent_skateshop.h | 1 + entity.c | 3 +- entity.h | 1 + gui.h | 13 +- menu.h | 2 + player.c | 23 +- player.h | 12 +- player_common.c | 4 +- player_common.h | 3 + player_ragdoll.c | 2 +- player_render.c | 16 +- player_render.h | 13 + player_skate.c | 14 +- player_walk.c | 4 +- render.h | 13 - skaterift.c | 118 ++--- skaterift.h | 1 + vehicle.c | 273 ++++++++++ vehicle.h | 282 +---------- workshop.c | 5 +- workshop.h | 6 + world.c | 268 ++++++++++ world.h | 853 ++----------------------------- world_audio.c | 143 ++++++ world_audio.h | 11 + world_entity.c | 259 ++++++++++ world_entity.h | 16 + world_gate.c | 336 ++++++++++++ world_gate.h | 245 +-------- world_gen.c | 628 +++++++++++++++++++++++ world_gen.h | 937 +--------------------------------- world_info.h | 2 +- world_load.c | 369 ++++++++++++++ world_load.h | 32 ++ world_physics.c | 110 ++++ world_physics.h | 24 + world_render.c | 530 +++++++++++++++++++ world_render.h | 539 ++------------------ world_routes.c | 1261 ++++++++++++++++++++++++++++++++++++++++++++++ world_routes.h | 1248 +-------------------------------------------- world_sfd.c | 213 ++++++++ world_sfd.h | 217 +------- world_volumes.c | 55 ++ world_volumes.h | 56 +- world_water.c | 227 +++++++++ world_water.h | 223 +------- 47 files changed, 5050 insertions(+), 4624 deletions(-) create mode 100644 vehicle.c create mode 100644 world.c create mode 100644 world_audio.c create mode 100644 world_audio.h create mode 100644 world_entity.c create mode 100644 world_entity.h create mode 100644 world_gate.c create mode 100644 world_gen.c create mode 100644 world_load.c create mode 100644 world_load.h create mode 100644 world_physics.c create mode 100644 world_physics.h create mode 100644 world_render.c create mode 100644 world_routes.c create mode 100644 world_sfd.c create mode 100644 world_volumes.c create mode 100644 world_water.c diff --git a/ent_skateshop.c b/ent_skateshop.c index a3e12dc..19b868f 100644 --- a/ent_skateshop.c +++ b/ent_skateshop.c @@ -10,6 +10,7 @@ #include "gui.h" #include "menu.h" #include "pointcloud.h" +#include "highscores.h" /* * Checks string equality but does a hash check first @@ -102,12 +103,6 @@ VG_STATIC void skateshop_update_viewpage(void) } } -/* generic reciever */ -VG_STATIC void workshop_async_any_complete( void *data, u32 size ) -{ - skaterift_end_op(); -} - /* * op/subroutine: k_workshop_op_item_load * ----------------------------------------------------------------------------- @@ -457,7 +452,7 @@ VG_STATIC void world_scan_thread( void *_args ) vg_str file = folder; vg_strcat( &file, "/" ); vg_strcat( &file, entry->d_name ); - if( !vg_strgood( &file ) ) break; + if( !vg_strgood( &file ) ) continue; char *ext = vg_strch( &file, '.' ); if( !ext ) continue; @@ -604,7 +599,7 @@ VG_STATIC void global_skateshop_preupdate(void) if( !global_skateshop.active ) return; - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); ent_skateshop *shop = global_skateshop.ptr_ent; @@ -719,6 +714,10 @@ VG_STATIC void global_skateshop_preupdate(void) if( global_skateshop.world_registry_count ){ gui_helper_action( axis_display_string(k_sraxis_mbrowse_h), "browse" ); } + + if( skaterift.async_op == k_async_op_none ){ + gui_helper_action( button_display_string(k_srbind_maccept), "load" ); + } int change = 0; if( button_down( k_srbind_mleft ) ){ @@ -736,14 +735,20 @@ VG_STATIC void global_skateshop_preupdate(void) } } - if( change && (pointcloud.anim == k_pointcloud_anim_idle) ){ + if( change && (pointcloud.anim == k_pointcloud_anim_idle) && + pointcloud.visibility == 1.0f ){ pointcloud.anim = k_pointcloud_anim_hiding; pointcloud.anim_start = vg.time; } - if( button_down( k_srbind_maccept ) ){ + if( (skaterift.async_op == k_async_op_none ) && + button_down( k_srbind_maccept ) ) + { + struct registry_world *rw = &global_skateshop.world_registry[ + global_skateshop.selected_world_id ]; + vg_info( "Select world (%u)\n", global_skateshop.selected_world_id ); - global_skateshop_exit(); + skaterift_change_world( rw->foldername ); return; } } @@ -759,7 +764,7 @@ VG_STATIC void global_skateshop_preupdate(void) VG_STATIC void skateshop_render_boardshop(void) { - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); ent_skateshop *shop = global_skateshop.ptr_ent; u32 slot_count = vg_list_size(global_skateshop.shop_view_slots); @@ -832,7 +837,7 @@ fade_out:; float scale = 0.2f, thickness = 0.03f; - font3d_bind( &world_global.font, &main_camera ); + font3d_bind( &gui.font, &main_camera ); shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} ); /* Selection counter @@ -846,7 +851,7 @@ fade_out:; m4x3_mul( mrack, mlocal, mmdl ); if( global_skateshop.registry_count == 0 ){ - font3d_simple_draw( &world_global.font, 0, + font3d_simple_draw( &gui.font, 0, "Nothing installed", &main_camera, mmdl ); } else{ @@ -857,7 +862,7 @@ fade_out:; i+=highscore_intl( buf+i, global_skateshop.registry_count, 3 ); buf[i++] = '\0'; - font3d_simple_draw( &world_global.font, 0, buf, &main_camera, mmdl ); + font3d_simple_draw( &gui.font, 0, buf, &main_camera, mmdl ); } struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded(); @@ -871,25 +876,23 @@ fade_out:; * ----------------------------------------------------------------- */ m3x3_zero( mlocal ); m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } ); - mlocal[3][0] = -font3d_string_width( &world_global.font, 0, info->title ); + mlocal[3][0] = -font3d_string_width( &gui.font, 0, info->title ); mlocal[3][0] *= scale*0.5f; mlocal[3][1] = 0.1f; mlocal[3][2] = 0.0f; m4x3_mul( mtext, mlocal, mmdl ); - font3d_simple_draw( &world_global.font, 0, info->title, &main_camera, mmdl ); + font3d_simple_draw( &gui.font, 0, info->title, &main_camera, mmdl ); /* Author name * ----------------------------------------------------------------- */ scale *= 0.4f; m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } ); - mlocal[3][0] = -font3d_string_width( &world_global.font, 0, - info->author_name ); + mlocal[3][0] = -font3d_string_width( &gui.font, 0, info->author_name ); mlocal[3][0] *= scale*0.5f; mlocal[3][1] = 0.0f; mlocal[3][2] = 0.0f; m4x3_mul( mtext, mlocal, mmdl ); - font3d_simple_draw( &world_global.font, 0, - info->author_name, &main_camera, mmdl ); + font3d_simple_draw( &gui.font, 0, info->author_name, &main_camera, mmdl ); } VG_STATIC void skateshop_render_charshop(void) @@ -898,7 +901,7 @@ VG_STATIC void skateshop_render_charshop(void) VG_STATIC void skateshop_render_worldshop(void) { - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); ent_skateshop *shop = global_skateshop.ptr_ent; ent_marker *mark_display = mdl_arritm( &world->ent_marker, @@ -936,29 +939,33 @@ VG_STATIC void skateshop_render_worldshop(void) vg_strcat( &info, "No worlds installed" ); } + if( skaterift.async_op == k_async_op_world_loading || + skaterift.async_op == k_async_op_world_preloading ){ + vg_strcat( &info, "Loading..." ); + } + m4x3f mtext,mlocal,mtextmdl; mdl_transform_m4x3( &mark_info->transform, mtext ); - font3d_bind( &world_global.font, &main_camera ); + font3d_bind( &gui.font, &main_camera ); shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} ); float scale = 0.2f, thickness = 0.015f, scale1 = 0.08f; m3x3_zero( mlocal ); m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } ); - mlocal[3][0] = -font3d_string_width( &world_global.font, 0, buftext ); + mlocal[3][0] = -font3d_string_width( &gui.font, 0, buftext ); mlocal[3][0] *= scale*0.5f; mlocal[3][1] = 0.1f; mlocal[3][2] = 0.0f; m4x3_mul( mtext, mlocal, mtextmdl ); - font3d_simple_draw( &world_global.font, 0, buftext, &main_camera, mtextmdl ); + font3d_simple_draw( &gui.font, 0, buftext, &main_camera, mtextmdl ); m3x3_setdiagonalv3( mlocal, (v3f){ scale1, scale1, thickness } ); - mlocal[3][0] = -font3d_string_width( &world_global.font, 0, bufsubtext ); + mlocal[3][0] = -font3d_string_width( &gui.font, 0, bufsubtext ); mlocal[3][0] *= scale1*0.5f; mlocal[3][1] = -scale1*0.3f; m4x3_mul( mtext, mlocal, mtextmdl ); - font3d_simple_draw( &world_global.font, 0, bufsubtext, - &main_camera, mtextmdl ); + font3d_simple_draw( &gui.font, 0, bufsubtext, &main_camera, mtextmdl ); /* pointcloud */ m4x3f mmdl; diff --git a/ent_skateshop.h b/ent_skateshop.h index f73b875..93ebbec 100644 --- a/ent_skateshop.h +++ b/ent_skateshop.h @@ -81,6 +81,7 @@ struct{ } static global_skateshop; +VG_STATIC void global_skateshop_exit(void); VG_STATIC void watch_cache_board( struct cache_board *ptr ); VG_STATIC void unwatch_cache_board( struct cache_board *ptr ); diff --git a/entity.c b/entity.c index 1d91c24..1e85e56 100644 --- a/entity.c +++ b/entity.c @@ -1,8 +1,9 @@ #ifndef ENTITY_C #define ENTITY_C -#include "entity.h" #include "world.h" +#include "entity.h" +#include "world_entity.h" #include "ent_skateshop.c" diff --git a/entity.h b/entity.h index 998557e..1de6f70 100644 --- a/entity.h +++ b/entity.h @@ -383,5 +383,6 @@ struct ent_call{ }; #include "world.h" +VG_STATIC void entity_call( world_instance *world, ent_call *call ); #endif /* ENTITY_H */ diff --git a/gui.h b/gui.h index 2ec0a50..99345c1 100644 --- a/gui.h +++ b/gui.h @@ -10,7 +10,8 @@ struct{ helpers[4]; u32 helper_count; - float factive; + f32 factive; + font3d font; } static gui; @@ -51,8 +52,7 @@ void gui_draw(void) render_fsquad1(); } - font3d *font = &world_global.font; - font3d_bind( font, &ortho ); + font3d_bind( &gui.font, &ortho ); float dy = ft/0.79f, scale = dy*0x1p-4f*0.75f; @@ -73,7 +73,7 @@ void gui_draw(void) shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} ); struct font3d_render render; - font3d_begin( font, 2, &ortho, mmdl, &render ); + font3d_begin( &gui.font, 2, &ortho, mmdl, &render ); render.u8pch = (u8*)helper->bindstr; font3d_draw( &render ); @@ -103,4 +103,9 @@ void gui_helper_action( const char *bindstr, const char *text ) helper->text = text; } +VG_STATIC void gui_init(void) +{ + font3d_load( &gui.font, "models/rs_font.mdl", vg_mem.rtmemory ); +} + #endif /* GUI_H */ diff --git a/menu.h b/menu.h index 0707e73..dd2cf68 100644 --- a/menu.h +++ b/menu.h @@ -7,6 +7,8 @@ #include "player.h" #include "conf.h" #include "shaders/model_menu.h" +#include "audio.h" +#include "input.h" #define MENU_STACK_SIZE 8 diff --git a/player.c b/player.c index 0339a8d..145a928 100644 --- a/player.c +++ b/player.c @@ -5,6 +5,8 @@ #include "camera.h" #include "player_model.h" #include "input.h" +#include "world.h" +#include "audio.h" VG_STATIC int localplayer_cmd_respawn( int argc, const char *argv[] ) { @@ -130,9 +132,9 @@ void player__pre_update( player_instance *player ) } if( button_down( k_srbind_reset ) && !player->immobile ){ - double delta = world_global.time - world_global.last_use; + f64 delta = world_static.time - world_static.last_use; - if( (delta <= RESET_MAX_TIME) && (world_global.last_use != 0.0) ){ + if( (delta <= RESET_MAX_TIME) && (world_static.last_use != 0.0) ){ player->rewinding = 1; player->rewind_sound_wait = 1; player->rewind_time = (double)player->rewind_length - 0.0001; @@ -219,11 +221,11 @@ void player__post_update( player_instance *player ) PLAYER_API void player__pass_gate( player_instance *player, ent_gate *gate ) { - world_routes_fracture( get_active_world(), gate, + world_routes_fracture( world_current_instance(), gate, player->rb.co, player->rb.v ); player->gate_waiting = gate; - world_routes_activate_entry_gate( get_active_world(), gate ); + world_routes_activate_entry_gate( world_current_instance(), gate ); m4x3_mulv( gate->transport, player->tpv_lpf, player->tpv_lpf ); m3x3_mulv( gate->transport, player->cam_velocity_smooth, @@ -250,9 +252,9 @@ void player__pass_gate( player_instance *player, ent_gate *gate ) player_save_rewind_frame( player ); if( gate->type == k_gate_type_nonlocel ) - world_global.active_world = gate->target; + world_static.active_world = gate->target; - world_global.in_volume = 0; + world_volumes.inside = 0; audio_lock(); audio_oneshot( &audio_gate_pass, 1.0f, 0.0f ); @@ -346,4 +348,13 @@ PLAYER_API void player__kill( player_instance *player ) } +/* implementation */ +#include "player_common.c" +#include "player_walk.c" +#include "player_skate.c" +#include "player_dead.c" +#include "player_drive.c" +#include "player_render.c" +#include "player_ragdoll.c" + #endif /* PLAYER_C */ diff --git a/player.h b/player.h index 0911e8f..b52201b 100644 --- a/player.h +++ b/player.h @@ -228,15 +228,7 @@ PLAYER_API void player__im_gui( player_instance *player ); PLAYER_API void player__spawn( player_instance *player, ent_spawn *rp ); PLAYER_API void player__kill( player_instance *player ); -/* implementation */ - -#include "player.c" -#include "player_common.c" -#include "player_walk.c" -#include "player_skate.c" -#include "player_dead.c" -#include "player_drive.c" -#include "player_render.c" -#include "player_ragdoll.c" +VG_STATIC int localplayer_cmd_respawn( int argc, const char *argv[] ); +VG_STATIC void player_apply_transport_to_cam( m4x3f transport ); #endif /* PLAYER_H */ diff --git a/player_common.c b/player_common.c index ee597d0..22c26a2 100644 --- a/player_common.c +++ b/player_common.c @@ -49,7 +49,7 @@ VG_STATIC void player_camera_portal_correction( player_instance *player ) vg_success( "Plane cleared\n" ); player_apply_transport_to_cam( player->gate_waiting->transport ); player->gate_waiting = NULL; - player->viewable_world = get_active_world(); + player->viewable_world = world_current_instance(); } else{ /* de-transform camera and player back */ @@ -63,8 +63,6 @@ VG_STATIC void player_camera_portal_correction( player_instance *player ) } } -static v3f TEMP_TPV_EXTRA; - VG_STATIC void player__cam_iterate( player_instance *player ) { struct player_avatar *av = player->playeravatar; diff --git a/player_common.h b/player_common.h index 6c825d8..5cf4411 100644 --- a/player_common.h +++ b/player_common.h @@ -3,8 +3,11 @@ #include "player_api.h" +static v3f TEMP_TPV_EXTRA; /* TODO: what? */ + VG_STATIC void player_look( player_instance *player, v3f angles ); VG_STATIC void player__cam_iterate( player_instance *player ); VG_STATIC void player_vector_angles( v3f angles, v3f v, float C, float k ); +struct player_board *player_get_player_board( struct player_instance *player ); #endif /* PLAYER_COMMON_H */ diff --git a/player_ragdoll.c b/player_ragdoll.c index 7b53088..aabf6f9 100644 --- a/player_ragdoll.c +++ b/player_ragdoll.c @@ -269,7 +269,7 @@ VG_STATIC void player_debug_ragdoll(void) */ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd ) { - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); int run_sim = 0; ragdoll_frame ++; diff --git a/player_render.c b/player_render.c index 8f25215..011d041 100644 --- a/player_render.c +++ b/player_render.c @@ -6,6 +6,7 @@ #include "camera.h" #include "player_model.h" #include "ent_skateshop.h" +#include "audio.h" #include "shaders/model_character_view.h" #include "shaders/model_board_view.h" @@ -174,7 +175,7 @@ VG_STATIC void player__pre_render( player_instance *player ) v3_zero( vp1 ); } - struct ub_world_lighting *ubo = &get_active_world()->ub_lighting; + struct ub_world_lighting *ubo = &world_current_instance()->ub_lighting; m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp0, ubo->g_board_0 ); m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp1, ubo->g_board_1 ); @@ -194,11 +195,11 @@ VG_STATIC void player__pre_render( player_instance *player ) player->rewind_length = 1; player->rewind_total_length = 0.0f; player->rewind_accum = 0.0f; - world_global.sky_target_rate = 1.0; - world_global.time = world_global.last_use; + world_render.sky_target_rate = 1.0; + world_static.time = world_static.last_use; } else{ - world_global.sky_target_rate = -100.0; + world_render.sky_target_rate = -100.0; float budget = vg.time_delta, overall_length = player->rewind_length; @@ -282,11 +283,6 @@ VG_STATIC void player__pre_render( player_instance *player ) player__cam_iterate( player ); } -enum board_shader{ - k_board_shader_player, - k_board_shader_entity -}; - VG_STATIC void render_board( camera *cam, world_instance *world, struct player_board *board, m4x3f root, enum board_shader shader ) @@ -423,7 +419,7 @@ PLAYER_API void player__render( camera *cam, player_instance *player ) inverse[2] = cam->farz-cam->nearz; shader_model_character_view_uInverseRatioMain( inverse ); - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); world_link_lighting_ub( world, _shader_model_character_view.id ); world_bind_position_texture( world, _shader_model_character_view.id, _uniform_model_character_view_g_world_depth, 2 ); diff --git a/player_render.h b/player_render.h index e9d17bb..f6c8194 100644 --- a/player_render.h +++ b/player_render.h @@ -3,6 +3,8 @@ #include "model.h" #include "skeleton.h" +#include "camera.h" +#include "world.h" struct player_avatar { @@ -56,4 +58,15 @@ struct player_board board; }; +enum board_shader{ + k_board_shader_player, + k_board_shader_entity +}; + +VG_STATIC void player_board_load( struct player_board *mdl, const char *path ); +VG_STATIC void player_board_unload( struct player_board *mdl ); +VG_STATIC void render_board( camera *cam, world_instance *world, + struct player_board *board, m4x3f root, + enum board_shader shader ); + #endif /* PLAYER_RENDER_H */ diff --git a/player_skate.c b/player_skate.c index e208a49..f2a1cd0 100644 --- a/player_skate.c +++ b/player_skate.c @@ -55,7 +55,7 @@ VG_STATIC int skate_collide_smooth( player_instance *player, m4x3f mtx, rb_sphere *sphere, rb_ct *man ) { - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); int len = 0; len = rb_sphere__scene( mtx, sphere, NULL, &world->rb_geo.inf.scene, man ); @@ -92,7 +92,7 @@ VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, v3f dir, float r, struct grind_info *inf ) { - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); v4f plane; v3_copy( dir, plane ); @@ -348,7 +348,7 @@ VG_STATIC int create_jumps_to_hit_target( player_instance *player, VG_STATIC void player__approximate_best_trajectory( player_instance *player ) { - world_instance *world0 = get_active_world(); + world_instance *world0 = world_current_instance(); struct player_skate *s = &player->_skate; float k_trace_delta = k_rb_delta * 10.0f; @@ -502,7 +502,7 @@ void player__approximate_best_trajectory( player_instance *player ) m3x3_mul( gate->transport, basis, basis ); if( gate->type == k_gate_type_nonlocel ){ - trace_world = &world_global.worlds[ gate->target ]; + trace_world = &world_static.worlds[ gate->target ]; } } } @@ -1320,7 +1320,7 @@ int skate_compute_surface_alignment( player_instance *player, v3f surface_normal, v3f axel_dir ) { struct player_skate *s = &player->_skate; - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); v3f truck, left, right; m4x3_mulv( player->rb.to_world, ra, truck ); @@ -1533,7 +1533,7 @@ VG_STATIC int skate_point_visible( v3f origin, v3f target ) v3_muls( dir, 1.0f/ray.dist, dir ); ray.dist -= 0.025f; - if( ray_world( get_active_world(), origin, dir, &ray ) ) + if( ray_world( world_current_instance(), origin, dir, &ray ) ) return 0; return 1; @@ -2075,7 +2075,7 @@ VG_STATIC enum skate_activity skate_availible_grind( player_instance *player ) VG_STATIC void player__skate_update( player_instance *player ) { struct player_skate *s = &player->_skate; - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); v3_copy( player->rb.co, s->state.prev_pos ); s->state.activity_prev = s->state.activity; diff --git a/player_walk.c b/player_walk.c index c3b6c2e..10d0094 100644 --- a/player_walk.c +++ b/player_walk.c @@ -120,7 +120,7 @@ VG_STATIC void player_walk_drop_in_overhang_transform( player_instance *player, VG_STATIC int player_walk_scan_for_drop_in( player_instance *player ) { struct player_walk *w = &player->_walk; - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); v3f dir, center; q_mulv( player->rb.q, (v3f){0.0f,0.0f,1.0f}, dir ); @@ -390,7 +390,7 @@ VG_STATIC void player__walk_update( player_instance *player ) joystick_state( k_srjoystick_steer, steer ); w->move_speed = v2_length( steer ); - world_instance *world = get_active_world(); + world_instance *world = world_current_instance(); /* * Collision detection diff --git a/render.h b/render.h index 541f047..d626b7a 100644 --- a/render.h +++ b/render.h @@ -11,21 +11,8 @@ #include "shaders/blitblur.h" #include "shaders/blitcolour.h" -#if 0 -#include "shaders/standard.h" -#include "shaders/vblend.h" -#endif - #define WORKSHOP_PREVIEW_WIDTH 504 #define WORKSHOP_PREVIEW_HEIGHT 336 - -VG_STATIC void render_water_texture( world_instance *world, camera *cam, - int layer_depth ); -VG_STATIC void render_water_surface( world_instance *world, camera *cam ); -VG_STATIC void render_world( world_instance *world, camera *cam, - int layer_depth ); -VG_STATIC void render_world_depth( world_instance *world, camera *cam ); - #ifndef RENDER_H #define RENDER_H diff --git a/skaterift.c b/skaterift.c index 6f0364a..d22846f 100644 --- a/skaterift.c +++ b/skaterift.c @@ -16,27 +16,38 @@ #define SR_NETWORKED #define VG_DEVWINDOW +/* + * system headers + * --------------------- */ + #include "common.h" #include "conf.h" #include "steam.h" #include "render.h" #include "audio.h" + #include "world.h" + #include "font.h" #include "player.h" -#include "entity.c" -#include "workshop.c" - #include "network.h" #include "menu.h" #include "vehicle.h" #include "pointcloud.h" +/* unity build + * ----------------- */ + +#include "world.c" +#include "player.c" +#include "vehicle.c" +#include "entity.c" +#include "workshop.c" + static struct player_avatar localplayer_avatar; static struct player_model localplayer_models[3]; - int main( int argc, char *argv[] ) { vg_mem.use_libc_malloc = 0; @@ -95,7 +106,7 @@ void temp_update_playermodel(void){ VG_STATIC void async_skaterift_complete( void *payload, u32 size ) { - localplayer.viewable_world = get_active_world(); + localplayer.viewable_world = world_current_instance(); localplayer_cmd_respawn( 1, (const char *[]){ "start" } ); skaterift_end_op(); @@ -111,8 +122,7 @@ VG_STATIC void vg_load(void) vg_loader_step( world_init, NULL ); vg_loader_step( vehicle_init, NULL ); vg_loader_step( font3d_init, NULL ); - - font3d_load( &world_global.font, "models/rs_font.mdl", vg_mem.rtmemory ); + vg_loader_step( gui_init, NULL ); vg_loader_step( player_init, NULL ); vg_loader_step( player_ragdoll_init, NULL ); @@ -135,8 +145,13 @@ VG_STATIC void vg_load(void) vg_loader_step( audio_init, audio_free ); /* 'systems' are completely loaded now */ - /* load home/permanent world */ - world_load( 0, "maps/mp_spawn/main.mdl" ); + + /* load home/permanent world manually */ + strcpy( world_loader.name, "mp_spawn" ); + world_loader.generate_point_cloud = 1; + world_loader.world_index = 0; + world_loader.location = k_world_load_type_local; + world_load_mdl( "maps/mp_spawn/main.mdl" ); vg_console_load_autos(); menu_link(); @@ -167,8 +182,8 @@ VG_STATIC void vg_update(void) player__pre_update( &localplayer ); global_skateshop_preupdate(); - world_update( get_active_world(), localplayer.rb.co ); - audio_ambient_sprites_update( get_active_world(), localplayer.rb.co ); + world_update( world_current_instance(), localplayer.rb.co ); + audio_ambient_sprites_update( world_current_instance(), localplayer.rb.co ); //gui_helper_action( localplayer.input_use, "\x7f Hello \x1f""A \x1e\x84" ); } @@ -176,7 +191,7 @@ VG_STATIC void vg_update_fixed(void) { if( skaterift.async_op == k_async_op_clientloading ) return; - world_routes_fixedupdate( get_active_world() ); + world_routes_fixedupdate( world_current_instance() ); player__update( &localplayer ); vehicle_update_fixed(); } @@ -293,9 +308,9 @@ VG_STATIC void render_scene(void) return; } - for( u32 i=0; istatus == k_world_status_unloading ){ - if( world_freeable( inst ) ){ - world_free( inst ); - } - return; - } - } - - vg_info( "worlds cleared, begining load\n" ); - skaterift_shift_op( k_async_op_world_loading ); - - /* finally can start the loader */ - vg_loader_start( skaterift_world_changer_thread, NULL ); -} - -/* places all loaded worlds into unloading state */ -static void skaterift_change_world( const char *world_path ) -{ - vg_info( "switching to %s\n", world_path ); - - if( world_global.active_world != 0 ){ - vg_error( "Cannot change worlds while in non-root world\n" ); - } - else{ - skaterift_begin_op( k_async_op_world_preloading ); - - vg_linear_clear( vg_mem.scratch ); - world_global.load_target = vg_linear_alloc( vg_mem.scratch, 1024 ); - vg_strncpy( world_path, world_global.load_target, - 1024, k_strncpy_overflow_fatal ); - - vg_info( "unloading old worlds\n" ); - world_unlink_nonlocal( &world_global.worlds[0] ); - - for( u32 i=1; istatus == k_world_status_loaded ){ - inst->status = k_world_status_unloading; - world_fadeout_audio( inst ); - } - } - } -} - -static int skaterift_change_world_command( int argc, const char *argv[] ) -{ - if( argc == 1 ) - skaterift_change_world( argv[0] ); - - return 0; -} #else diff --git a/skaterift.h b/skaterift.h index 5d250a1..ca31f23 100644 --- a/skaterift.h +++ b/skaterift.h @@ -2,6 +2,7 @@ #define SKATERIFT_H #include "common.h" +#include "world.h" struct{ enum async_operation{ diff --git a/vehicle.c b/vehicle.c new file mode 100644 index 0000000..ad3e00d --- /dev/null +++ b/vehicle.c @@ -0,0 +1,273 @@ +#ifndef VEHICLE_C +#define VEHICLE_C + +#include "vehicle.h" + +VG_STATIC int spawn_car( int argc, const char *argv[] ) +{ + v3f ra, rb, rx; + v3_copy( main_camera.pos, ra ); + v3_muladds( ra, main_camera.transform[2], -10.0f, rb ); + + float t; + if( spherecast_world( world_current_instance(), ra, rb, + gzoomer.obj.inf.sphere.radius, &t, rx ) != -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 ); + + rb_update_transform( &gzoomer.obj.rb ); + gzoomer.alive = 1; + + vg_success( "Spawned car\n" ); + } + else{ + vg_error( "Can't spawn here\n" ); + } + + return 0; +} + +VG_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 ); + + VG_VAR_F32( k_car_spring, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_spring_damp, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_spring_length, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_wheel_radius, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_friction_lat, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_friction_roll, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_drive_force, flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_air_resistance,flags=VG_VAR_PERSISTENT ); + VG_VAR_F32( k_car_downforce, flags=VG_VAR_PERSISTENT ); + + VG_VAR_I32( gzoomer.inside ); + + vg_console_reg_cmd( "spawn_car", spawn_car, NULL ); + + v3_copy((v3f){ -1.0f, -0.25f, -1.5f }, gzoomer.wheels_local[0] ); + v3_copy((v3f){ 1.0f, -0.25f, -1.5f }, gzoomer.wheels_local[1] ); + v3_copy((v3f){ -1.0f, -0.25f, 1.5f }, gzoomer.wheels_local[2] ); + v3_copy((v3f){ 1.0f, -0.25f, 1.5f }, gzoomer.wheels_local[3] ); +} + +VG_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 ); + + +#if 1 + float t; + if( spherecast_world( world_current_instance(), pa, pb, + k_car_wheel_radius, &t, n ) == -1 ) + { t = 1.0f; + } + +#else + + v3f dir; + v3_muls( gzoomer.rb.up, -1.0f, dir ); + + ray_hit hit; + hit.dist = k_car_spring_length; + ray_world( pa, dir, &hit ); + + float t = hit.dist / k_car_spring_length; + +#endif + + v3f pc; + v3_lerp( pa, pb, t, pc ); + + m4x3f mtx; + m3x3_copy( gzoomer.obj.rb.to_world, mtx ); + v3_copy( pc, mtx[3] ); + debug_sphere( mtx, k_car_wheel_radius, VG__BLACK ); + vg_line( pa, pc, VG__WHITE ); + v3_copy( pc, gzoomer.wheels[index] ); + + if( t < 1.0f ){ + /* spring force */ + float Fv = (1.0f-t) * k_car_spring*k_rb_delta; + + v3f delta; + v3_sub( pa, gzoomer.obj.rb.co, delta ); + + v3f rv; + v3_cross( gzoomer.obj.rb.w, delta, rv ); + v3_add( gzoomer.obj.rb.v, rv, rv ); + + Fv += v3_dot( rv, gzoomer.obj.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] ); + + v3f F; + v3_muls( gzoomer.obj.rb.to_world[1], Fv, F ); + + rb_linear_impulse( &gzoomer.obj.rb, delta, F ); + + /* friction vectors + * -------------------------------------------------------------*/ + v3f tx, ty; + + if( index <= 1 ) + v3_cross( gzoomer.steerv, n, tx ); + else + v3_cross( n, gzoomer.obj.rb.to_world[2], tx ); + v3_cross( tx, n, ty ); + + v3_copy( tx, gzoomer.tangent_vectors[ index ][0] ); + v3_copy( ty, gzoomer.tangent_vectors[ index ][1] ); + + gzoomer.normal_forces[ index ] = Fv; + gzoomer.tangent_forces[ index ][0] = 0.0f; + gzoomer.tangent_forces[ index ][1] = 0.0f; + + /* orient inverse inertia tensors */ + v3f raW; + m3x3_mulv( gzoomer.obj.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 ); + + gzoomer.tangent_mass[index][0] = gzoomer.obj.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] += 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 ); + } + } + else{ + gzoomer.normal_forces[ index ] = 0.0f; + gzoomer.tangent_forces[ index ][0] = 0.0f; + gzoomer.tangent_forces[ index ][1] = 0.0f; + } +} + +VG_STATIC void vehicle_solve_friction(void) +{ + rigidbody *rb = &gzoomer.obj.rb; + for( int i=0; i<4; i++ ){ + v3f raW; + m3x3_mulv( rb->to_world, gzoomer.wheels_local[i], raW ); + + v3f rv; + v3_cross( rb->w, raW, rv ); + v3_add( rb->v, rv, rv ); + + float fx = k_car_friction_lat * gzoomer.normal_forces[i], + fy = k_car_friction_roll * gzoomer.normal_forces[i], + vtx = v3_dot( rv, gzoomer.tangent_vectors[i][0] ), + vty = v3_dot( rv, gzoomer.tangent_vectors[i][1] ), + lambdax = gzoomer.tangent_mass[i][0] * -vtx, + lambday = gzoomer.tangent_mass[i][1] * -vty; + + float tempx = gzoomer.tangent_forces[i][0], + tempy = gzoomer.tangent_forces[i][1]; + gzoomer.tangent_forces[i][0] = vg_clampf( tempx + lambdax, -fx, fx ); + gzoomer.tangent_forces[i][1] = vg_clampf( tempy + lambday, -fy, fy ); + lambdax = gzoomer.tangent_forces[i][0] - tempx; + lambday = gzoomer.tangent_forces[i][1] - tempy; + + v3f impulsex, impulsey; + v3_muls( gzoomer.tangent_vectors[i][0], lambdax, impulsex ); + v3_muls( gzoomer.tangent_vectors[i][1], lambday, impulsey ); + rb_linear_impulse( rb, raW, impulsex ); + rb_linear_impulse( rb, raW, impulsey ); + } +} + +VG_STATIC void vehicle_update_fixed(void) +{ + if( !gzoomer.alive ) + return; + + rigidbody *rb = &gzoomer.obj.rb; + + v3_muls( rb->to_world[2], -cosf(gzoomer.steer), gzoomer.steerv ); + v3_muladds( gzoomer.steerv, rb->to_world[0], + sinf(gzoomer.steer), gzoomer.steerv ); + + /* apply air resistance */ + v3f Fair, Fdown; + + v3_muls( rb->v, -k_car_air_resistance, Fair ); + v3_muls( rb->to_world[1], -fabsf(v3_dot( rb->v, rb->to_world[2] )) * + k_car_downforce, Fdown ); + + v3_muladds( rb->v, Fair, k_rb_delta, rb->v ); + v3_muladds( rb->v, Fdown, k_rb_delta, rb->v ); + + for( int i=0; i<4; i++ ) + vehicle_wheel_force( i ); + + rb_ct manifold[64]; + int len = rb_sphere__scene( rb->to_world, &gzoomer.obj.inf.sphere, NULL, + &world_current_instance()->rb_geo.inf.scene, + manifold ); + for( int j=0; jrb_geo.rb; + } + rb_manifold_filter_coplanar( manifold, len, 0.05f ); + + if( len > 1 ){ + rb_manifold_filter_backface( manifold, len ); + rb_manifold_filter_joint_edges( manifold, len, 0.05f ); + rb_manifold_filter_pairs( manifold, len, 0.05f ); + } + len = rb_manifold_apply_filtered( manifold, len ); + + rb_presolve_contacts( manifold, len ); + for( int i=0; i<8; i++ ){ + rb_solve_contacts( manifold, len ); + vehicle_solve_friction(); + } + + rb_iter( rb ); + rb_update_transform( rb ); +} + +VG_STATIC void vehicle_update_post(void) +{ + if( !gzoomer.alive ) + return; + + rb_object_debug( &gzoomer.obj, VG__WHITE ); + + /* draw friction vectors */ + v3f p0, px, py; + + for( int i=0; i<4; i++ ){ + v3_copy( gzoomer.wheels[i], p0 ); + v3_muladds( p0, gzoomer.tangent_vectors[i][0], 0.5f, px ); + v3_muladds( p0, gzoomer.tangent_vectors[i][1], 0.5f, py ); + + vg_line( p0, px, VG__RED ); + vg_line( p0, py, VG__GREEN ); + } +} + +#endif /* VEHICLE_H */ diff --git a/vehicle.h b/vehicle.h index c2429b9..b1f5a28 100644 --- a/vehicle.h +++ b/vehicle.h @@ -1,11 +1,11 @@ #ifndef VEHICLE_H #define VEHICLE_H -#define VG_GAME -#include "vg/vg.h" +#include "skaterift.h" #include "rigidbody.h" -#include "world.h" #include "player.h" +#include "world.h" +#include "world_physics.h" VG_STATIC float k_car_spring = 1.0f, k_car_spring_damp = 0.001f, @@ -18,7 +18,7 @@ VG_STATIC float k_car_spring = 1.0f, k_car_downforce = 0.5f; typedef struct drivable_vehicle drivable_vehicle; -VG_STATIC struct drivable_vehicle +struct drivable_vehicle { int alive, inside; rb_object obj; @@ -35,276 +35,16 @@ VG_STATIC struct drivable_vehicle v3f tangent_vectors[4][2]; v3f wheels_local[4]; } -gzoomer = +static gzoomer = { .obj = { .type = k_rb_shape_sphere, .inf.sphere.radius = 1.0f } }; -VG_STATIC int spawn_car( int argc, const char *argv[] ) -{ - v3f ra, rb, rx; - v3_copy( main_camera.pos, ra ); - v3_muladds( ra, main_camera.transform[2], -10.0f, rb ); - - float t; - if( spherecast_world( get_active_world(), ra, rb, - gzoomer.obj.inf.sphere.radius, &t, rx ) != -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 ); - - rb_update_transform( &gzoomer.obj.rb ); - gzoomer.alive = 1; - - vg_success( "Spawned car\n" ); - } - else{ - vg_error( "Can't spawn here\n" ); - } - - return 0; -} - -VG_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 ); - - VG_VAR_F32( k_car_spring, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_spring_damp, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_spring_length, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_wheel_radius, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_friction_lat, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_friction_roll, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_drive_force, flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_air_resistance,flags=VG_VAR_PERSISTENT ); - VG_VAR_F32( k_car_downforce, flags=VG_VAR_PERSISTENT ); - - VG_VAR_I32( gzoomer.inside ); - - vg_console_reg_cmd( "spawn_car", spawn_car, NULL ); - - v3_copy((v3f){ -1.0f, -0.25f, -1.5f }, gzoomer.wheels_local[0] ); - v3_copy((v3f){ 1.0f, -0.25f, -1.5f }, gzoomer.wheels_local[1] ); - v3_copy((v3f){ -1.0f, -0.25f, 1.5f }, gzoomer.wheels_local[2] ); - v3_copy((v3f){ 1.0f, -0.25f, 1.5f }, gzoomer.wheels_local[3] ); -} - -VG_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 ); - - -#if 1 - float t; - if( spherecast_world( get_active_world(), pa, pb, - k_car_wheel_radius, &t, n ) == -1 ) - { t = 1.0f; - } - -#else - - v3f dir; - v3_muls( gzoomer.rb.up, -1.0f, dir ); - - ray_hit hit; - hit.dist = k_car_spring_length; - ray_world( pa, dir, &hit ); - - float t = hit.dist / k_car_spring_length; - -#endif - - v3f pc; - v3_lerp( pa, pb, t, pc ); - - m4x3f mtx; - m3x3_copy( gzoomer.obj.rb.to_world, mtx ); - v3_copy( pc, mtx[3] ); - debug_sphere( mtx, k_car_wheel_radius, VG__BLACK ); - vg_line( pa, pc, VG__WHITE ); - v3_copy( pc, gzoomer.wheels[index] ); - - if( t < 1.0f ){ - /* spring force */ - float Fv = (1.0f-t) * k_car_spring*k_rb_delta; - - v3f delta; - v3_sub( pa, gzoomer.obj.rb.co, delta ); - - v3f rv; - v3_cross( gzoomer.obj.rb.w, delta, rv ); - v3_add( gzoomer.obj.rb.v, rv, rv ); - - Fv += v3_dot( rv, gzoomer.obj.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] ); - - v3f F; - v3_muls( gzoomer.obj.rb.to_world[1], Fv, F ); - - rb_linear_impulse( &gzoomer.obj.rb, delta, F ); - - /* friction vectors - * -------------------------------------------------------------*/ - v3f tx, ty; - - if( index <= 1 ) - v3_cross( gzoomer.steerv, n, tx ); - else - v3_cross( n, gzoomer.obj.rb.to_world[2], tx ); - v3_cross( tx, n, ty ); - - v3_copy( tx, gzoomer.tangent_vectors[ index ][0] ); - v3_copy( ty, gzoomer.tangent_vectors[ index ][1] ); - - gzoomer.normal_forces[ index ] = Fv; - gzoomer.tangent_forces[ index ][0] = 0.0f; - gzoomer.tangent_forces[ index ][1] = 0.0f; - - /* orient inverse inertia tensors */ - v3f raW; - m3x3_mulv( gzoomer.obj.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 ); - - gzoomer.tangent_mass[index][0] = gzoomer.obj.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] += 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 ); - } - } - else{ - gzoomer.normal_forces[ index ] = 0.0f; - gzoomer.tangent_forces[ index ][0] = 0.0f; - gzoomer.tangent_forces[ index ][1] = 0.0f; - } -} - -VG_STATIC void vehicle_solve_friction(void) -{ - rigidbody *rb = &gzoomer.obj.rb; - for( int i=0; i<4; i++ ){ - v3f raW; - m3x3_mulv( rb->to_world, gzoomer.wheels_local[i], raW ); - - v3f rv; - v3_cross( rb->w, raW, rv ); - v3_add( rb->v, rv, rv ); - - float fx = k_car_friction_lat * gzoomer.normal_forces[i], - fy = k_car_friction_roll * gzoomer.normal_forces[i], - vtx = v3_dot( rv, gzoomer.tangent_vectors[i][0] ), - vty = v3_dot( rv, gzoomer.tangent_vectors[i][1] ), - lambdax = gzoomer.tangent_mass[i][0] * -vtx, - lambday = gzoomer.tangent_mass[i][1] * -vty; - - float tempx = gzoomer.tangent_forces[i][0], - tempy = gzoomer.tangent_forces[i][1]; - gzoomer.tangent_forces[i][0] = vg_clampf( tempx + lambdax, -fx, fx ); - gzoomer.tangent_forces[i][1] = vg_clampf( tempy + lambday, -fy, fy ); - lambdax = gzoomer.tangent_forces[i][0] - tempx; - lambday = gzoomer.tangent_forces[i][1] - tempy; - - v3f impulsex, impulsey; - v3_muls( gzoomer.tangent_vectors[i][0], lambdax, impulsex ); - v3_muls( gzoomer.tangent_vectors[i][1], lambday, impulsey ); - rb_linear_impulse( rb, raW, impulsex ); - rb_linear_impulse( rb, raW, impulsey ); - } -} - -VG_STATIC void vehicle_update_fixed(void) -{ - if( !gzoomer.alive ) - return; - - rigidbody *rb = &gzoomer.obj.rb; - - v3_muls( rb->to_world[2], -cosf(gzoomer.steer), gzoomer.steerv ); - v3_muladds( gzoomer.steerv, rb->to_world[0], - sinf(gzoomer.steer), gzoomer.steerv ); - - /* apply air resistance */ - v3f Fair, Fdown; - - v3_muls( rb->v, -k_car_air_resistance, Fair ); - v3_muls( rb->to_world[1], -fabsf(v3_dot( rb->v, rb->to_world[2] )) * - k_car_downforce, Fdown ); - - v3_muladds( rb->v, Fair, k_rb_delta, rb->v ); - v3_muladds( rb->v, Fdown, k_rb_delta, rb->v ); - - for( int i=0; i<4; i++ ) - vehicle_wheel_force( i ); - - rb_ct manifold[64]; - int len = rb_sphere__scene( rb->to_world, &gzoomer.obj.inf.sphere, - NULL, &get_active_world()->rb_geo.inf.scene, - manifold ); - for( int j=0; jrb_geo.rb; - } - rb_manifold_filter_coplanar( manifold, len, 0.05f ); - - if( len > 1 ){ - rb_manifold_filter_backface( manifold, len ); - rb_manifold_filter_joint_edges( manifold, len, 0.05f ); - rb_manifold_filter_pairs( manifold, len, 0.05f ); - } - len = rb_manifold_apply_filtered( manifold, len ); - - rb_presolve_contacts( manifold, len ); - for( int i=0; i<8; i++ ){ - rb_solve_contacts( manifold, len ); - vehicle_solve_friction(); - } - - rb_iter( rb ); - rb_update_transform( rb ); -} - -VG_STATIC void vehicle_update_post(void) -{ - if( !gzoomer.alive ) - return; - - rb_object_debug( &gzoomer.obj, VG__WHITE ); - - /* draw friction vectors */ - v3f p0, px, py; - - for( int i=0; i<4; i++ ){ - v3_copy( gzoomer.wheels[i], p0 ); - v3_muladds( p0, gzoomer.tangent_vectors[i][0], 0.5f, px ); - v3_muladds( p0, gzoomer.tangent_vectors[i][1], 0.5f, py ); - - vg_line( p0, px, VG__RED ); - vg_line( p0, py, VG__GREEN ); - } -} +VG_STATIC int spawn_car( int argc, const char *argv[] ); +VG_STATIC void vehicle_init(void); +VG_STATIC void vehicle_wheel_force( int index ); +VG_STATIC void vehicle_solve_friction(void); +VG_STATIC void vehicle_update_fixed(void); +VG_STATIC void vehicle_update_post(void); #endif /* VEHICLE_H */ diff --git a/workshop.c b/workshop.c index d5eb820..4d84fa0 100644 --- a/workshop.c +++ b/workshop.c @@ -9,6 +9,9 @@ #include "vg/vg_steam_auth.h" #include "vg/vg_steam_ugc.h" #include "vg/vg_steam_friends.h" +#include "conf.h" +#include "steam.h" +#include "highscores.h" #define WORKSHOP_VIEW_PER_PAGE 15 @@ -889,7 +892,7 @@ VG_STATIC void workshop_init(void) VG_STATIC void workshop_find_preview_entity(void) { - workshop_form.view_world = get_active_world(); + workshop_form.view_world = world_current_instance(); if( mdl_arrcount( &workshop_form.view_world->ent_swspreview ) ){ workshop_form.ptr_ent = diff --git a/workshop.h b/workshop.h index 9029ed0..b7b8a26 100644 --- a/workshop.h +++ b/workshop.h @@ -49,4 +49,10 @@ VG_STATIC void async_workshop_get_installed_files( void *data, u32 len ); VG_STATIC void workshop_load_metadata( const char *path, struct workshop_file_info *info ); +/* generic reciever */ +VG_STATIC void workshop_async_any_complete( void *data, u32 size ) +{ + skaterift_end_op(); +} + #endif /* WORKSHOP_H */ diff --git a/world.c b/world.c new file mode 100644 index 0000000..209de3e --- /dev/null +++ b/world.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#ifndef WORLD_C +#define WORLD_C + +#include "world.h" +#include "network.h" + +static world_instance *world_current_instance(void){ + return &world_static.worlds[ world_static.active_world ]; +} + +static void world_init(void) +{ + VG_VAR_F32( k_day_length ); + VG_VAR_I32( k_debug_light_indices ); + VG_VAR_I32( k_debug_light_complexity ); + VG_VAR_I32( k_light_preview ); + + world_render.sky_rate = 1.0; + world_render.sky_target_rate = 1.0; + + shader_scene_standard_register(); + shader_scene_standard_alphatest_register(); + shader_scene_vertex_blend_register(); + shader_scene_terrain_register(); + shader_scene_depth_register(); + shader_scene_position_register(); + + shader_model_sky_register(); + + vg_info( "Loading world resources\n" ); + + vg_linear_clear( vg_mem.scratch ); + + mdl_context msky; + mdl_open( &msky, "models/rs_skydome.mdl", vg_mem.scratch ); + mdl_load_metadata_block( &msky, vg_mem.scratch ); + mdl_async_load_glmesh( &msky, &world_render.skydome ); + mdl_close( &msky ); + + /* Other systems */ + vg_info( "Loading other world systems\n" ); + + vg_loader_step( world_render_init, NULL ); + vg_loader_step( world_sfd_init, NULL ); + vg_loader_step( world_water_init, NULL ); + vg_loader_step( world_gates_init, NULL ); + vg_loader_step( world_routes_init, NULL ); + + /* Allocate dynamic world memory arena */ + u32 max_size = 76*1024*1024; + world_static.heap = vg_create_linear_allocator( vg_mem.rtmemory, max_size, + VG_MEMORY_SYSTEM ); +} + +#include "world_entity.c" +#include "world_gate.c" +#include "world_gen.c" +#include "world_load.c" +#include "world_physics.c" +#include "world_render.c" +#include "world_sfd.c" +#include "world_volumes.c" +#include "world_water.c" +#include "world_audio.c" +#include "world_routes.c" + +VG_STATIC void world_update( world_instance *world, v3f pos ) +{ + world_render.sky_time += world_render.sky_rate * vg.time_delta; + world_render.sky_rate = vg_lerp( world_render.sky_rate, + world_render.sky_target_rate, + vg.time_delta * 5.0 ); + + world_routes_update_timer_texts( world ); + world_routes_update( world ); + //world_routes_debug( world ); + + /* ---- traffic -------- */ + + for( u32 i=0; ient_traffic ); i++ ){ + ent_traffic *traffic = mdl_arritm( &world->ent_traffic, i ); + + u32 i1 = traffic->index, + i0, + i2 = i1+1; + + if( i1 == 0 ) i0 = traffic->node_count-1; + else i0 = i1-1; + + if( i2 >= traffic->node_count ) i2 = 0; + + i0 += traffic->start_node; + i1 += traffic->start_node; + i2 += traffic->start_node; + + v3f h[3]; + + ent_route_node *rn0 = mdl_arritm( &world->ent_route_node, i0 ), + *rn1 = mdl_arritm( &world->ent_route_node, i1 ), + *rn2 = mdl_arritm( &world->ent_route_node, i2 ); + + v3_copy( rn1->co, h[1] ); + v3_lerp( rn0->co, rn1->co, 0.5f, h[0] ); + v3_lerp( rn1->co, rn2->co, 0.5f, h[2] ); + + float const k_sample_dist = 0.0025f; + v3f pc, pd; + eval_bezier3( h[0], h[1], h[2], traffic->t, pc ); + eval_bezier3( h[0], h[1], h[2], traffic->t+k_sample_dist, pd ); + + v3f v0; + v3_sub( pd, pc, v0 ); + float length = vg_maxf( 0.0001f, v3_length( v0 ) ); + v3_muls( v0, 1.0f/length, v0 ); + + float mod = k_sample_dist / length; + + traffic->t += traffic->speed * vg.time_delta * mod; + + if( traffic->t > 1.0f ){ + traffic->t -= 1.0f; + + if( traffic->t > 1.0f ) traffic->t = 0.0f; + + traffic->index ++; + + if( traffic->index >= traffic->node_count ) + traffic->index = 0; + } + + v3_copy( pc, traffic->transform.co ); + + float a = atan2f( -v0[0], v0[2] ); + q_axis_angle( traffic->transform.q, (v3f){0.0f,1.0f,0.0f}, -a ); + + vg_line_pt3( traffic->transform.co, 0.3f, VG__BLUE ); + } + + /* ---- SFD ------------ */ + + if( mdl_arrcount( &world->ent_route ) ){ + u32 closest = 0; + float min_dist = INFINITY; + + for( u32 i=0; ient_route ); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + float dist = v3_dist2( route->board_transform[3], pos ); + + if( dist < min_dist ){ + min_dist = dist; + closest = i; + } + } + + if( (world_sfd.active_route_board != closest) || network_scores_updated ) + { + network_scores_updated = 0; + world_sfd.active_route_board = closest; + + ent_route *route = mdl_arritm( &world->ent_route, closest ); + u32 id = route->official_track_id; + + if( id != 0xffffffff ){ + struct netmsg_board *local_board = + &scoreboard_client_data.boards[id]; + + for( int i=0; i<13; i++ ){ + sfd_encode( i, &local_board->data[27*i] ); + } + }else{ + sfd_encode( 0, mdl_pstr( &world->meta, route->pstr_name ) ); + sfd_encode( 1, "No data" ); + } + } + } + sfd_update(); + + static float random_accum = 0.0f; + random_accum += vg.time_delta; + + u32 random_ticks = 0; + + while( random_accum > 0.1f ){ + random_accum -= 0.1f; + random_ticks ++; + } + + float radius = 25.0f; + boxf volume_proximity; + v3_add( pos, (v3f){ radius, radius, radius }, volume_proximity[1] ); + v3_sub( pos, (v3f){ radius, radius, radius }, volume_proximity[0] ); + + bh_iter it; + bh_iter_init_box( 0, &it, volume_proximity ); + i32 idx; + + int in_volume = 0; + + while( bh_next( world->volume_bh, &it, &idx ) ){ + ent_volume *volume = mdl_arritm( &world->ent_volume, idx ); + + boxf cube = {{-1.0f,-1.0f,-1.0f},{1.0f,1.0f,1.0f}}; + + if( volume->type == k_volume_subtype_trigger ){ + v3f local; + m4x3_mulv( volume->to_local, pos, local ); + + if( (fabsf(local[0]) <= 1.0f) && + (fabsf(local[1]) <= 1.0f) && + (fabsf(local[2]) <= 1.0f) ) + { + in_volume = 1; + vg_line_boxf_transformed( volume->to_world, cube, 0xff00ff00 ); + + if( !world_static.in_volume ){ + ent_call basecall; + basecall.function = k_ent_function_trigger; + basecall.id = mdl_entity_id( k_ent_volume, idx ); + basecall.data = NULL; + + entity_call( world, &basecall ); + } + } + else + vg_line_boxf_transformed( volume->to_world, cube, 0xff0000ff ); + } + else if( volume->type == k_volume_subtype_particle ){ + vg_line_boxf_transformed( volume->to_world, cube, 0xff00c0ff ); + + for( int j=0; jlight_count; i++ ){ + struct world_light *light = &world->lights[i]; + struct classtype_world_light *inf = light->inf; + + u32 colour = 0xff000000; + u8 r = inf->colour[0] * 255.0f, + g = inf->colour[1] * 255.0f, + b = inf->colour[2] * 255.0f; + + colour |= r; + colour |= g << 8; + colour |= b << 16; + + vg_line_pt3( light->node->co, 0.25f, colour ); + } + } + +#endif +} + +#endif /* WORLD_C */ diff --git a/world.h b/world.h index 98d7efb..d6f8041 100644 --- a/world.h +++ b/world.h @@ -1,62 +1,46 @@ /* - * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved */ -#include "common.h" - #ifndef WORLD_H #define WORLD_H -typedef struct world_instance world_instance; +#include "render.h" -#include "vg/vg_loader.h" +/* types + */ -#include "network.h" -#include "network_msg.h" -#include "scene.h" -#include "render.h" -#include "rigidbody.h" -#include "bvh.h" -#include "model.h" -#include "entity.h" -#include "font.h" - -#include "shaders/scene_standard.h" -#include "shaders/scene_standard_alphatest.h" -#include "shaders/scene_vertex_blend.h" -#include "shaders/scene_terrain.h" -#include "shaders/scene_depth.h" -#include "shaders/scene_position.h" - -#include "shaders/model_sky.h" - -enum { k_max_ui_segments = 8 }; - -enum { k_max_ui_elements = k_max_ui_segments }; -enum { k_max_element_verts = 10 }; -enum { k_max_element_indices = 20 }; - -enum { k_route_ui_max_verts = k_max_ui_elements*k_max_element_verts }; -enum { k_route_ui_max_indices = k_max_ui_elements*k_max_element_indices }; - -enum logic_type -{ - k_logic_type_relay = 1, - k_logic_type_chance = 2, - k_logic_type_achievement = 3 +enum world_geo_type{ + k_world_geo_type_solid = 0, + k_world_geo_type_nonsolid = 1, + k_world_geo_type_water = 2 }; -enum geo_type -{ - k_geo_type_solid = 0, - k_geo_type_nonsolid = 1, - k_geo_type_water = 2 -}; +typedef struct world_instance world_instance; -static const float k_light_cube_size = 8.0f; +/* submodule headers */ +#include "world_entity.h" +#include "world_gate.h" +#include "world_gen.h" +#include "world_info.h" +#include "world_load.h" +#include "world_physics.h" +#include "world_render.h" +#include "world_sfd.h" +#include "world_volumes.h" +#include "world_water.h" +#include "world_audio.h" +#include "world_routes.h" -struct world_instance { +/* console variables */ + +static float k_day_length = 30.0f; /* minutes */ +static int k_debug_light_indices = 0, + k_debug_light_complexity= 0, + k_light_preview = 0; + +struct world_instance { /* Fixed items * ------------------------------------------------------- */ @@ -134,8 +118,6 @@ struct world_instance { * * the following arrays index somewhere into this linear * allocator - * - * (world_gen.h) * -------------------------------------------------------------------------- */ @@ -199,791 +181,24 @@ struct world_instance { rb_object rb_geo; }; -struct world_global{ +struct world_static { /* * Allocated as system memory * -------------------------------------------------------------------------- */ void *heap; - char *load_target; - - /* rendering */ - glmesh skydome; - glmesh mesh_gate; - mdl_submesh sm_gate_surface, - sm_gate_marker[4]; - - double sky_time, sky_rate, sky_target_rate; u32 current_run_version; double time, rewind_from, rewind_to, last_use; - /* water rendering */ - struct{ - struct framebuffer fbreflect, fbdepth; - } - water; - - /* split flap display */ - struct{ - glmesh mesh_base, mesh_display; - mdl_submesh sm_base; - u32 active_route_board; - scene_context scene; - - u32 w, h; - float *buffer; - } - sfd; - - v3f render_gate_pos; int in_volume; world_instance worlds[4]; u32 active_world; - - /* text particles */ - font3d font; - - struct timer_text{ - char text[8]; - m4x3f transform; - ent_gate *gate; - ent_route *route; - } - timer_texts[4]; - u32 timer_text_count; - - struct text_particle{ - rb_object obj; - m4x3f mlocal; - ent_glyph *glyph; - v4f colour; - - m4x3f mdl; - } - text_particles[6*4]; - u32 text_particle_count; -} -static world_global; -VG_STATIC void entity_call( world_instance *world, ent_call *call ); - -VG_STATIC world_instance *get_active_world( void ) -{ - return &world_global.worlds[ world_global.active_world ]; } +static world_static; -/* - * API - */ - -VG_STATIC -int ray_hit_is_ramp( world_instance *world, ray_hit *hit ); - -VG_STATIC -struct world_surface *ray_hit_surface( world_instance *world, ray_hit *hit ); - -VG_STATIC -void ray_world_get_tri( world_instance *world, ray_hit *hit, v3f tri[3] ); - -VG_STATIC -int ray_world( world_instance *world, v3f pos, v3f dir, ray_hit *hit ); - -VG_STATIC -ent_spawn *world_find_closest_spawn( world_instance *world, v3f position ) -{ - ent_spawn *rp = NULL, *r; - float min_dist = INFINITY; - - for( u32 i=0; ient_spawn); i++ ){ - r = mdl_arritm( &world->ent_spawn, i ); - float d = v3_dist2( r->transform.co, position ); - - if( d < min_dist ){ - min_dist = d; - rp = r; - } - } - - if( !rp ){ - if( mdl_arrcount(&world->ent_spawn) ){ - vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" ); - return mdl_arritm( &world->ent_spawn, 0 ); - } - else{ - vg_error( "There are no spawns in the level!\n" ); - } - } - - return rp; -} - -VG_STATIC -ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name ) -{ - ent_spawn *rp = NULL, *r; - for( u32 i=0; ient_spawn); i++ ){ - r = mdl_arritm( &world->ent_spawn, i ); - if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){ - rp = r; - break; - } - } - - if( !rp ) - vg_warn( "No spawn named '%s'\n", name ); - - return rp; -} - -/* - * Submodules - */ - -VG_STATIC float - k_day_length = 30.0f; /* minutes */ - -VG_STATIC int k_debug_light_indices = 0, - k_debug_light_complexity = 0, - k_light_preview = 0; - -#include "world_routes.h" -#include "world_sfd.h" -#include "world_render.h" -#include "world_water.h" -#include "world_volumes.h" -#include "world_gen.h" -#include "world_gate.h" - -/* - * ----------------------------------------------------------------------------- - * Events - * ----------------------------------------------------------------------------- - */ - -VG_STATIC int world_stop_sound( int argc, const char *argv[] ) -{ - world_instance *world = get_active_world(); - return 0; -} - -VG_STATIC void world_init(void) -{ - VG_VAR_F32( k_day_length ); - VG_VAR_I32( k_debug_light_indices ); - VG_VAR_I32( k_debug_light_complexity ); - VG_VAR_I32( k_light_preview ); - - world_global.sky_rate = 1.0; - world_global.sky_target_rate = 1.0; - - shader_scene_standard_register(); - shader_scene_standard_alphatest_register(); - shader_scene_vertex_blend_register(); - shader_scene_terrain_register(); - shader_scene_depth_register(); - shader_scene_position_register(); - - shader_model_sky_register(); - - vg_info( "Loading world resources\n" ); - - vg_linear_clear( vg_mem.scratch ); - - mdl_context msky; - mdl_open( &msky, "models/rs_skydome.mdl", vg_mem.scratch ); - mdl_load_metadata_block( &msky, vg_mem.scratch ); - mdl_async_load_glmesh( &msky, &world_global.skydome ); - mdl_close( &msky ); - - /* Other systems */ - vg_info( "Loading other world systems\n" ); - - vg_loader_step( world_render_init, NULL ); - vg_loader_step( world_sfd_init, NULL ); - vg_loader_step( world_water_init, NULL ); - vg_loader_step( world_gates_init, NULL ); - vg_loader_step( world_routes_init, NULL ); - - /* Allocate dynamic world memory arena */ - u32 max_size = 76*1024*1024; - world_global.heap = vg_create_linear_allocator( vg_mem.rtmemory, max_size, - VG_MEMORY_SYSTEM ); -} - -VG_STATIC void ent_volume_call( world_instance *world, ent_call *call ) -{ - u32 index = mdl_entity_id_id( call->id ); - ent_volume *volume = mdl_arritm( &world->ent_volume, index ); - if( !volume->target ) return; - - if( call->function == k_ent_function_trigger ){ - call->id = volume->target; - - if( volume->type == k_volume_subtype_particle ){ - float *co = alloca( sizeof(float)*3 ); - co[0] = vg_randf64()*2.0f-1.0f; - co[1] = vg_randf64()*2.0f-1.0f; - co[2] = vg_randf64()*2.0f-1.0f; - m4x3_mulv( volume->to_world, co, co ); - - call->function = k_ent_function_particle_spawn; - call->data = co; - entity_call( world, call ); - } - else{ - entity_call( world, call ); - } - } -} - -VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ) -{ - if( world->status == k_world_status_unloading ){ - vg_warn( "cannot modify audio while unloading world\n" ); - return; - } - - u8 world_id = (world - world_global.worlds) + 1; - u32 index = mdl_entity_id_id( call->id ); - ent_audio *audio = mdl_arritm( &world->ent_audio, index ); - - v3f sound_co; - - if( call->function == k_ent_function_particle_spawn ){ - v3_copy( call->data, sound_co ); - } - else if( call->function == k_ent_function_trigger ){ - v3_copy( audio->transform.co, sound_co ); - } - else - vg_fatal_error( "ent_audio_call (invalid function id)" ); - - float chance = vg_randf64()*100.0f, - bar = 0.0f; - - for( u32 i=0; iclip_count; i++ ){ - ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip, - audio->clip_start+i ); - - float mod = world->probabilities[ audio->probability_curve ], - p = clip->probability * mod; - - bar += p; - - if( chance < bar ){ - audio_lock(); - - if( audio->behaviour == k_channel_behaviour_unlimited ){ - audio_oneshot_3d( &clip->clip, sound_co, - audio->transform.s[0], - audio->volume ); - } - else if( audio->behaviour == k_channel_behaviour_discard_if_full ){ - audio_channel *ch = - audio_get_group_idle_channel( audio->group, - audio->max_channels ); - - if( ch ){ - audio_channel_init( ch, &clip->clip, audio->flags ); - audio_channel_group( ch, audio->group ); - audio_channel_world( ch, world_id ); - audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] ); - audio_channel_edit_volume( ch, audio->volume, 1 ); - ch = audio_relinquish_channel( ch ); - } - } - else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){ - audio_channel *ch = - audio_get_group_idle_channel( audio->group, - audio->max_channels ); - - /* group is full */ - if( !ch ){ - audio_channel *existing = - audio_get_group_first_active_channel( audio->group ); - - if( existing ){ - if( existing->source == &clip->clip ){ - audio_unlock(); - return; - } - - existing->group = 0; - existing = audio_channel_fadeout(existing, audio->crossfade); - } - - ch = audio_get_first_idle_channel(); - } - - if( ch ){ - audio_channel_init( ch, &clip->clip, audio->flags ); - audio_channel_group( ch, audio->group ); - audio_channel_world( ch, world_id ); - audio_channel_fadein( ch, audio->crossfade ); - ch = audio_relinquish_channel( ch ); - } - } - - audio_unlock(); - return; - } - } -} - -/* finds any active playing in world and fades them out, we can only do this - * while unloading */ -VG_STATIC void world_fadeout_audio( world_instance *world ) -{ - if( world->status != k_world_status_unloading ){ - vg_fatal_error( "World status must be set to 'unloading', to fadeout" - " audio.\n" ); - } - - u8 world_id = (world - world_global.worlds) + 1; - - audio_lock(); - for( u32 i=0; iallocated && (ch->world_id == world_id) ){ - ch = audio_channel_fadeout( ch, 1.0f ); - } - } - audio_unlock(); -} - -VG_STATIC void world_update( world_instance *world, v3f pos ) -{ - world_global.sky_time += world_global.sky_rate * vg.time_delta; - world_global.sky_rate = vg_lerp( world_global.sky_rate, - world_global.sky_target_rate, - vg.time_delta * 5.0 ); - - world_routes_update_timer_texts( world ); - world_routes_update( world ); - //world_routes_debug( world ); - - /* ---- traffic -------- */ - - for( u32 i=0; ient_traffic ); i++ ){ - ent_traffic *traffic = mdl_arritm( &world->ent_traffic, i ); - - u32 i1 = traffic->index, - i0, - i2 = i1+1; - - if( i1 == 0 ) i0 = traffic->node_count-1; - else i0 = i1-1; - - if( i2 >= traffic->node_count ) i2 = 0; - - i0 += traffic->start_node; - i1 += traffic->start_node; - i2 += traffic->start_node; - - v3f h[3]; - - ent_route_node *rn0 = mdl_arritm( &world->ent_route_node, i0 ), - *rn1 = mdl_arritm( &world->ent_route_node, i1 ), - *rn2 = mdl_arritm( &world->ent_route_node, i2 ); - - v3_copy( rn1->co, h[1] ); - v3_lerp( rn0->co, rn1->co, 0.5f, h[0] ); - v3_lerp( rn1->co, rn2->co, 0.5f, h[2] ); - - float const k_sample_dist = 0.0025f; - v3f pc, pd; - eval_bezier3( h[0], h[1], h[2], traffic->t, pc ); - eval_bezier3( h[0], h[1], h[2], traffic->t+k_sample_dist, pd ); - - v3f v0; - v3_sub( pd, pc, v0 ); - float length = vg_maxf( 0.0001f, v3_length( v0 ) ); - v3_muls( v0, 1.0f/length, v0 ); - - float mod = k_sample_dist / length; - - traffic->t += traffic->speed * vg.time_delta * mod; - - if( traffic->t > 1.0f ){ - traffic->t -= 1.0f; - - if( traffic->t > 1.0f ) traffic->t = 0.0f; - - traffic->index ++; - - if( traffic->index >= traffic->node_count ) - traffic->index = 0; - } - - v3_copy( pc, traffic->transform.co ); - - float a = atan2f( -v0[0], v0[2] ); - q_axis_angle( traffic->transform.q, (v3f){0.0f,1.0f,0.0f}, -a ); - - vg_line_pt3( traffic->transform.co, 0.3f, VG__BLUE ); - } - - /* ---- SFD ------------ */ - - if( mdl_arrcount( &world->ent_route ) ){ - u32 closest = 0; - float min_dist = INFINITY; - - for( u32 i=0; ient_route ); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - float dist = v3_dist2( route->board_transform[3], pos ); - - if( dist < min_dist ){ - min_dist = dist; - closest = i; - } - } - - if( (world_global.sfd.active_route_board != closest) - || network_scores_updated ) - { - network_scores_updated = 0; - world_global.sfd.active_route_board = closest; - - ent_route *route = mdl_arritm( &world->ent_route, closest ); - u32 id = route->official_track_id; - - if( id != 0xffffffff ){ - struct netmsg_board *local_board = - &scoreboard_client_data.boards[id]; - - for( int i=0; i<13; i++ ){ - sfd_encode( i, &local_board->data[27*i] ); - } - }else{ - sfd_encode( 0, mdl_pstr( &world->meta, route->pstr_name ) ); - sfd_encode( 1, "No data" ); - } - } - } - sfd_update(); - - static float random_accum = 0.0f; - random_accum += vg.time_delta; - - u32 random_ticks = 0; - - while( random_accum > 0.1f ){ - random_accum -= 0.1f; - random_ticks ++; - } - - float radius = 25.0f; - boxf volume_proximity; - v3_add( pos, (v3f){ radius, radius, radius }, volume_proximity[1] ); - v3_sub( pos, (v3f){ radius, radius, radius }, volume_proximity[0] ); - - bh_iter it; - bh_iter_init_box( 0, &it, volume_proximity ); - i32 idx; - - int in_volume = 0; - - while( bh_next( world->volume_bh, &it, &idx ) ){ - ent_volume *volume = mdl_arritm( &world->ent_volume, idx ); - - boxf cube = {{-1.0f,-1.0f,-1.0f},{1.0f,1.0f,1.0f}}; - - if( volume->type == k_volume_subtype_trigger ){ - v3f local; - m4x3_mulv( volume->to_local, pos, local ); - - if( (fabsf(local[0]) <= 1.0f) && - (fabsf(local[1]) <= 1.0f) && - (fabsf(local[2]) <= 1.0f) ) - { - in_volume = 1; - vg_line_boxf_transformed( volume->to_world, cube, 0xff00ff00 ); - - if( !world_global.in_volume ){ - ent_call basecall; - basecall.function = k_ent_function_trigger; - basecall.id = mdl_entity_id( k_ent_volume, idx ); - basecall.data = NULL; - - entity_call( world, &basecall ); - } - } - else - vg_line_boxf_transformed( volume->to_world, cube, 0xff0000ff ); - } - else if( volume->type == k_volume_subtype_particle ){ - vg_line_boxf_transformed( volume->to_world, cube, 0xff00c0ff ); - - for( int j=0; jlight_count; i++ ){ - struct world_light *light = &world->lights[i]; - struct classtype_world_light *inf = light->inf; - - u32 colour = 0xff000000; - u8 r = inf->colour[0] * 255.0f, - g = inf->colour[1] * 255.0f, - b = inf->colour[2] * 255.0f; - - colour |= r; - colour |= g << 8; - colour |= b << 16; - - vg_line_pt3( light->node->co, 0.25f, colour ); - } - } - -#endif -} - -/* - * ----------------------------------------------------------------------------- - * API implementation - * ----------------------------------------------------------------------------- - */ - -VG_STATIC void ray_world_get_tri( world_instance *world, - ray_hit *hit, v3f tri[3] ) -{ - for( int i=0; i<3; i++ ) - v3_copy( world->scene_geo.arrvertices[ hit->tri[i] ].co, tri[i] ); -} - -VG_STATIC int ray_world( world_instance *world, - v3f pos, v3f dir, ray_hit *hit ) -{ - return scene_raycast( &world->scene_geo, world->geo_bh, pos, dir, hit ); -} - -/* - * Cast a sphere from a to b and see what time it hits - */ -VG_STATIC int spherecast_world( world_instance *world, - v3f pa, v3f pb, float r, float *t, v3f n ) -{ - boxf region; - box_init_inf( region ); - box_addpt( region, pa ); - box_addpt( region, pb ); - - v3_add( (v3f){ r, r, r}, region[1], region[1] ); - v3_add( (v3f){-r,-r,-r}, region[0], region[0] ); - - v3f dir; - v3_sub( pb, pa, dir ); - - v3f dir_inv; - dir_inv[0] = 1.0f/dir[0]; - dir_inv[1] = 1.0f/dir[1]; - dir_inv[2] = 1.0f/dir[2]; - - int hit = -1; - float min_t = 1.0f; - - bh_iter it; - bh_iter_init_box( 0, &it, region ); - i32 idx; - while( bh_next( world->geo_bh, &it, &idx ) ){ - u32 *ptri = &world->scene_geo.arrindices[ idx*3 ]; - v3f tri[3]; - - boxf box; - box_init_inf( box ); - - for( int j=0; j<3; j++ ){ - v3_copy( world->scene_geo.arrvertices[ptri[j]].co, tri[j] ); - box_addpt( box, tri[j] ); - } - - v3_add( (v3f){ r, r, r}, box[1], box[1] ); - v3_add( (v3f){-r,-r,-r}, box[0], box[0] ); - - if( !ray_aabb1( box, pa, dir_inv, 1.0f ) ) - continue; - - float t; - v3f n1; - if( spherecast_triangle( tri, pa, dir, r, &t, n1 ) ){ - if( t < min_t ){ - min_t = t; - hit = idx; - v3_copy( n1, n ); - } - } - } - - *t = min_t; - return hit; -} - -VG_STATIC -struct world_surface *world_tri_index_surface( world_instance *world, - u32 index ) -{ - for( int i=1; isurface_count; i++ ){ - struct world_surface *surf = &world->surfaces[i]; - - if( (index >= surf->sm_geo.vertex_start) && - (index < surf->sm_geo.vertex_start+surf->sm_geo.vertex_count ) ) - { - return surf; - } - } - - return &world->surfaces[0]; -} - -VG_STATIC struct world_surface *world_contact_surface( world_instance *world, - rb_ct *ct ) -{ - return world_tri_index_surface( world, ct->element_id ); -} - -VG_STATIC struct world_surface *ray_hit_surface( world_instance *world, - ray_hit *hit ) -{ - return world_tri_index_surface( world, hit->tri[0] ); -} - -/* - * ----------------------------------------------------------------------------- - * Audio sampling - * ----------------------------------------------------------------------------- - */ - -VG_STATIC -enum audio_sprite_type world_audio_sample_sprite_kandom(v3f origin, v3f output); -VG_STATIC void world_audio_sample_distances( v3f co, int *index, float *value ); - -#include "audio.h" - -/* - * Trace out a random point, near the player to try and determine water areas - */ -VG_STATIC -enum audio_sprite_type world_audio_sample_sprite_random(v3f origin, v3f output) -{ - v3f chance = { (vg_randf64()-0.5f) * 30.0f, - 8.0f, - (vg_randf64()-0.5f) * 30.0f }; - - v3f pos; - v3_add( chance, origin, pos ); - - ray_hit contact; - contact.dist = vg_minf( 16.0f, pos[1] ); - - world_instance *world = get_active_world(); - - if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &contact ) ){ - struct world_surface *mat = ray_hit_surface( world, &contact ); - - if( mat->info.surface_prop == k_surface_prop_grass){ - v3_copy( contact.pos, output ); - return k_audio_sprite_type_grass; - } - else{ - return k_audio_sprite_type_none; - } - } - - output[0] = pos[0]; - output[1] = 0.0f; - output[2] = pos[2]; - - float dist = fabsf(output[1] - origin[1]); - - if( world->water.enabled && dist<=40.0f ) - return k_audio_sprite_type_water; - else - return k_audio_sprite_type_none; -} - -VG_STATIC void world_audio_sample_distances( v3f co, int *index, float *value ) -{ - float inr3 = 0.57735027, - inr2 = 0.70710678118; - - v3f sample_directions[] = { - { -1.0f, 0.0f, 0.0f }, - { 1.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f, 1.0f }, - { 0.0f, 0.0f, -1.0f }, - { 0.0f, 1.0f, 0.0f }, - { 0.0f, -1.0f, 0.0f }, - { -inr3, inr3, inr3 }, - { inr3, inr3, inr3 }, - { -inr3, inr3, -inr3 }, - { inr3, inr3, -inr3 }, - { -inr2, 0.0f, inr2 }, - { inr2, 0.0f, inr2 }, - { -inr2, 0.0f, -inr2 }, - { inr2, 0.0f, -inr2 }, - }; - - static int si = 0; - static float distances[16]; - - ray_hit ray; - ray.dist = 5.0f; - - v3f rc, rd, ro; - v3_copy( sample_directions[ si ], rd ); - v3_add( co, (v3f){0.0f,1.5f,0.0f}, ro ); - v3_copy( ro, rc ); - - float dist = 200.0f; - - for( int i=0; i<10; i++ ){ - if( ray_world( get_active_world(), rc, rd, &ray ) ){ - dist = (float)i*5.0f + ray.dist; - break; - } - else{ - v3_muladds( rc, rd, ray.dist, rc ); - } - } - - distances[si] = dist; - - if( vg_lines.draw ){ - for( int i=0; i<14; i++ ){ - if( distances[i] != 200.0f ){ - u32 colours[] = { VG__RED, VG__BLUE, VG__GREEN, - VG__CYAN, VG__YELOW, VG__PINK, - VG__WHITE }; - - u32 colour = colours[i%7]; - - v3f p1; - v3_muladds( ro, sample_directions[i], distances[i], p1 ); - vg_line( ro, p1, colour ); - vg_line_pt3( p1, 0.1f, colour ); - } - } - } - - *index = si; - *value = dist; - - si ++; - if( si >= 14 ) - si = 0; -} +static void world_init(void); +static world_instance *world_current_instance(void); #endif /* WORLD_H */ diff --git a/world_audio.c b/world_audio.c new file mode 100644 index 0000000..facbcc4 --- /dev/null +++ b/world_audio.c @@ -0,0 +1,143 @@ +#ifndef WORLD_AUDIO_C +#define WORLD_AUDIO_C + +#include "audio.h" +#include "world_audio.h" + +/* finds any active playing in world and fades them out, we can only do this + * while unloading */ +VG_STATIC void world_fadeout_audio( world_instance *world ) +{ + if( world->status != k_world_status_unloading ){ + vg_fatal_error( "World status must be set to 'unloading', to fadeout" + " audio.\n" ); + } + + u8 world_id = (world - world_static.worlds) + 1; + + audio_lock(); + for( u32 i=0; iallocated && (ch->world_id == world_id) ){ + ch = audio_channel_fadeout( ch, 1.0f ); + } + } + audio_unlock(); +} + +/* + * Trace out a random point, near the player to try and determine water areas + */ +VG_STATIC +enum audio_sprite_type world_audio_sample_sprite_random(v3f origin, v3f output) +{ + v3f chance = { (vg_randf64()-0.5f) * 30.0f, + 8.0f, + (vg_randf64()-0.5f) * 30.0f }; + + v3f pos; + v3_add( chance, origin, pos ); + + ray_hit contact; + contact.dist = vg_minf( 16.0f, pos[1] ); + + world_instance *world = world_current_instance(); + + if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &contact ) ){ + struct world_surface *mat = ray_hit_surface( world, &contact ); + + if( mat->info.surface_prop == k_surface_prop_grass){ + v3_copy( contact.pos, output ); + return k_audio_sprite_type_grass; + } + else{ + return k_audio_sprite_type_none; + } + } + + output[0] = pos[0]; + output[1] = 0.0f; + output[2] = pos[2]; + + float dist = fabsf(output[1] - origin[1]); + + if( world->water.enabled && dist<=40.0f ) + return k_audio_sprite_type_water; + else + return k_audio_sprite_type_none; +} + +VG_STATIC void world_audio_sample_distances( v3f co, int *index, float *value ) +{ + float inr3 = 0.57735027, + inr2 = 0.70710678118; + + v3f sample_directions[] = { + { -1.0f, 0.0f, 0.0f }, + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, -1.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + { -inr3, inr3, inr3 }, + { inr3, inr3, inr3 }, + { -inr3, inr3, -inr3 }, + { inr3, inr3, -inr3 }, + { -inr2, 0.0f, inr2 }, + { inr2, 0.0f, inr2 }, + { -inr2, 0.0f, -inr2 }, + { inr2, 0.0f, -inr2 }, + }; + + static int si = 0; + static float distances[16]; + + ray_hit ray; + ray.dist = 5.0f; + + v3f rc, rd, ro; + v3_copy( sample_directions[ si ], rd ); + v3_add( co, (v3f){0.0f,1.5f,0.0f}, ro ); + v3_copy( ro, rc ); + + float dist = 200.0f; + + for( int i=0; i<10; i++ ){ + if( ray_world( world_current_instance(), rc, rd, &ray ) ){ + dist = (float)i*5.0f + ray.dist; + break; + } + else{ + v3_muladds( rc, rd, ray.dist, rc ); + } + } + + distances[si] = dist; + + if( vg_lines.draw ){ + for( int i=0; i<14; i++ ){ + if( distances[i] != 200.0f ){ + u32 colours[] = { VG__RED, VG__BLUE, VG__GREEN, + VG__CYAN, VG__YELOW, VG__PINK, + VG__WHITE }; + + u32 colour = colours[i%7]; + + v3f p1; + v3_muladds( ro, sample_directions[i], distances[i], p1 ); + vg_line( ro, p1, colour ); + vg_line_pt3( p1, 0.1f, colour ); + } + } + } + + *index = si; + *value = dist; + + si ++; + if( si >= 14 ) + si = 0; +} + +#endif /* WORLD_AUDIO_C */ diff --git a/world_audio.h b/world_audio.h new file mode 100644 index 0000000..285dc23 --- /dev/null +++ b/world_audio.h @@ -0,0 +1,11 @@ +#ifndef WORLD_AUDIO_H +#define WORLD_AUDIO_H + +#include "world.h" + +VG_STATIC void world_fadeout_audio( world_instance *world ); +VG_STATIC void world_audio_sample_distances( v3f co, int *index, float *value ); +VG_STATIC enum audio_sprite_type world_audio_sample_sprite_random( v3f origin, + v3f output ); + +#endif /* WORLD_AUDIO_H */ diff --git a/world_entity.c b/world_entity.c new file mode 100644 index 0000000..38bf86f --- /dev/null +++ b/world_entity.c @@ -0,0 +1,259 @@ +#ifndef WORLD_ENTITY_C +#define WORLD_ENTITY_C + +#include "world.h" +#include "world_load.h" +#include "entity.h" + +VG_STATIC void world_gen_entities_init(void) +{ + world_instance *world = world_loading_instance(); + + /* lights */ + for( u32 j=0; jent_light); j ++ ){ + ent_light *light = mdl_arritm( &world->ent_light, j ); + + m4x3f to_world; + q_m3x3( light->transform.q, to_world ); + v3_copy( light->transform.co, to_world[3] ); + m4x3_invert_affine( to_world, light->inverse_world ); + + light->angle_sin_cos[0] = sinf( light->angle * 0.5f ); + light->angle_sin_cos[1] = cosf( light->angle * 0.5f ); + } + + /* gates */ + for( u32 j=0; jent_gate); j ++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, j ); + + if( gate->type == k_gate_type_teleport ){ + gate_transform_update( gate ); + } + } + vg_async_call( world_link_nonlocal_async, world, 0 ); + + /* water */ + for( u32 j=0; jent_water); j++ ){ + ent_water *water = mdl_arritm( &world->ent_water, j ); + if( world->water.enabled ){ + vg_warn( "Multiple water surfaces in level!\n" ); + break; + } + + world->water.enabled = 1; + water_set_surface( world, water->transform.co[1] ); + } + + /* volumes */ + for( u32 j=0; jent_volume); j++ ){ + ent_volume *volume = mdl_arritm( &world->ent_volume, j ); + mdl_transform_m4x3( &volume->transform, volume->to_world ); + m4x3_invert_full( volume->to_world, volume->to_local ); + } + + /* audio packs */ + for( u32 j=0; jent_audio); j++ ){ + ent_audio *audio = mdl_arritm( &world->ent_audio, j ); + + for( u32 k=0; kclip_count; k++ ){ + ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip, + audio->clip_start+k ); + + if( clip->file.pack_size ){ + u32 size = clip->file.pack_size, + offset = clip->file.pack_offset; + + /* embedded files are fine to clear the scratch buffer, only + * external audio uses it */ + + vg_linear_clear( vg_mem.scratch ); + void *data = vg_linear_alloc( vg_mem.scratch, + clip->file.pack_size ); + + mdl_fread_pack_file( &world->meta, &clip->file, data ); + + clip->clip.path = NULL; + clip->clip.flags = audio->flags; + clip->clip.data = data; + clip->clip.size = size; + } + else{ + clip->clip.path = mdl_pstr( &world->meta, clip->file.pstr_path ); + clip->clip.flags = audio->flags; + clip->clip.data = NULL; + clip->clip.size = 0; + } + + audio_clip_load( &clip->clip, world->heap ); + } + } +} + +VG_STATIC +ent_spawn *world_find_closest_spawn( world_instance *world, v3f position ) +{ + ent_spawn *rp = NULL, *r; + float min_dist = INFINITY; + + for( u32 i=0; ient_spawn); i++ ){ + r = mdl_arritm( &world->ent_spawn, i ); + float d = v3_dist2( r->transform.co, position ); + + if( d < min_dist ){ + min_dist = d; + rp = r; + } + } + + if( !rp ){ + if( mdl_arrcount(&world->ent_spawn) ){ + vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" ); + return mdl_arritm( &world->ent_spawn, 0 ); + } + else{ + vg_error( "There are no spawns in the level!\n" ); + } + } + + return rp; +} + +VG_STATIC +ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name ) +{ + ent_spawn *rp = NULL, *r; + for( u32 i=0; ient_spawn); i++ ){ + r = mdl_arritm( &world->ent_spawn, i ); + if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){ + rp = r; + break; + } + } + + if( !rp ) + vg_warn( "No spawn named '%s'\n", name ); + + return rp; +} + +VG_STATIC void ent_volume_call( world_instance *world, ent_call *call ) +{ + u32 index = mdl_entity_id_id( call->id ); + ent_volume *volume = mdl_arritm( &world->ent_volume, index ); + if( !volume->target ) return; + + if( call->function == k_ent_function_trigger ){ + call->id = volume->target; + + if( volume->type == k_volume_subtype_particle ){ + float *co = alloca( sizeof(float)*3 ); + co[0] = vg_randf64()*2.0f-1.0f; + co[1] = vg_randf64()*2.0f-1.0f; + co[2] = vg_randf64()*2.0f-1.0f; + m4x3_mulv( volume->to_world, co, co ); + + call->function = k_ent_function_particle_spawn; + call->data = co; + entity_call( world, call ); + } + else{ + entity_call( world, call ); + } + } +} + +VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ) +{ + if( world->status == k_world_status_unloading ){ + vg_warn( "cannot modify audio while unloading world\n" ); + return; + } + + u8 world_id = (world - world_static.worlds) + 1; + u32 index = mdl_entity_id_id( call->id ); + ent_audio *audio = mdl_arritm( &world->ent_audio, index ); + + v3f sound_co; + + if( call->function == k_ent_function_particle_spawn ){ + v3_copy( call->data, sound_co ); + } + else if( call->function == k_ent_function_trigger ){ + v3_copy( audio->transform.co, sound_co ); + } + else + vg_fatal_error( "ent_audio_call (invalid function id)" ); + + float chance = vg_randf64()*100.0f, + bar = 0.0f; + + for( u32 i=0; iclip_count; i++ ){ + ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip, + audio->clip_start+i ); + + float mod = world->probabilities[ audio->probability_curve ], + p = clip->probability * mod; + + bar += p; + + if( chance < bar ){ + audio_lock(); + + if( audio->behaviour == k_channel_behaviour_unlimited ){ + audio_oneshot_3d( &clip->clip, sound_co, + audio->transform.s[0], + audio->volume ); + } + else if( audio->behaviour == k_channel_behaviour_discard_if_full ){ + audio_channel *ch = + audio_get_group_idle_channel( audio->group, + audio->max_channels ); + + if( ch ){ + audio_channel_init( ch, &clip->clip, audio->flags ); + audio_channel_group( ch, audio->group ); + audio_channel_world( ch, world_id ); + audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] ); + audio_channel_edit_volume( ch, audio->volume, 1 ); + ch = audio_relinquish_channel( ch ); + } + } + else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){ + audio_channel *ch = + audio_get_group_idle_channel( audio->group, + audio->max_channels ); + + /* group is full */ + if( !ch ){ + audio_channel *existing = + audio_get_group_first_active_channel( audio->group ); + + if( existing ){ + if( existing->source == &clip->clip ){ + audio_unlock(); + return; + } + + existing->group = 0; + existing = audio_channel_fadeout(existing, audio->crossfade); + } + + ch = audio_get_first_idle_channel(); + } + + if( ch ){ + audio_channel_init( ch, &clip->clip, audio->flags ); + audio_channel_group( ch, audio->group ); + audio_channel_world( ch, world_id ); + audio_channel_fadein( ch, audio->crossfade ); + ch = audio_relinquish_channel( ch ); + } + } + + audio_unlock(); + return; + } + } +} + +#endif /* WORLD_ENTITY_C */ diff --git a/world_entity.h b/world_entity.h new file mode 100644 index 0000000..807cd97 --- /dev/null +++ b/world_entity.h @@ -0,0 +1,16 @@ +#ifndef WORLD_ENTITY_H +#define WORLD_ENTITY_H + +#include "world.h" +#include "entity.h" + +VG_STATIC void world_gen_entities_init(void); +VG_STATIC ent_spawn *world_find_spawn_by_name( world_instance *world, + const char *name ); +VG_STATIC ent_spawn *world_find_closest_spawn( world_instance *world, + v3f position ); + +VG_STATIC void ent_volume_call( world_instance *world, ent_call *call ); +VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ); + +#endif /* WORLD_ENTITY_H */ diff --git a/world_gate.c b/world_gate.c new file mode 100644 index 0000000..7a5360a --- /dev/null +++ b/world_gate.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#ifndef WORLD_GATE_C +#define WORLD_GATE_C + +#include "world.h" +#include "world_gate.h" + +#include "skaterift.h" +#include "common.h" +#include "model.h" +#include "entity.h" +#include "render.h" +#include "camera.h" + +#include "world_water.h" + +/* + * Update the transform matrices for gate + */ +VG_STATIC void gate_transform_update( ent_gate *gate ) +{ + m4x3f to_local, recv_to_world; + + q_m3x3( gate->q[0], gate->to_world ); + v3_copy( gate->co[0], gate->to_world[3] ); + + m4x3_invert_affine( gate->to_world, to_local ); + + q_m3x3( gate->q[1], recv_to_world ); + v3_copy( gate->co[1], recv_to_world[3] ); + m4x3_mul( recv_to_world, to_local, gate->transport ); + + m3x3_scale( gate->to_world, (v3f){ gate->dimensions[0], + gate->dimensions[1], 1.0f } ); +} + +VG_STATIC void world_gates_init(void) +{ + vg_info( "world_gates_init\n" ); + + shader_model_gate_register(); + + vg_linear_clear( vg_mem.scratch ); + + mdl_context mgate; + mdl_open( &mgate, "models/rs_gate.mdl", vg_mem.scratch ); + mdl_load_metadata_block( &mgate, vg_mem.scratch ); + + mdl_mesh *surface = mdl_find_mesh( &mgate, "rs_gate" ); + mdl_submesh *sm = mdl_arritm(&mgate.submeshs,surface->submesh_start); + world_gates.sm_surface = *sm; + + const char *names[] = { "rs_gate_marker", "rs_gate_marker.001", + "rs_gate_marker.002", "rs_gate_marker.003" }; + + for( int i=0; i<4; i++ ){ + mdl_mesh *marker = mdl_find_mesh( &mgate, names[i] ); + sm = mdl_arritm( &mgate.submeshs, marker->submesh_start ); + world_gates.sm_marker[i] = *sm; + } + + mdl_async_load_glmesh( &mgate, &world_gates.mesh ); + mdl_close( &mgate ); +} + +/* + * Render the view through a gate + */ +VG_STATIC int render_gate( world_instance *world_inside, + ent_gate *gate, camera *cam, int layer_depth ) +{ + v3f viewdir, gatedir; + m3x3_mulv( cam->transform, (v3f){0.0f,0.0f,-1.0f}, viewdir ); + q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, gatedir ); + + v3f v0; + v3_sub( cam->pos, gate->co[0], v0 ); + + float dist = v3_dot(v0, gatedir); + + /* Hard cutoff */ + if( dist > 3.0f ) + return 0; + + if( v3_dist( cam->pos, gate->co[0] ) > 100.0f ) + return 0; + + { + v3f a,b,c,d; + + m4x3_mulv( gate->to_world, (v3f){-1.0f,-1.0f,0.0f}, a ); + m4x3_mulv( gate->to_world, (v3f){ 1.0f,-1.0f,0.0f}, b ); + m4x3_mulv( gate->to_world, (v3f){ 1.0f, 1.0f,0.0f}, c ); + m4x3_mulv( gate->to_world, (v3f){-1.0f, 1.0f,0.0f}, d ); + + vg_line( a,b, 0xffffa000 ); + vg_line( b,c, 0xffffa000 ); + vg_line( c,d, 0xffffa000 ); + vg_line( d,a, 0xffffa000 ); + + vg_line2( gate->co[0], gate->co[1], 0xff0000ff, 0x00000000 ); + } + + /* update gate camera */ + gate_camera.fov = cam->fov; + gate_camera.nearz = 0.1f; + gate_camera.farz = 2000.0f; + + m4x3_mul( gate->transport, cam->transform, gate_camera.transform ); + camera_update_view( &gate_camera ); + camera_update_projection( &gate_camera ); + + /* Add special clipping plane to projection */ + v4f surface; + q_mulv( gate->q[1], (v3f){0.0f,0.0f,-1.0f}, surface ); + surface[3] = v3_dot( surface, gate->co[1] ); + + m4x3_mulp( gate_camera.transform_inverse, surface, surface ); + surface[3] = -fabsf(surface[3]); + + if( dist < -0.5f ) + m4x4_clip_projection( gate_camera.mtx.p, surface ); + + /* Ready to draw with new camrea */ + camera_finalize( &gate_camera ); + + vg_line_pt3( gate_camera.transform[3], 0.3f, 0xff00ff00 ); + { + shader_model_gate_use(); + shader_model_gate_uPv( cam->mtx.pv ); + shader_model_gate_uMdl( gate->to_world ); + shader_model_gate_uCam( cam->pos ); + shader_model_gate_uColour( (v4f){0.0f,1.0f,0.0f,0.0f} ); + shader_model_gate_uTime( vg.time*0.25f ); + shader_model_gate_uInvRes( (v2f){ + 1.0f / (float)vg.window_x, + 1.0f / (float)vg.window_y }); + + glEnable( GL_STENCIL_TEST ); + glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); + glStencilFunc( GL_ALWAYS, 1, 0xFF ); + glStencilMask( 0xFF ); + + mesh_bind( &world_gates.mesh ); + mdl_draw_submesh( &world_gates.sm_surface ); + + glClear( GL_DEPTH_BUFFER_BIT ); + glStencilFunc( GL_EQUAL, 1, 0xFF ); + glStencilMask( 0x00 ); + } + + render_world( world_inside, &gate_camera, layer_depth ); + + { + glDisable( GL_STENCIL_TEST ); + + render_water_texture( world_inside, &gate_camera, layer_depth ); + render_fb_bind( gpipeline.fb_main, 1 ); + + glEnable( GL_STENCIL_TEST ); + + render_water_surface( world_inside, &gate_camera ); + + glStencilMask( 0xFF ); + glStencilFunc( GL_ALWAYS, 1, 0xFF ); + glDisable( GL_STENCIL_TEST ); + } + + return 1; +} + +/* + * Intersect the plane of a gate with a line segment, plane coordinate result + * stored in 'where' + */ +VG_STATIC int gate_intersect_plane( ent_gate *gate, + v3f pos, v3f last, v2f where ) +{ + v4f surface; + q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, surface ); + surface[3] = v3_dot( surface, gate->co[0] ); + + v3f v0, c, delta, p0; + v3_sub( pos, last, v0 ); + float l = v3_length( v0 ); + + if( l == 0.0f ) + return 0; + + v3_divs( v0, l, v0 ); + + v3_muls( surface, surface[3], c ); + v3_sub( c, last, delta ); + + float d = v3_dot( surface, v0 ); + + if( d > 0.00001f ){ + float t = v3_dot(delta, surface) / d; + if( t >= 0.0f && t <= l ){ + v3f local, rel; + v3_muladds( last, v0, t, local ); + v3_sub( gate->co[0], local, rel ); + + where[0] = v3_dot( rel, gate->to_world[0] ); + where[1] = v3_dot( rel, gate->to_world[1] ); + + where[0] /= v3_dot( gate->to_world[0], gate->to_world[0] ); + where[1] /= v3_dot( gate->to_world[1], gate->to_world[1] ); + + return 1; + } + } + + return 0; +} + +/* + * Intersect specific gate + */ +VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last ) +{ + v2f xy; + + if( gate_intersect_plane( gate, pos, last, xy ) ){ + if( fabsf(xy[0]) <= 1.0f && fabsf(xy[1]) <= 1.0f ){ + return 1; + } + } + + return 0; +} + +/* + * Intersect all gates in the world + */ +VG_STATIC ent_gate *world_intersect_gates( world_instance *world, + v3f pos, v3f last ) +{ + for( u32 i=0; ient_gate); i++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, i ); + if( gate->type == k_gate_type_unlinked || + gate->type == k_gate_type_nonlocal_unlinked ) + continue; + + if( gate->type == k_gate_type_nonlocel ){ + if( skaterift.async_op == k_async_op_world_loading || + skaterift.async_op == k_async_op_world_preloading ){ + continue; + } + } + + if( gate_intersect( gate, pos, last ) ){ + return gate; + } + } + + return NULL; +} + +/* + * detatches any nonlocal gates + */ +VG_STATIC void world_unlink_nonlocal( world_instance *world ) +{ + for( u32 j=0; jent_gate); j ++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, j ); + + if( gate->type == k_gate_type_nonlocel ){ + gate->type = k_gate_type_nonlocal_unlinked; + } + } +} + +/* + * attatches nonlocal gates, to be called from main thread ONLY! + */ +VG_STATIC void world_link_nonlocal_async( void *payload, u32 size ) +{ + world_instance *world = payload; + u32 world_id = world - world_static.worlds; + + for( u32 j=0; jent_gate); j ++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, j ); + + if( gate->type == k_gate_type_nonlocal_unlinked ){ + const char *key = mdl_pstr( &world->meta, gate->key ); + vg_info( "key: %s\n", key ); + + for( u32 i=0; istatus != k_world_status_loaded ) continue; + vg_info( "Checking world %u for key matches\n", i ); + + for( u32 j=0; jent_gate ); j++ ){ + ent_gate *gate2 = mdl_arritm( &other->ent_gate, j ); + if( gate2->type != k_gate_type_nonlocal_unlinked ) continue; + + const char *key2 = mdl_pstr( &other->meta, gate2->key ); + vg_info( " key2: %s\n", key2 ); + + if( strcmp( key, key2 ) ) continue; + + vg_success( "Non-local matching pair '%s' found. (%u:%u)\n", + key, world_id, i ); + + gate->type = k_gate_type_nonlocel; + gate2->type = k_gate_type_nonlocel; + gate->target = i; + gate2->target = world_id; + + v3_copy( gate->co[0], gate2->co[1] ); + v3_copy( gate2->co[0], gate->co[1] ); + v4_copy( gate->q[0], gate2->q[1] ); + v4_copy( gate2->q[0], gate->q[1] ); + + v4f qflip; + q_axis_angle( qflip, (v3f){0.0f,1.0f,0.0f}, VG_PIf ); + q_mul( gate->q[0], qflip, gate->q[0] ); + q_mul( gate->q[1], qflip, gate->q[1] ); + + gate_transform_update( gate ); + gate_transform_update( gate2 ); + + goto matched; + } + } +matched:; + } + } +} + +#endif /* WORLD_GATE_C */ diff --git a/world_gate.h b/world_gate.h index b409c51..4defb75 100644 --- a/world_gate.h +++ b/world_gate.h @@ -1,248 +1,29 @@ /* - * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved */ #ifndef WORLD_GATE_H #define WORLD_GATE_H -#include "skaterift.h" -#include "common.h" -#include "model.h" -#include "entity.h" -#include "render.h" -#include "camera.h" - +#include "world.h" #include "shaders/model_gate.h" -#include "world_water.h" - -VG_STATIC void gate_transform_update( ent_gate *gate ) -{ - m4x3f to_local, recv_to_world; - - q_m3x3( gate->q[0], gate->to_world ); - v3_copy( gate->co[0], gate->to_world[3] ); - - m4x3_invert_affine( gate->to_world, to_local ); - - q_m3x3( gate->q[1], recv_to_world ); - v3_copy( gate->co[1], recv_to_world[3] ); - m4x3_mul( recv_to_world, to_local, gate->transport ); - m3x3_scale( gate->to_world, (v3f){ gate->dimensions[0], - gate->dimensions[1], 1.0f } ); -} - -VG_STATIC void world_gates_init(void) -{ - vg_info( "world_gates_init\n" ); - - shader_model_gate_register(); - - vg_linear_clear( vg_mem.scratch ); - - mdl_context mgate; - mdl_open( &mgate, "models/rs_gate.mdl", vg_mem.scratch ); - mdl_load_metadata_block( &mgate, vg_mem.scratch ); - - mdl_mesh *surface = mdl_find_mesh( &mgate, "rs_gate" ); - mdl_submesh *sm = mdl_arritm(&mgate.submeshs,surface->submesh_start); - world_global.sm_gate_surface = *sm; - - const char *names[] = { "rs_gate_marker", "rs_gate_marker.001", - "rs_gate_marker.002", "rs_gate_marker.003" }; - - for( int i=0; i<4; i++ ){ - mdl_mesh *marker = mdl_find_mesh( &mgate, names[i] ); - sm = mdl_arritm( &mgate.submeshs, marker->submesh_start ); - world_global.sm_gate_marker[i] = *sm; - } - - mdl_async_load_glmesh( &mgate, &world_global.mesh_gate ); - mdl_close( &mgate ); +struct world_gates{ + glmesh mesh; + mdl_submesh sm_surface, sm_marker[4]; } +static world_gates; +VG_STATIC void world_gates_init(void); +VG_STATIC void gate_transform_update( ent_gate *gate ); +VG_STATIC void world_link_nonlocal_async( void *payload, u32 size ); +VG_STATIC void world_unlink_nonlocal( world_instance *world ); VG_STATIC int render_gate( world_instance *world_inside, - ent_gate *gate, camera *cam, int layer_depth ) -{ - v3f viewdir, gatedir; - m3x3_mulv( cam->transform, (v3f){0.0f,0.0f,-1.0f}, viewdir ); - q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, gatedir ); - - v3f v0; - v3_sub( cam->pos, gate->co[0], v0 ); - - float dist = v3_dot(v0, gatedir); - - /* Hard cutoff */ - if( dist > 3.0f ) - return 0; - - if( v3_dist( cam->pos, gate->co[0] ) > 100.0f ) - return 0; - - { - v3f a,b,c,d; - - m4x3_mulv( gate->to_world, (v3f){-1.0f,-1.0f,0.0f}, a ); - m4x3_mulv( gate->to_world, (v3f){ 1.0f,-1.0f,0.0f}, b ); - m4x3_mulv( gate->to_world, (v3f){ 1.0f, 1.0f,0.0f}, c ); - m4x3_mulv( gate->to_world, (v3f){-1.0f, 1.0f,0.0f}, d ); - - vg_line( a,b, 0xffffa000 ); - vg_line( b,c, 0xffffa000 ); - vg_line( c,d, 0xffffa000 ); - vg_line( d,a, 0xffffa000 ); - - vg_line2( gate->co[0], gate->co[1], 0xff0000ff, 0x00000000 ); - } - - /* update gate camera */ - gate_camera.fov = cam->fov; - gate_camera.nearz = 0.1f; - gate_camera.farz = 2000.0f; - - m4x3_mul( gate->transport, cam->transform, gate_camera.transform ); - camera_update_view( &gate_camera ); - camera_update_projection( &gate_camera ); - - /* Add special clipping plane to projection */ - v4f surface; - q_mulv( gate->q[1], (v3f){0.0f,0.0f,-1.0f}, surface ); - surface[3] = v3_dot( surface, gate->co[1] ); - - m4x3_mulp( gate_camera.transform_inverse, surface, surface ); - surface[3] = -fabsf(surface[3]); - - if( dist < -0.5f ) - m4x4_clip_projection( gate_camera.mtx.p, surface ); - - /* Ready to draw with new camrea */ - camera_finalize( &gate_camera ); - - vg_line_pt3( gate_camera.transform[3], 0.3f, 0xff00ff00 ); - { - shader_model_gate_use(); - shader_model_gate_uPv( cam->mtx.pv ); - shader_model_gate_uMdl( gate->to_world ); - shader_model_gate_uCam( cam->pos ); - shader_model_gate_uColour( (v4f){0.0f,1.0f,0.0f,0.0f} ); - shader_model_gate_uTime( vg.time*0.25f ); - shader_model_gate_uInvRes( (v2f){ - 1.0f / (float)vg.window_x, - 1.0f / (float)vg.window_y }); - - glEnable( GL_STENCIL_TEST ); - glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE ); - glStencilFunc( GL_ALWAYS, 1, 0xFF ); - glStencilMask( 0xFF ); - - mesh_bind( &world_global.mesh_gate ); - mdl_draw_submesh( &world_global.sm_gate_surface ); + ent_gate *gate, camera *cam, int layer_depth ); - glClear( GL_DEPTH_BUFFER_BIT ); - glStencilFunc( GL_EQUAL, 1, 0xFF ); - glStencilMask( 0x00 ); - } - - render_world( world_inside, &gate_camera, layer_depth ); - - { - glDisable( GL_STENCIL_TEST ); - - render_water_texture( world_inside, &gate_camera, layer_depth ); - render_fb_bind( gpipeline.fb_main, 1 ); - - glEnable( GL_STENCIL_TEST ); - - render_water_surface( world_inside, &gate_camera ); - - glStencilMask( 0xFF ); - glStencilFunc( GL_ALWAYS, 1, 0xFF ); - glDisable( GL_STENCIL_TEST ); - } - - return 1; -} - -VG_STATIC int gate_intersect_plane( ent_gate *gate, - v3f pos, v3f last, v2f where ) -{ - v4f surface; - q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, surface ); - surface[3] = v3_dot( surface, gate->co[0] ); - - v3f v0, c, delta, p0; - v3_sub( pos, last, v0 ); - float l = v3_length( v0 ); - - if( l == 0.0f ) - return 0; - - v3_divs( v0, l, v0 ); - - v3_muls( surface, surface[3], c ); - v3_sub( c, last, delta ); - - float d = v3_dot( surface, v0 ); - - if( d > 0.00001f ){ - float t = v3_dot(delta, surface) / d; - if( t >= 0.0f && t <= l ){ - v3f local, rel; - v3_muladds( last, v0, t, local ); - v3_sub( gate->co[0], local, rel ); - - where[0] = v3_dot( rel, gate->to_world[0] ); - where[1] = v3_dot( rel, gate->to_world[1] ); - - where[0] /= v3_dot( gate->to_world[0], gate->to_world[0] ); - where[1] /= v3_dot( gate->to_world[1], gate->to_world[1] ); - - return 1; - } - } - - return 0; -} - -VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last ) -{ - v2f xy; - - if( gate_intersect_plane( gate, pos, last, xy ) ){ - if( fabsf(xy[0]) <= 1.0f && fabsf(xy[1]) <= 1.0f ){ - return 1; - } - } - - return 0; -} - -/* - * Intersect all gates in the world - */ +VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last ); VG_STATIC ent_gate *world_intersect_gates( world_instance *world, - v3f pos, v3f last ) -{ - for( u32 i=0; ient_gate); i++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, i ); - if( gate->type == k_gate_type_unlinked || - gate->type == k_gate_type_nonlocal_unlinked ) - continue; + v3f pos, v3f last ); - if( gate->type == k_gate_type_nonlocel ){ - if( skaterift.async_op == k_async_op_world_loading || - skaterift.async_op == k_async_op_world_preloading ){ - continue; - } - } - - if( gate_intersect( gate, pos, last ) ){ - return gate; - } - } - - return NULL; -} #endif /* WORLD_GATE_H */ diff --git a/world_gen.c b/world_gen.c new file mode 100644 index 0000000..799059a --- /dev/null +++ b/world_gen.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + * + * World generation/population. Different to regular loading, since it needs to + * create geometry, apply procedural stuff and save that image to files etc. + */ + +#ifndef WORLD_GEN_C +#define WORLD_GEN_C + +#include "world.h" +#include "world_gen.h" +#include "world_load.h" +#include "world_volumes.h" +#include "world_gate.h" + +/* + * Add all triangles from the model, which match the material ID + * applies affine transform to the model + */ +VG_STATIC void world_add_all_if_material( m4x3f transform, scene_context *scene, + mdl_context *mdl, u32 id ) +{ + for( u32 i=0; imeshs); i++ ){ + mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i ); + + for( u32 j=0; jsubmesh_count; j++ ){ + mdl_submesh *sm = mdl_arritm( &mdl->submeshs, mesh->submesh_start+j ); + if( sm->material_id == id ){ + m4x3f transform2; + mdl_transform_m4x3( &mesh->transform, transform2 ); + m4x3_mul( transform, transform2, transform2 ); + + scene_add_mdl_submesh( scene, mdl, sm, transform2 ); + } + } + } +} + +/* + * Adds a small blob shape to the world at a raycast location. This is for the + * grass sprites + * + * /''''\ + * / \ + * | | + * |________| + */ +VG_STATIC void world_gen_add_blob( scene_context *scene, ray_hit *hit ) +{ + world_instance *world = world_loading_instance(); + m4x3f transform; + v4f qsurface, qrandom; + v3f axis; + + v3_cross( (v3f){0.0f,1.0f,0.0f}, hit->normal, axis ); + + float angle = v3_dot(hit->normal,(v3f){0.0f,1.0f,0.0f}); + q_axis_angle( qsurface, axis, angle ); + q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf64()*VG_TAUf ); + q_mul( qsurface, qrandom, qsurface ); + q_m3x3( qsurface, transform ); + v3_copy( hit->pos, transform[3] ); + + scene_vert verts[] = + { + { .co = { -1.00f, 0.0f, 0.0f } }, + { .co = { 1.00f, 0.0f, 0.0f } }, + { .co = { -1.00f, 1.2f, 0.0f } }, + { .co = { 1.00f, 1.2f, 0.0f } }, + { .co = { -0.25f, 2.0f, 0.0f } }, + { .co = { 0.25f, 2.0f, 0.0f } } + }; + + const u32 indices[] = { 0,1,3, 0,3,2, 2,3,5, 2,5,4 }; + + if( scene->vertex_count + vg_list_size(verts) > scene->max_vertices ) + vg_fatal_error( "Scene vertex buffer overflow" ); + + if( scene->indice_count + vg_list_size(indices) > scene->max_indices ) + vg_fatal_error( "Scene index buffer overflow" ); + + scene_vert *dst_verts = &scene->arrvertices[ scene->vertex_count ]; + u32 *dst_indices = &scene->arrindices [ scene->indice_count ]; + + scene_vert *ref = &world->scene_geo.arrvertices[ hit->tri[0] ]; + + for( u32 i=0; ico, pvert->co ); + scene_vert_pack_norm( pvert, transform[1] ); + + v2_copy( ref->uv, pvert->uv ); + } + + for( u32 i=0; ivertex_count; + + scene->vertex_count += vg_list_size(verts); + scene->indice_count += vg_list_size(indices); +} + +/* + * Sprinkle foliage models over the map on terrain material + */ +VG_STATIC void world_apply_procedural_foliage( scene_context *scene, + struct world_surface *mat ) +{ + if( vg.quality_profile == k_quality_profile_low ) + return; + + world_instance *world = world_loading_instance(); + vg_info( "Applying foliage (%u)\n", mat->info.pstr_name ); + + v3f volume; + v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], volume ); + volume[1] = 1.0f; + + int count = 0; + + float area = volume[0]*volume[2]; + u32 particles = 0.08f * area; + + /* TODO: Quasirandom? */ + vg_info( "Map area: %f. Max particles: %u\n", area, particles ); + + for( u32 i=0; iscene_geo.bbx[0], pos ); + + ray_hit hit; + hit.dist = INFINITY; + + if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &hit )){ + struct world_surface *m1 = ray_hit_surface( world, &hit ); + if((hit.normal[1] > 0.8f) && (m1 == mat) && (hit.pos[1] > 0.0f+10.0f)){ + world_gen_add_blob( scene, &hit ); + count ++; + } + } + } + + vg_info( "%d foliage models added\n", count ); +} + +/* + * Create the main meshes for the world + */ +VG_STATIC void world_gen_generate_meshes(void) +{ + /* + * Compile meshes into the world scenes + */ + world_instance *world = world_loading_instance(); + scene_init( &world->scene_geo, 320000, 1200000 ); + u32 buf_size = scene_mem_required( &world->scene_geo ); + u8 *buffer = vg_linear_alloc( world->heap, buf_size ); + scene_supply_buffer( &world->scene_geo, buffer ); + + m4x3f midentity; + m4x3_identity( midentity ); + + /* + * Generate scene: collidable geometry + * ---------------------------------------------------------------- + */ + + vg_info( "Generating collidable geometry\n" ); + + for( u32 i=0; isurface_count; i++ ){ + struct world_surface *surf = &world->surfaces[ i ]; + + if( surf->info.flags & k_material_flag_collision ) + world_add_all_if_material( midentity, &world->scene_geo, + &world->meta, i ); + + scene_copy_slice( &world->scene_geo, &surf->sm_geo ); + } + + /* compress that bad boy */ + u32 new_vert_max = world->scene_geo.vertex_count, + new_vert_size = vg_align8(new_vert_max*sizeof(scene_vert)), + new_indice_len = world->scene_geo.indice_count*sizeof(u32); + + u32 *src_indices = world->scene_geo.arrindices, + *dst_indices = (u32 *)(buffer + new_vert_size); + + memmove( dst_indices, src_indices, new_indice_len ); + + world->scene_geo.max_indices = world->scene_geo.indice_count; + world->scene_geo.max_vertices = world->scene_geo.vertex_count; + buf_size = scene_mem_required( &world->scene_geo ); + + buffer = vg_linear_resize( world->heap, buffer, buf_size ); + + world->scene_geo.arrvertices = (scene_vert *)(buffer); + world->scene_geo.arrindices = (u32 *)(buffer + new_vert_size); + + scene_upload_async( &world->scene_geo, &world->mesh_geo ); + + /* 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 + * ---------------------------------------------------------------- + */ + vg_info( "Generating non-collidable geometry\n" ); + + vg_async_item *call = scene_alloc_async( &world->scene_no_collide, + &world->mesh_no_collide, + 200000, 500000 ); + + for( u32 i=0; isurface_count; i++ ){ + struct world_surface *surf = &world->surfaces[ i ]; + + if( !(surf->info.flags & k_material_flag_collision) ){ + world_add_all_if_material( midentity, + &world->scene_no_collide, &world->meta, i ); + } + + if( surf->info.flags & k_material_flag_grow_grass ) + world_apply_procedural_foliage( &world->scene_no_collide, surf ); + + scene_copy_slice( &world->scene_no_collide, &surf->sm_no_collide ); + } + + for( u32 i=0; ient_traffic ); i++ ){ + ent_traffic *vehc = mdl_arritm( &world->ent_traffic, i ); + + for( u32 j=0; jsubmesh_count; j++ ){ + mdl_submesh *sm = mdl_arritm( &world->meta.submeshs, + vehc->submesh_start+j ); + + if( sm->flags & k_submesh_flag_consumed ){ + continue; + } + + m4x3f identity; + m4x3_identity( identity ); + scene_add_mdl_submesh( &world->scene_no_collide, + &world->meta, sm, identity ); + + scene_copy_slice( &world->scene_no_collide, sm ); + sm->flags |= k_submesh_flag_consumed; + } + } + + vg_async_dispatch( call, async_scene_upload ); +} + +/* signed distance function for cone */ +static f32 fsd_cone_infinite( v3f p, v2f c ) +{ + v2f q = { v2_length( (v2f){ p[0], p[2] } ), -p[1] }; + float s = vg_maxf( 0.0f, v2_dot( q, c ) ); + + v2f v0; + v2_muls( c, s, v0 ); + v2_sub( q, v0, v0 ); + + float d = v2_length( v0 ); + return d * ((q[0]*c[1]-q[1]*c[0]<0.0f)?-1.0f:1.0f); +} + +struct light_indices_upload_info{ + world_instance *world; + v3i count; + + void *data; +}; + +/* + * Async reciever to buffer light index data + */ +VG_STATIC void async_upload_light_indices( void *payload, u32 size ) +{ + struct light_indices_upload_info *info = payload; + + glGenTextures( 1, &info->world->tex_light_cubes ); + glBindTexture( GL_TEXTURE_3D, info->world->tex_light_cubes ); + glTexImage3D( GL_TEXTURE_3D, 0, GL_RG32UI, + info->count[0], info->count[1], info->count[2], + 0, GL_RG_INTEGER, GL_UNSIGNED_INT, info->data ); + glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +} + +/* + * Computes light indices for world + */ +VG_STATIC void world_gen_compute_light_indices(void) +{ + /* light cubes */ + world_instance *world = world_loading_instance(); + v3f cubes_min, cubes_max; + v3_muls( world->scene_geo.bbx[0], 1.0f/k_world_light_cube_size, cubes_min ); + v3_muls( world->scene_geo.bbx[1], 1.0f/k_world_light_cube_size, cubes_max ); + + v3_sub( cubes_min, (v3f){ 0.5f, 0.5f, 0.5f }, cubes_min ); + v3_add( cubes_max, (v3f){ 0.5f, 0.5f, 0.5f }, cubes_max ); + + v3_floor( cubes_min, cubes_min ); + v3_floor( cubes_max, cubes_max ); + + v3i icubes_min, icubes_max; + + for( int i=0; i<3; i++ ){ + icubes_min[i] = cubes_min[i]; + icubes_max[i] = cubes_max[i]; + } + + v3f cube_size; + + v3i icubes_count; + v3i_sub( icubes_max, icubes_min, icubes_count ); + + for( int i=0; i<3; i++ ){ + int clamped_count = VG_MIN( 128, icubes_count[i]+1 ); + float clamped_max = icubes_min[i] + clamped_count, + max = icubes_min[i] + icubes_count[i]+1; + + icubes_count[i] = clamped_count; + cube_size[i] = (max / clamped_max) * k_world_light_cube_size; + cubes_max[i] = clamped_max; + } + + v3_mul( cubes_min, cube_size, cubes_min ); + v3_mul( cubes_max, cube_size, cubes_max ); + + for( int i=0; i<3; i++ ){ + float range = cubes_max[i]-cubes_min[i]; + world->ub_lighting.g_cube_inv_range[i] = 1.0f / range; + world->ub_lighting.g_cube_inv_range[i] *= (float)icubes_count[i]; + + vg_info( "cubes[%d]: %d\n", i, icubes_count[i] ); + } + + int total_cubes = icubes_count[0]*icubes_count[1]*icubes_count[2]; + + u32 data_size = vg_align8(total_cubes*sizeof(u32)*2), + hdr_size = vg_align8(sizeof(struct light_indices_upload_info)); + + vg_async_item *call = vg_async_alloc( data_size + hdr_size ); + struct light_indices_upload_info *info = call->payload; + info->data = ((u8*)call->payload) + hdr_size; + info->world = world; + u32 *cubes_index = info->data; + + for( int i=0; i<3; i++ ) + info->count[i] = icubes_count[i]; + + vg_info( "Computing light cubes (%d) [%f %f %f] -> [%f %f %f]\n", + total_cubes, cubes_min[0], -cubes_min[2], cubes_min[1], + cubes_max[0], -cubes_max[2], cubes_max[1] ); + v3_copy( cubes_min, world->ub_lighting.g_cube_min ); + + float bound_radius = v3_length( cube_size ); + + for( int iz = 0; izub_lighting.g_cube_inv_range, + bbx[0] ); + v3_div( (v3f){ ix+1, iy+1, iz+1 }, + world->ub_lighting.g_cube_inv_range, + bbx[1] ); + + v3_add( bbx[0], world->ub_lighting.g_cube_min, bbx[0] ); + v3_add( bbx[1], world->ub_lighting.g_cube_min, bbx[1] ); + + v3f center; + v3_add( bbx[0], bbx[1], center ); + v3_muls( center, 0.5f, center ); + + u32 indices[6] = { 0, 0, 0, 0, 0, 0 }; + u32 count = 0; + + float influences[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + const int N = vg_list_size( influences ); + + for( u32 j=0; jent_light); j ++ ){ + ent_light *light = mdl_arritm( &world->ent_light, j ); + v3f closest; + closest_point_aabb( light->transform.co, bbx, closest ); + + float dist = v3_dist( closest, light->transform.co ), + influence = 1.0f/(dist+1.0f); + + if( dist > light->range ) + continue; + + if( light->type == k_light_type_spot){ + v3f local; + m4x3_mulv( light->inverse_world, center, local ); + + float r = fsd_cone_infinite( local, light->angle_sin_cos ); + + if( r > bound_radius ) + continue; + } + + int best_pos = N; + for( int k=best_pos-1; k>=0; k -- ) + if( influence > influences[k] ) + best_pos = k; + + if( best_pos < N ){ + for( int k=N-1; k>best_pos; k -- ){ + influences[k] = influences[k-1]; + indices[k] = indices[k-1]; + } + + influences[best_pos] = influence; + indices[best_pos] = j; + } + } + + for( int j=0; j 0.0f ) + count ++; + + int base_index = iz * (icubes_count[0]*icubes_count[1]) + + iy * (icubes_count[0]) + + ix; + + int lower_count = VG_MIN( 3, count ); + u32 packed_index_lower = lower_count; + packed_index_lower |= indices[0]<<2; + packed_index_lower |= indices[1]<<12; + packed_index_lower |= indices[2]<<22; + + int upper_count = VG_MAX( 0, count - lower_count ); + u32 packed_index_upper = upper_count; + packed_index_upper |= indices[3]<<2; + packed_index_upper |= indices[4]<<12; + packed_index_upper |= indices[5]<<22; + + cubes_index[ base_index*2 + 0 ] = packed_index_lower; + cubes_index[ base_index*2 + 1 ] = packed_index_upper; + } + } + } + + vg_async_dispatch( call, async_upload_light_indices ); +} + +/* + * Rendering pass needed to complete the world + */ +VG_STATIC void async_world_postprocess_render( void *payload, u32 _size ) +{ + /* create scene lighting buffer */ + world_instance *world = world_loading_instance(); + + u32 size = VG_MAX(mdl_arrcount(&world->ent_light),1) * sizeof(float)*12; + vg_info( "Upload %ubytes (lighting)\n", size ); + + glGenBuffers( 1, &world->tbo_light_entities ); + glBindBuffer( GL_TEXTURE_BUFFER, world->tbo_light_entities ); + glBufferData( GL_TEXTURE_BUFFER, size, NULL, GL_DYNAMIC_DRAW ); + + /* buffer layout + * + * colour position direction (spots) + * | . . . . | . . . . | . . . . | + * | Re Ge Be Night | Xco Yco Zco Range | Dx Dy Dz Da | + * + */ + + v4f *light_dst = glMapBuffer( GL_TEXTURE_BUFFER, GL_WRITE_ONLY ); + for( u32 i=0; ient_light); i++ ){ + ent_light *light = mdl_arritm( &world->ent_light, i ); + + /* colour + night */ + v3_muls( light->colour, light->colour[3] * 2.0f, light_dst[i*3+0] ); + light_dst[i*3+0][3] = 2.0f; + + if( !light->daytime ){ + u32 hash = (i * 29986577u) & 0xffu; + float switch_on = hash; + switch_on *= (1.0f/255.0f); + + light_dst[i*3+0][3] = 0.44f + switch_on * 0.015f; + } + + /* position + 1/range^2 */ + v3_copy( light->transform.co, light_dst[i*3+1] ); + light_dst[i*3+1][3] = 1.0f/(light->range*light->range); + + /* direction + angle */ + q_mulv( light->transform.q, (v3f){0.0f,-1.0f,0.0f}, light_dst[i*3+2]); + light_dst[i*3+2][3] = cosf( light->angle ); + } + + glUnmapBuffer( GL_TEXTURE_BUFFER ); + + glGenTextures( 1, &world->tex_light_entities ); + glBindTexture( GL_TEXTURE_BUFFER, world->tex_light_entities ); + glTexBuffer( GL_TEXTURE_BUFFER, GL_RGBA32F, world->tbo_light_entities ); + + /* Upload lighting uniform buffer */ + if( world->water.enabled ) + v4_copy( world->water.plane, world->ub_lighting.g_water_plane ); + + v4f info_vec; + v3f *bounds = world->scene_geo.bbx; + + info_vec[0] = bounds[0][0]; + info_vec[1] = bounds[0][2]; + info_vec[2] = 1.0f/ (bounds[1][0]-bounds[0][0]); + info_vec[3] = 1.0f/ (bounds[1][2]-bounds[0][2]); + v4_copy( info_vec, world->ub_lighting.g_depth_bounds ); + + /* + * Rendering the depth map + */ + camera ortho; + + v3f extent; + v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], extent ); + + float fl = world->scene_geo.bbx[0][0], + fr = world->scene_geo.bbx[1][0], + fb = world->scene_geo.bbx[0][2], + ft = world->scene_geo.bbx[1][2], + rl = 1.0f / (fr-fl), + tb = 1.0f / (ft-fb); + + m4x4_zero( ortho.mtx.p ); + ortho.mtx.p[0][0] = 2.0f * rl; + ortho.mtx.p[2][1] = 2.0f * tb; + ortho.mtx.p[3][0] = (fr + fl) * -rl; + ortho.mtx.p[3][1] = (ft + fb) * -tb; + ortho.mtx.p[3][3] = 1.0f; + m4x3_identity( ortho.transform ); + camera_update_view( &ortho ); + camera_finalize( &ortho ); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + render_fb_bind( &world->heightmap, 0 ); + shader_blitcolour_use(); + shader_blitcolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} ); + render_fsquad(); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_MAX); + + render_world_position( world, &ortho ); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + /* upload full buffer */ + glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); + glBufferSubData( GL_UNIFORM_BUFFER, 0, + sizeof(struct ub_world_lighting), &world->ub_lighting ); +} + +/* Loads textures from the pack file */ +VG_STATIC void world_gen_load_surfaces(void) +{ + world_instance *world = world_loading_instance(); + vg_info( "Loading textures\n" ); + world->texture_count = 0; + + world->texture_count = world->meta.textures.count+1; + world->textures = vg_linear_alloc( world->heap, + vg_align8(sizeof(GLuint)*world->texture_count) ); + + vg_tex2d_replace_with_error( &world->textures[0] ); + + for( u32 i=0; imeta.textures); i++ ){ + mdl_texture *tex = mdl_arritm( &world->meta.textures, i ); + + if( !tex->file.pack_size ){ + vg_fatal_error( "World models must have packed textures!" ); + } + + vg_linear_clear( vg_mem.scratch ); + void *src_data = vg_linear_alloc( vg_mem.scratch, + tex->file.pack_size ); + mdl_fread_pack_file( &world->meta, &tex->file, src_data ); + + vg_tex2d_load_qoi_async( src_data, tex->file.pack_size, + VG_TEX2D_NEAREST|VG_TEX2D_REPEAT, + &world->textures[i+1] ); + } + + vg_info( "Loading materials\n" ); + + world->surface_count = world->meta.materials.count+1; + world->surfaces = vg_linear_alloc( world->heap, + vg_align8(sizeof(struct world_surface)*world->surface_count) ); + + /* error material */ + struct world_surface *errmat = &world->surfaces[0]; + memset( errmat, 0, sizeof(struct world_surface) ); + + for( u32 i=0; imeta.materials); i++ ){ + world->surfaces[i+1].info = + *(mdl_material *)mdl_arritm( &world->meta.materials, i ); + } +} + +#endif /* WORLD_GEN_C */ diff --git a/world_gen.h b/world_gen.h index b5b06e5..90c01e3 100644 --- a/world_gen.h +++ b/world_gen.h @@ -1,5 +1,8 @@ /* - * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + * + * World generation/population. Different to regular loading, since it needs to + * create geometry, apply procedural stuff and save that image to files etc. */ #ifndef WORLD_GEN_H @@ -7,932 +10,10 @@ #include "world.h" -VG_STATIC void world_load( u32 index, const char *path ); - -VG_STATIC void world_add_all_if_material( m4x3f transform, scene_context *scene, - mdl_context *mdl, u32 id ) -{ - for( u32 i=0; imeshs); i++ ){ - mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i ); - - for( u32 j=0; jsubmesh_count; j++ ){ - mdl_submesh *sm = mdl_arritm( &mdl->submeshs, mesh->submesh_start+j ); - if( sm->material_id == id ){ - m4x3f transform2; - mdl_transform_m4x3( &mesh->transform, transform2 ); - m4x3_mul( transform, transform2, transform2 ); - - scene_add_mdl_submesh( scene, mdl, sm, transform2 ); - } - } - } -} - -VG_STATIC void world_add_blob( world_instance *world, - scene_context *scene, ray_hit *hit ) -{ - m4x3f transform; - v4f qsurface, qrandom; - v3f axis; - - v3_cross( (v3f){0.0f,1.0f,0.0f}, hit->normal, axis ); - - float angle = v3_dot(hit->normal,(v3f){0.0f,1.0f,0.0f}); - q_axis_angle( qsurface, axis, angle ); - q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf64()*VG_TAUf ); - q_mul( qsurface, qrandom, qsurface ); - q_m3x3( qsurface, transform ); - v3_copy( hit->pos, transform[3] ); - - scene_vert verts[] = - { - { .co = { -1.00f, 0.0f, 0.0f } }, - { .co = { 1.00f, 0.0f, 0.0f } }, - { .co = { -1.00f, 1.2f, 0.0f } }, - { .co = { 1.00f, 1.2f, 0.0f } }, - { .co = { -0.25f, 2.0f, 0.0f } }, - { .co = { 0.25f, 2.0f, 0.0f } } - }; - - const u32 indices[] = { 0,1,3, 0,3,2, 2,3,5, 2,5,4 }; - - if( scene->vertex_count + vg_list_size(verts) > scene->max_vertices ) - vg_fatal_error( "Scene vertex buffer overflow" ); - - if( scene->indice_count + vg_list_size(indices) > scene->max_indices ) - vg_fatal_error( "Scene index buffer overflow" ); - - scene_vert *dst_verts = &scene->arrvertices[ scene->vertex_count ]; - u32 *dst_indices = &scene->arrindices [ scene->indice_count ]; - - scene_vert *ref = &world->scene_geo.arrvertices[ hit->tri[0] ]; - - for( u32 i=0; ico, pvert->co ); - scene_vert_pack_norm( pvert, transform[1] ); - - v2_copy( ref->uv, pvert->uv ); - } - - for( u32 i=0; ivertex_count; - - scene->vertex_count += vg_list_size(verts); - scene->indice_count += vg_list_size(indices); -} - -/* Sprinkle foliage models over the map on terrain material */ -VG_STATIC void world_apply_procedural_foliage( world_instance *world, - scene_context *scene, - struct world_surface *mat ) -{ - if( vg.quality_profile == k_quality_profile_low ) - return; - - vg_info( "Applying foliage (%u)\n", mat->info.pstr_name ); - - v3f volume; - v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], volume ); - volume[1] = 1.0f; - - int count = 0; - - float area = volume[0]*volume[2]; - u32 particles = 0.08f * area; - - /* TODO: Quasirandom? */ - vg_info( "Map area: %f. Max particles: %u\n", area, particles ); - - for( u32 i=0; iscene_geo.bbx[0], pos ); - - ray_hit hit; - hit.dist = INFINITY; - - if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &hit )){ - struct world_surface *m1 = ray_hit_surface( world, &hit ); - if((hit.normal[1] > 0.8f) && (m1 == mat) && (hit.pos[1] > 0.0f+10.0f)){ - world_add_blob( world, scene, &hit ); - count ++; - } - } - } - - vg_info( "%d foliage models added\n", count ); -} - -VG_STATIC void world_generate( world_instance *world ) -{ - /* - * Compile meshes into the world scenes - */ - scene_init( &world->scene_geo, 320000, 1200000 ); - u32 buf_size = scene_mem_required( &world->scene_geo ); - u8 *buffer = vg_linear_alloc( world->heap, buf_size ); - scene_supply_buffer( &world->scene_geo, buffer ); - - m4x3f midentity; - m4x3_identity( midentity ); - - /* - * Generate scene: collidable geometry - * ---------------------------------------------------------------- - */ - - vg_info( "Generating collidable geometry\n" ); - - for( u32 i=0; isurface_count; i++ ){ - struct world_surface *surf = &world->surfaces[ i ]; - - if( surf->info.flags & k_material_flag_collision ) - world_add_all_if_material( midentity, &world->scene_geo, - &world->meta, i ); - - scene_copy_slice( &world->scene_geo, &surf->sm_geo ); - } - - /* compress that bad boy */ - u32 new_vert_max = world->scene_geo.vertex_count, - new_vert_size = vg_align8(new_vert_max*sizeof(scene_vert)), - new_indice_len = world->scene_geo.indice_count*sizeof(u32); - - u32 *src_indices = world->scene_geo.arrindices, - *dst_indices = (u32 *)(buffer + new_vert_size); - - memmove( dst_indices, src_indices, new_indice_len ); - - world->scene_geo.max_indices = world->scene_geo.indice_count; - world->scene_geo.max_vertices = world->scene_geo.vertex_count; - buf_size = scene_mem_required( &world->scene_geo ); - - buffer = vg_linear_resize( world->heap, buffer, buf_size ); - - world->scene_geo.arrvertices = (scene_vert *)(buffer); - world->scene_geo.arrindices = (u32 *)(buffer + new_vert_size); - - scene_upload_async( &world->scene_geo, &world->mesh_geo ); - - /* 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 - * ---------------------------------------------------------------- - */ - vg_info( "Generating non-collidable geometry\n" ); - - vg_async_item *call = scene_alloc_async( &world->scene_no_collide, - &world->mesh_no_collide, - 200000, 500000 ); - - for( u32 i=0; isurface_count; i++ ){ - struct world_surface *surf = &world->surfaces[ i ]; - - if( !(surf->info.flags & k_material_flag_collision) ){ - world_add_all_if_material( midentity, - &world->scene_no_collide, &world->meta, i ); - } - - if( surf->info.flags & k_material_flag_grow_grass ) - world_apply_procedural_foliage( world, - &world->scene_no_collide, surf ); - - scene_copy_slice( &world->scene_no_collide, &surf->sm_no_collide ); - } - - for( u32 i=0; ient_traffic ); i++ ){ - ent_traffic *vehc = mdl_arritm( &world->ent_traffic, i ); - - for( u32 j=0; jsubmesh_count; j++ ){ - mdl_submesh *sm = mdl_arritm( &world->meta.submeshs, - vehc->submesh_start+j ); - - if( sm->flags & k_submesh_flag_consumed ){ - continue; - } - - m4x3f identity; - m4x3_identity( identity ); - scene_add_mdl_submesh( &world->scene_no_collide, - &world->meta, sm, identity ); - - scene_copy_slice( &world->scene_no_collide, sm ); - sm->flags |= k_submesh_flag_consumed; - } - } - - vg_async_dispatch( call, async_scene_upload ); -} - -float fsd_cone_infinite( v3f p, v2f c ) -{ - v2f q = { v2_length( (v2f){ p[0], p[2] } ), -p[1] }; - float s = vg_maxf( 0.0f, v2_dot( q, c ) ); - - v2f v0; - v2_muls( c, s, v0 ); - v2_sub( q, v0, v0 ); - - float d = v2_length( v0 ); - return d * ((q[0]*c[1]-q[1]*c[0]<0.0f)?-1.0f:1.0f); -} - -struct light_indices_upload_info{ - world_instance *world; - v3i count; - - void *data; -}; - -VG_STATIC void async_upload_light_indices( void *payload, u32 size ) -{ - struct light_indices_upload_info *info = payload; - - glGenTextures( 1, &info->world->tex_light_cubes ); - glBindTexture( GL_TEXTURE_3D, info->world->tex_light_cubes ); - glTexImage3D( GL_TEXTURE_3D, 0, GL_RG32UI, - info->count[0], info->count[1], info->count[2], - 0, GL_RG_INTEGER, GL_UNSIGNED_INT, info->data ); - glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); -} - -VG_STATIC void world_compute_light_indices( world_instance *world ) -{ - /* light cubes */ - v3f cubes_min, cubes_max; - v3_muls( world->scene_geo.bbx[0], 1.0f/k_light_cube_size, cubes_min ); - v3_muls( world->scene_geo.bbx[1], 1.0f/k_light_cube_size, cubes_max ); - - v3_sub( cubes_min, (v3f){ 0.5f, 0.5f, 0.5f }, cubes_min ); - v3_add( cubes_max, (v3f){ 0.5f, 0.5f, 0.5f }, cubes_max ); - - v3_floor( cubes_min, cubes_min ); - v3_floor( cubes_max, cubes_max ); - - v3i icubes_min, icubes_max; - - for( int i=0; i<3; i++ ){ - icubes_min[i] = cubes_min[i]; - icubes_max[i] = cubes_max[i]; - } - - v3f cube_size; - - v3i icubes_count; - v3i_sub( icubes_max, icubes_min, icubes_count ); - - for( int i=0; i<3; i++ ){ - int clamped_count = VG_MIN( 128, icubes_count[i]+1 ); - float clamped_max = icubes_min[i] + clamped_count, - max = icubes_min[i] + icubes_count[i]+1; - - icubes_count[i] = clamped_count; - cube_size[i] = (max / clamped_max) * k_light_cube_size; - cubes_max[i] = clamped_max; - } - - v3_mul( cubes_min, cube_size, cubes_min ); - v3_mul( cubes_max, cube_size, cubes_max ); - - for( int i=0; i<3; i++ ){ - float range = cubes_max[i]-cubes_min[i]; - world->ub_lighting.g_cube_inv_range[i] = 1.0f / range; - world->ub_lighting.g_cube_inv_range[i] *= (float)icubes_count[i]; - - vg_info( "cubes[%d]: %d\n", i, icubes_count[i] ); - } - - int total_cubes = icubes_count[0]*icubes_count[1]*icubes_count[2]; - - u32 data_size = vg_align8(total_cubes*sizeof(u32)*2), - hdr_size = vg_align8(sizeof(struct light_indices_upload_info)); - - vg_async_item *call = vg_async_alloc( data_size + hdr_size ); - struct light_indices_upload_info *info = call->payload; - info->data = ((u8*)call->payload) + hdr_size; - info->world = world; - u32 *cubes_index = info->data; - - for( int i=0; i<3; i++ ) - info->count[i] = icubes_count[i]; - - vg_info( "Computing light cubes (%d) [%f %f %f] -> [%f %f %f]\n", - total_cubes, cubes_min[0], -cubes_min[2], cubes_min[1], - cubes_max[0], -cubes_max[2], cubes_max[1] ); - v3_copy( cubes_min, world->ub_lighting.g_cube_min ); - - float bound_radius = v3_length( cube_size ); - - for( int iz = 0; izub_lighting.g_cube_inv_range, - bbx[0] ); - v3_div( (v3f){ ix+1, iy+1, iz+1 }, - world->ub_lighting.g_cube_inv_range, - bbx[1] ); - - v3_add( bbx[0], world->ub_lighting.g_cube_min, bbx[0] ); - v3_add( bbx[1], world->ub_lighting.g_cube_min, bbx[1] ); - - v3f center; - v3_add( bbx[0], bbx[1], center ); - v3_muls( center, 0.5f, center ); - - u32 indices[6] = { 0, 0, 0, 0, 0, 0 }; - u32 count = 0; - - float influences[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; - const int N = vg_list_size( influences ); - - for( u32 j=0; jent_light); j ++ ){ - ent_light *light = mdl_arritm( &world->ent_light, j ); - v3f closest; - closest_point_aabb( light->transform.co, bbx, closest ); - - float dist = v3_dist( closest, light->transform.co ), - influence = 1.0f/(dist+1.0f); - - if( dist > light->range ) - continue; - - if( light->type == k_light_type_spot){ - v3f local; - m4x3_mulv( light->inverse_world, center, local ); - - float r = fsd_cone_infinite( local, light->angle_sin_cos ); - - if( r > bound_radius ) - continue; - } - - int best_pos = N; - for( int k=best_pos-1; k>=0; k -- ) - if( influence > influences[k] ) - best_pos = k; - - if( best_pos < N ){ - for( int k=N-1; k>best_pos; k -- ){ - influences[k] = influences[k-1]; - indices[k] = indices[k-1]; - } - - influences[best_pos] = influence; - indices[best_pos] = j; - } - } - - for( int j=0; j 0.0f ) - count ++; - - int base_index = iz * (icubes_count[0]*icubes_count[1]) + - iy * (icubes_count[0]) + - ix; - - int lower_count = VG_MIN( 3, count ); - u32 packed_index_lower = lower_count; - packed_index_lower |= indices[0]<<2; - packed_index_lower |= indices[1]<<12; - packed_index_lower |= indices[2]<<22; - - int upper_count = VG_MAX( 0, count - lower_count ); - u32 packed_index_upper = upper_count; - packed_index_upper |= indices[3]<<2; - packed_index_upper |= indices[4]<<12; - packed_index_upper |= indices[5]<<22; - - cubes_index[ base_index*2 + 0 ] = packed_index_lower; - cubes_index[ base_index*2 + 1 ] = packed_index_upper; - } - } - } - - vg_async_dispatch( call, async_upload_light_indices ); -} - -VG_STATIC void async_world_postprocess_render( void *payload, u32 _size ) -{ - /* create scene lighting buffer */ - world_instance *world = payload; - - u32 size = VG_MAX(mdl_arrcount(&world->ent_light),1) * sizeof(float)*12; - vg_info( "Upload %ubytes (lighting)\n", size ); - - glGenBuffers( 1, &world->tbo_light_entities ); - glBindBuffer( GL_TEXTURE_BUFFER, world->tbo_light_entities ); - glBufferData( GL_TEXTURE_BUFFER, size, NULL, GL_DYNAMIC_DRAW ); - - /* buffer layout - * - * colour position direction (spots) - * | . . . . | . . . . | . . . . | - * | Re Ge Be Night | Xco Yco Zco Range | Dx Dy Dz Da | - * - */ - - v4f *light_dst = glMapBuffer( GL_TEXTURE_BUFFER, GL_WRITE_ONLY ); - for( u32 i=0; ient_light); i++ ){ - ent_light *light = mdl_arritm( &world->ent_light, i ); - - /* colour + night */ - v3_muls( light->colour, light->colour[3] * 2.0f, light_dst[i*3+0] ); - light_dst[i*3+0][3] = 2.0f; - - if( !light->daytime ){ - u32 hash = (i * 29986577u) & 0xffu; - float switch_on = hash; - switch_on *= (1.0f/255.0f); - - light_dst[i*3+0][3] = 0.44f + switch_on * 0.015f; - } - - /* position + 1/range^2 */ - v3_copy( light->transform.co, light_dst[i*3+1] ); - light_dst[i*3+1][3] = 1.0f/(light->range*light->range); - - /* direction + angle */ - q_mulv( light->transform.q, (v3f){0.0f,-1.0f,0.0f}, light_dst[i*3+2]); - light_dst[i*3+2][3] = cosf( light->angle ); - } - - glUnmapBuffer( GL_TEXTURE_BUFFER ); - - glGenTextures( 1, &world->tex_light_entities ); - glBindTexture( GL_TEXTURE_BUFFER, world->tex_light_entities ); - glTexBuffer( GL_TEXTURE_BUFFER, GL_RGBA32F, world->tbo_light_entities ); - - /* Upload lighting uniform buffer */ - if( world->water.enabled ) - v4_copy( world->water.plane, world->ub_lighting.g_water_plane ); - - v4f info_vec; - v3f *bounds = world->scene_geo.bbx; - - info_vec[0] = bounds[0][0]; - info_vec[1] = bounds[0][2]; - info_vec[2] = 1.0f/ (bounds[1][0]-bounds[0][0]); - info_vec[3] = 1.0f/ (bounds[1][2]-bounds[0][2]); - v4_copy( info_vec, world->ub_lighting.g_depth_bounds ); - - /* - * Rendering the depth map - */ - camera ortho; - - v3f extent; - v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], extent ); - - float fl = world->scene_geo.bbx[0][0], - fr = world->scene_geo.bbx[1][0], - fb = world->scene_geo.bbx[0][2], - ft = world->scene_geo.bbx[1][2], - rl = 1.0f / (fr-fl), - tb = 1.0f / (ft-fb); - - m4x4_zero( ortho.mtx.p ); - ortho.mtx.p[0][0] = 2.0f * rl; - ortho.mtx.p[2][1] = 2.0f * tb; - ortho.mtx.p[3][0] = (fr + fl) * -rl; - ortho.mtx.p[3][1] = (ft + fb) * -tb; - ortho.mtx.p[3][3] = 1.0f; - m4x3_identity( ortho.transform ); - camera_update_view( &ortho ); - camera_finalize( &ortho ); - - glDisable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); - render_fb_bind( &world->heightmap, 0 ); - shader_blitcolour_use(); - shader_blitcolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} ); - render_fsquad(); - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - glBlendEquation(GL_MAX); - - render_world_position( world, &ortho ); - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - - /* upload full buffer */ - glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); - glBufferSubData( GL_UNIFORM_BUFFER, 0, - sizeof(struct ub_world_lighting), &world->ub_lighting ); -} - -VG_STATIC int reset_player( int argc, char const *argv[] ); -VG_STATIC void world_post_process( world_instance *world ) -{ - world_compute_light_indices( world ); - vg_async_call( async_world_postprocess_render, world, 0 ); -} - -VG_STATIC void world_process_resources( world_instance *world ) -{ - vg_info( "Loading textures\n" ); - - world->texture_count = 0; - - world->texture_count = world->meta.textures.count+1; - world->textures = vg_linear_alloc( world->heap, - vg_align8(sizeof(GLuint)*world->texture_count) ); - - vg_tex2d_replace_with_error( &world->textures[0] ); - - for( u32 i=0; imeta.textures); i++ ){ - mdl_texture *tex = mdl_arritm( &world->meta.textures, i ); - - if( !tex->file.pack_size ){ - vg_fatal_error( "World models must have packed textures!" ); - } - - vg_linear_clear( vg_mem.scratch ); - void *src_data = vg_linear_alloc( vg_mem.scratch, - tex->file.pack_size ); - mdl_fread_pack_file( &world->meta, &tex->file, src_data ); - - vg_tex2d_load_qoi_async( src_data, tex->file.pack_size, - VG_TEX2D_NEAREST|VG_TEX2D_REPEAT, - &world->textures[i+1] ); - } - - vg_info( "Loading materials\n" ); - - world->surface_count = world->meta.materials.count+1; - world->surfaces = vg_linear_alloc( world->heap, - vg_align8(sizeof(struct world_surface)*world->surface_count) ); - - /* error material */ - struct world_surface *errmat = &world->surfaces[0]; - memset( errmat, 0, sizeof(struct world_surface) ); - - for( u32 i=0; imeta.materials); i++ ){ - world->surfaces[i+1].info = - *(mdl_material *)mdl_arritm( &world->meta.materials, i ); - } -} - -VG_STATIC void world_free( world_instance *world ) -{ - vg_info( "Free world @%p\n", world ); - - /* free meshes */ - mesh_free( &world->mesh_route_lines ); - mesh_free( &world->mesh_geo ); - mesh_free( &world->mesh_no_collide ); - mesh_free( &world->mesh_water ); - - /* glDeleteBuffers silently ignores 0's and names that do not correspond to - * existing buffer objects. - * */ - glDeleteBuffers( 1, &world->tbo_light_entities ); - glDeleteTextures( 1, &world->tex_light_entities ); - glDeleteTextures( 1, &world->tex_light_cubes ); - - /* delete textures and meshes */ - glDeleteTextures( world->texture_count, world->textures ); - - u32 world_index = world - world_global.worlds; - if( world_index ){ - vg_linear_del( world_global.worlds[world_index-1].heap, - vg_linear_header(world->heap) ); - } - - world->status = k_world_status_unloaded; -} - -/* - * checks: - * 1. to see if all audios owned by the world have been stopped - * 2. that this is the least significant world - */ -VG_STATIC int world_freeable( world_instance *world ) -{ - if( world->status != k_world_status_unloading ) return 0; - u8 world_id = (world - world_global.worlds) + 1; - - for( u32 i=world_id; iallocated && (ch->world_id == world_id)){ - if( !audio_channel_finished( ch ) ){ - freeable = 0; - break; - } - } - } - audio_unlock(); - return freeable; -} - -VG_STATIC void world_init_blank( world_instance *world ) -{ - memset( &world->meta, 0, sizeof(mdl_context) ); - - world->textures = NULL; - world->texture_count = 0; - world->surfaces = NULL; - world->surface_count = 0; - - world->geo_bh = NULL; - world->volume_bh = NULL; - world->audio_bh = NULL; - world->rendering_gate = NULL; - - world->water.enabled = 0; - world->time = 0.0; - - /* default lighting conditions - * -------------------------------------------------------------*/ - struct ub_world_lighting *state = &world->ub_lighting; - - state->g_light_preview = 0; - state->g_shadow_samples = 8; - state->g_water_fog = 0.04f; - - v4_zero( state->g_water_plane ); - v4_zero( state->g_depth_bounds ); - - state->g_shadow_length = 9.50f; - state->g_shadow_spread = 0.65f; - - v3_copy( (v3f){0.37f, 0.54f, 0.97f}, state->g_daysky_colour ); - v3_copy( (v3f){0.03f, 0.05f, 0.20f}, state->g_nightsky_colour ); - v3_copy( (v3f){1.00f, 0.32f, 0.01f}, state->g_sunset_colour ); - v3_copy( (v3f){0.13f, 0.17f, 0.35f}, state->g_ambient_colour ); - v3_copy( (v3f){0.25f, 0.17f, 0.51f}, state->g_sunset_ambient ); - v3_copy( (v3f){1.10f, 0.89f, 0.35f}, state->g_sun_colour ); -} - -/* detatches any nonlocal gates */ -VG_STATIC void world_unlink_nonlocal( world_instance *world ) -{ - for( u32 j=0; jent_gate); j ++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, j ); - - if( gate->type == k_gate_type_nonlocel ){ - gate->type = k_gate_type_nonlocal_unlinked; - } - } -} - -/* attatches nonlocal gates, to be called from main thread ONLY! */ -VG_STATIC void world_link_nonlocal_async( void *payload, u32 size ) -{ - world_instance *world = payload; - u32 world_id = world - world_global.worlds; - - for( u32 j=0; jent_gate); j ++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, j ); - - if( gate->type == k_gate_type_nonlocal_unlinked ){ - const char *key = mdl_pstr( &world->meta, gate->key ); - vg_info( "key: %s\n", key ); - - for( u32 i=0; istatus != k_world_status_loaded ) continue; - vg_info( "Checking world %u for key matches\n", i ); - - for( u32 j=0; jent_gate ); j++ ){ - ent_gate *gate2 = mdl_arritm( &other->ent_gate, j ); - if( gate2->type != k_gate_type_nonlocal_unlinked ) continue; - - const char *key2 = mdl_pstr( &other->meta, gate2->key ); - vg_info( " key2: %s\n", key2 ); - - if( strcmp( key, key2 ) ) continue; - - vg_success( "Non-local matching pair '%s' found. (%u:%u)\n", - key, world_id, i ); - - gate->type = k_gate_type_nonlocel; - gate2->type = k_gate_type_nonlocel; - gate->target = i; - gate2->target = world_id; - - v3_copy( gate->co[0], gate2->co[1] ); - v3_copy( gate2->co[0], gate->co[1] ); - v4_copy( gate->q[0], gate2->q[1] ); - v4_copy( gate2->q[0], gate->q[1] ); - - v4f qflip; - q_axis_angle( qflip, (v3f){0.0f,1.0f,0.0f}, VG_PIf ); - q_mul( gate->q[0], qflip, gate->q[0] ); - q_mul( gate->q[1], qflip, gate->q[1] ); - - gate_transform_update( gate ); - gate_transform_update( gate2 ); - - goto matched; - } - } -matched:; - } - } -} - -VG_STATIC void world_entities_init( u32 world_id ) -{ - world_instance *world = &world_global.worlds[world_id]; - - /* lights */ - for( u32 j=0; jent_light); j ++ ){ - ent_light *light = mdl_arritm( &world->ent_light, j ); - - m4x3f to_world; - q_m3x3( light->transform.q, to_world ); - v3_copy( light->transform.co, to_world[3] ); - m4x3_invert_affine( to_world, light->inverse_world ); - - light->angle_sin_cos[0] = sinf( light->angle * 0.5f ); - light->angle_sin_cos[1] = cosf( light->angle * 0.5f ); - } - - /* gates */ - for( u32 j=0; jent_gate); j ++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, j ); - - if( gate->type == k_gate_type_teleport ){ - gate_transform_update( gate ); - } - } - vg_async_call( world_link_nonlocal_async, world, 0 ); - - /* water */ - for( u32 j=0; jent_water); j++ ){ - ent_water *water = mdl_arritm( &world->ent_water, j ); - if( world->water.enabled ){ - vg_warn( "Multiple water surfaces in level!\n" ); - break; - } - - world->water.enabled = 1; - water_set_surface( world, water->transform.co[1] ); - } - - /* volumes */ - for( u32 j=0; jent_volume); j++ ){ - ent_volume *volume = mdl_arritm( &world->ent_volume, j ); - mdl_transform_m4x3( &volume->transform, volume->to_world ); - m4x3_invert_full( volume->to_world, volume->to_local ); - } - - /* audio packs */ - for( u32 j=0; jent_audio); j++ ){ - ent_audio *audio = mdl_arritm( &world->ent_audio, j ); - - for( u32 k=0; kclip_count; k++ ){ - ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip, - audio->clip_start+k ); - - if( clip->file.pack_size ){ - u32 size = clip->file.pack_size, - offset = clip->file.pack_offset; - - /* embedded files are fine to clear the scratch buffer, only - * external audio uses it */ - - vg_linear_clear( vg_mem.scratch ); - void *data = vg_linear_alloc( vg_mem.scratch, - clip->file.pack_size ); - - mdl_fread_pack_file( &world->meta, &clip->file, data ); - - clip->clip.path = NULL; - clip->clip.flags = audio->flags; - clip->clip.data = data; - clip->clip.size = size; - } - else{ - clip->clip.path = mdl_pstr( &world->meta, clip->file.pstr_path ); - clip->clip.flags = audio->flags; - clip->clip.data = NULL; - clip->clip.size = 0; - } - - audio_clip_load( &clip->clip, world->heap ); - } - } -} - -VG_STATIC void world_load( u32 index, const char *path ) -{ - vg_rand_seed( 9001 ); - - world_instance *world = &world_global.worlds[index]; - world_init_blank( world ); - world->status = k_world_status_loading; - - vg_info( "Loading world: %s\n", path ); - - void *allocator = NULL; - if( index == 0 ) allocator = world_global.heap; - else allocator = world_global.worlds[index-1].heap; - - u32 heap_availible = vg_linear_remaining( allocator ); - u32 min_overhead = sizeof(vg_linear_allocator); - - if( heap_availible < (min_overhead+1024) ){ - vg_fatal_error( "out of memory" ); - } - - u32 size = heap_availible - min_overhead; - void *heap = vg_create_linear_allocator( allocator, size, VG_MEMORY_SYSTEM ); - - world->heap = heap; - mdl_context *meta = &world->meta; - - mdl_open( meta, path, world->heap ); - mdl_load_metadata_block( meta, world->heap ); - mdl_load_animation_block( meta, world->heap ); - mdl_load_mesh_block( meta, world->heap ); - - mdl_load_array( meta, &world->ent_gate, "ent_gate", heap ); - mdl_load_array( meta, &world->ent_camera, "ent_camera", heap ); - mdl_load_array( meta, &world->ent_spawn, "ent_spawn", heap ); - mdl_load_array( meta, &world->ent_light, "ent_light", heap ); - mdl_load_array( meta, &world->ent_route_node,"ent_route_node", heap ); - mdl_load_array( meta, &world->ent_path_index,"ent_path_index", heap ); - mdl_load_array( meta, &world->ent_checkpoint,"ent_checkpoint", heap ); - mdl_load_array( meta, &world->ent_route, "ent_route", heap ); - mdl_load_array( meta, &world->ent_water, "ent_water", heap ); - mdl_load_array( meta, &world->ent_audio_clip,"ent_audio_clip", heap ); - mdl_load_array( meta, &world->ent_audio, "ent_audio", heap ); - mdl_load_array( meta, &world->ent_volume, "ent_volume", heap ); - mdl_load_array( meta, &world->ent_traffic, "ent_traffic", heap ); - mdl_load_array( meta, &world->ent_marker, "ent_marker", heap ); - mdl_load_array( meta, &world->ent_skateshop, "ent_skateshop", heap ); - mdl_load_array( meta, &world->ent_swspreview,"ent_swspreview", heap ); - - mdl_array_ptr infos; - mdl_load_array( meta, &infos, "ent_worldinfo", vg_mem.scratch ); - - if( mdl_arrcount(&infos) ){ - world->info = *((ent_worldinfo *)mdl_arritm(&infos,0)); - } - else{ - world->info.pstr_author = 0; - world->info.pstr_desc = 0; - world->info.pstr_name = 0; - world->info.timezone = 0.0f; - } - - time_t t; - struct tm *tm; - time( &t ); - tm = gmtime( &t ); - world->time = (float)tm->tm_min/20.0f + (world->info.timezone/24.0f); - - /* process resources from pack */ - world_process_resources( world ); - - world_routes_ent_init( world ); - world_entities_init( index ); - world->volume_bh = bh_create( heap, &bh_system_volumes, world, - mdl_arrcount( &world->ent_volume ), 1 ); - - /* main bulk */ - world_generate( world ); - world_routes_generate( world ); - world_post_process( world ); - - mdl_close( meta ); - world->status = k_world_status_loaded; -} +VG_STATIC void world_init_blank( world_instance *world ); +VG_STATIC void world_gen_load_surfaces(void); +VG_STATIC void world_gen_generate_meshes(void); +VG_STATIC void world_gen_compute_light_indices(void); +VG_STATIC void async_world_postprocess_render( void *payload, u32 _size ); #endif /* WORLD_GEN_H */ diff --git a/world_info.h b/world_info.h index 012199e..6b9d620 100644 --- a/world_info.h +++ b/world_info.h @@ -5,7 +5,7 @@ #ifndef WORLD_INFO_H #define WORLD_INFO_H -#include "vg/vg_stdint.h" +#include "world.h" /* Purely an information header, shares common strings across client and * server programs. */ diff --git a/world_load.c b/world_load.c new file mode 100644 index 0000000..493b89d --- /dev/null +++ b/world_load.c @@ -0,0 +1,369 @@ +#ifndef WORLD_LOAD_C +#define WORLD_LOAD_C + +#include "world_load.h" +#include "world_routes.h" +#include "world_gate.h" + +/* + * load the .mdl file located in path (relative to exe), it will load into the + * slot specified by world_loader.world_index + */ +VG_STATIC void world_load_mdl( const char *path ) +{ + vg_rand_seed( 9001 ); + + world_instance *world = world_loading_instance(); + world_init_blank( world ); + world->status = k_world_status_loading; + + vg_info( "Loading world: %s\n", path ); + + void *allocator = NULL; + if( world_loader.world_index == 0 ) allocator = world_static.heap; + else allocator = world_static.worlds[world_loader.world_index-1].heap; + + u32 heap_availible = vg_linear_remaining( allocator ); + u32 min_overhead = sizeof(vg_linear_allocator); + + if( heap_availible < (min_overhead+1024) ){ + vg_fatal_error( "out of memory" ); + } + + u32 size = heap_availible - min_overhead; + void *heap = vg_create_linear_allocator( allocator, size, VG_MEMORY_SYSTEM ); + + world->heap = heap; + mdl_context *meta = &world->meta; + + mdl_open( meta, path, world->heap ); + mdl_load_metadata_block( meta, world->heap ); + mdl_load_animation_block( meta, world->heap ); + mdl_load_mesh_block( meta, world->heap ); + + mdl_load_array( meta, &world->ent_gate, "ent_gate", heap ); + mdl_load_array( meta, &world->ent_camera, "ent_camera", heap ); + mdl_load_array( meta, &world->ent_spawn, "ent_spawn", heap ); + mdl_load_array( meta, &world->ent_light, "ent_light", heap ); + mdl_load_array( meta, &world->ent_route_node,"ent_route_node", heap ); + mdl_load_array( meta, &world->ent_path_index,"ent_path_index", heap ); + mdl_load_array( meta, &world->ent_checkpoint,"ent_checkpoint", heap ); + mdl_load_array( meta, &world->ent_route, "ent_route", heap ); + mdl_load_array( meta, &world->ent_water, "ent_water", heap ); + mdl_load_array( meta, &world->ent_audio_clip,"ent_audio_clip", heap ); + mdl_load_array( meta, &world->ent_audio, "ent_audio", heap ); + mdl_load_array( meta, &world->ent_volume, "ent_volume", heap ); + mdl_load_array( meta, &world->ent_traffic, "ent_traffic", heap ); + mdl_load_array( meta, &world->ent_marker, "ent_marker", heap ); + mdl_load_array( meta, &world->ent_skateshop, "ent_skateshop", heap ); + mdl_load_array( meta, &world->ent_swspreview,"ent_swspreview", heap ); + + mdl_array_ptr infos; + mdl_load_array( meta, &infos, "ent_worldinfo", vg_mem.scratch ); + + if( mdl_arrcount(&infos) ){ + world->info = *((ent_worldinfo *)mdl_arritm(&infos,0)); + } + else{ + world->info.pstr_author = 0; + world->info.pstr_desc = 0; + world->info.pstr_name = 0; + world->info.timezone = 0.0f; + } + + time_t t; + struct tm *tm; + time( &t ); + tm = gmtime( &t ); + world->time = (float)tm->tm_min/20.0f + (world->info.timezone/24.0f); + + /* process resources from pack */ + world_gen_load_surfaces(); + world_gen_routes_ent_init(); + world_gen_entities_init(); + world->volume_bh = bh_create( heap, &bh_system_volumes, world, + mdl_arrcount( &world->ent_volume ), 1 ); + + /* main bulk */ + world_gen_generate_meshes(); + world_gen_routes_generate(); + world_gen_compute_light_indices(); + vg_async_call( async_world_postprocess_render, NULL, 0 ); + + mdl_close( meta ); + world->status = k_world_status_loaded; +} + +/* + * op: k_async_op_world_loading + * k_async_op_world_preloading + * ----------------------------------------------------------------------------- + */ + +static void async_skaterift_world_loaded( void *payload, u32 size ) +{ + skaterift_end_op(); +} + +/* + * Does a complete world switch using the remaining free slots + */ +static void skaterift_world_changer_thread( void *data ) +{ + if( world_loader.location == k_world_load_type_workshop ){ + vg_fatal_error( "Unimplemented\n" ); + } + + char path_buf[4096]; + vg_str path; + vg_strnull( &path, path_buf, 4096 ); + vg_strcat( &path, "maps/" ); + + vg_str folder = path; + vg_strcat( &folder, world_loader.name ); + if( !vg_strgood( &folder ) ) { + vg_error( "Load target too long\n" ); + vg_async_call( workshop_async_any_complete, NULL, 0 ); + return; + } + + char worlds[3][4096]; + u32 i=0; + + DIR *dir = opendir( folder.buffer ); + if( !dir ){ + vg_error( "opendir('%s') failed\n", folder.buffer ); + vg_async_call( async_skaterift_world_loaded, NULL, 0 ); + return; + } + + struct dirent *entry; + while( (entry = readdir(dir)) ){ + if( entry->d_type == DT_REG ){ + if( entry->d_name[0] == '.' ) continue; + + vg_str file = folder; + vg_strcat( &file, "/" ); + vg_strcat( &file, entry->d_name ); + if( !vg_strgood( &file ) ) continue; + + char *ext = vg_strch( &file, '.' ); + if( !ext ) continue; + if( strcmp(ext,".mdl") ) continue; + + if( i == 3 ){ + vg_warn( "There are too many .mdl files in the map folder!(3)\n" ); + break; + } + + strcpy( worlds[i++], file.buffer ); + } + } + closedir(dir); + + if( i == 0 ){ + vg_warn( "There are no .mdl files in the map folder.\n" ); + } + + u32 first_index = 0; + for( u32 j=0; jstatus == k_world_status_unloading ){ + if( world_freeable( inst ) ){ + world_free( inst ); + } + return; + } + } + + vg_info( "worlds cleared, begining load\n" ); + skaterift_shift_op( k_async_op_world_loading ); + + /* finally can start the loader */ + vg_loader_start( skaterift_world_changer_thread, NULL ); +} + +/* places all loaded worlds into unloading state */ +static void skaterift_change_world( const char *world_name ) +{ + vg_info( "switching to %s\n", world_name ); + + if( world_static.active_world != 0 ){ + vg_error( "Cannot change worlds while in non-root world\n" ); + } + else{ + skaterift_begin_op( k_async_op_world_preloading ); + + vg_linear_clear( vg_mem.scratch ); + vg_strncpy( world_name, world_loader.name, + vg_list_size(world_loader.name), k_strncpy_overflow_fatal ); + + vg_info( "unloading old worlds\n" ); + world_unlink_nonlocal( &world_static.worlds[0] ); + + for( u32 i=1; istatus == k_world_status_loaded ){ + inst->status = k_world_status_unloading; + world_fadeout_audio( inst ); + } + } + } +} + +/* console command for the above function */ +static int skaterift_change_world_command( int argc, const char *argv[] ) +{ + if( argc == 1 ) + skaterift_change_world( argv[0] ); + + return 0; +} + + +static world_instance *world_loading_instance(void){ + return &world_static.worlds[ world_loader.world_index ]; +} + +/* + * checks: + * 1. to see if all audios owned by the world have been stopped + * 2. that this is the least significant world + */ +static int world_freeable( world_instance *world ) +{ + if( world->status != k_world_status_unloading ) return 0; + u8 world_id = (world - world_static.worlds) + 1; + + for( u32 i=world_id; iallocated && (ch->world_id == world_id)){ + if( !audio_channel_finished( ch ) ){ + freeable = 0; + break; + } + } + } + audio_unlock(); + return freeable; +} + +/* + * Free all resources for world instance + */ +static void world_free( world_instance *world ) +{ + vg_info( "Free world @%p\n", world ); + + /* free meshes */ + mesh_free( &world->mesh_route_lines ); + mesh_free( &world->mesh_geo ); + mesh_free( &world->mesh_no_collide ); + mesh_free( &world->mesh_water ); + + /* glDeleteBuffers silently ignores 0's and names that do not correspond to + * existing buffer objects. + * */ + glDeleteBuffers( 1, &world->tbo_light_entities ); + glDeleteTextures( 1, &world->tex_light_entities ); + glDeleteTextures( 1, &world->tex_light_cubes ); + + /* delete textures and meshes */ + glDeleteTextures( world->texture_count, world->textures ); + + u32 world_index = world - world_static.worlds; + if( world_index ){ + vg_linear_del( world_static.worlds[world_index-1].heap, + vg_linear_header(world->heap) ); + } + + world->status = k_world_status_unloaded; +} + +/* + * reset the world structure without deallocating persistent buffers + * TODO: Make this a memset(0), and have persistent items live in a static loc + */ +VG_STATIC void world_init_blank( world_instance *world ) +{ + memset( &world->meta, 0, sizeof(mdl_context) ); + + world->textures = NULL; + world->texture_count = 0; + world->surfaces = NULL; + world->surface_count = 0; + + world->geo_bh = NULL; + world->volume_bh = NULL; + world->audio_bh = NULL; + world->rendering_gate = NULL; + + world->water.enabled = 0; + world->time = 0.0; + + /* default lighting conditions + * -------------------------------------------------------------*/ + struct ub_world_lighting *state = &world->ub_lighting; + + state->g_light_preview = 0; + state->g_shadow_samples = 8; + state->g_water_fog = 0.04f; + + v4_zero( state->g_water_plane ); + v4_zero( state->g_depth_bounds ); + + state->g_shadow_length = 9.50f; + state->g_shadow_spread = 0.65f; + + v3_copy( (v3f){0.37f, 0.54f, 0.97f}, state->g_daysky_colour ); + v3_copy( (v3f){0.03f, 0.05f, 0.20f}, state->g_nightsky_colour ); + v3_copy( (v3f){1.00f, 0.32f, 0.01f}, state->g_sunset_colour ); + v3_copy( (v3f){0.13f, 0.17f, 0.35f}, state->g_ambient_colour ); + v3_copy( (v3f){0.25f, 0.17f, 0.51f}, state->g_sunset_ambient ); + v3_copy( (v3f){1.10f, 0.89f, 0.35f}, state->g_sun_colour ); +} + + + +#endif /* WORLD_LOAD_C */ diff --git a/world_load.h b/world_load.h new file mode 100644 index 0000000..74b5911 --- /dev/null +++ b/world_load.h @@ -0,0 +1,32 @@ +#ifndef WORLD_LOAD_H +#define WORLD_LOAD_H + +#include + +#include "world.h" +#include "world_gen.h" +#include "world_routes.h" +#include "world_entity.h" +#include "world_volumes.h" + +#include "ent_skateshop.h" +#include "workshop.h" + +struct{ + char name[64]; + + enum world_load_type{ + k_world_load_type_local, + k_world_load_type_workshop /* unimplemented */ + } + location; + int generate_point_cloud; + u32 world_index; +} +static world_loader; + +static world_instance *world_loading_instance(void); +static void world_free( world_instance *world ); +static int world_freeable( world_instance *world ); + +#endif /* WORLD_LOAD_H */ diff --git a/world_physics.c b/world_physics.c new file mode 100644 index 0000000..96ffa2f --- /dev/null +++ b/world_physics.c @@ -0,0 +1,110 @@ +#ifndef WORLD_PHYSICS_C +#define WORLD_PHYSICS_C + +#include "world.h" +#include "world_physics.h" + +VG_STATIC void ray_world_get_tri( world_instance *world, + ray_hit *hit, v3f tri[3] ) +{ + for( int i=0; i<3; i++ ) + v3_copy( world->scene_geo.arrvertices[ hit->tri[i] ].co, tri[i] ); +} + +VG_STATIC int ray_world( world_instance *world, + v3f pos, v3f dir, ray_hit *hit ) +{ + return scene_raycast( &world->scene_geo, world->geo_bh, pos, dir, hit ); +} + +/* + * Cast a sphere from a to b and see what time it hits + */ +VG_STATIC int spherecast_world( world_instance *world, + v3f pa, v3f pb, float r, float *t, v3f n ) +{ + boxf region; + box_init_inf( region ); + box_addpt( region, pa ); + box_addpt( region, pb ); + + v3_add( (v3f){ r, r, r}, region[1], region[1] ); + v3_add( (v3f){-r,-r,-r}, region[0], region[0] ); + + v3f dir; + v3_sub( pb, pa, dir ); + + v3f dir_inv; + dir_inv[0] = 1.0f/dir[0]; + dir_inv[1] = 1.0f/dir[1]; + dir_inv[2] = 1.0f/dir[2]; + + int hit = -1; + float min_t = 1.0f; + + bh_iter it; + bh_iter_init_box( 0, &it, region ); + i32 idx; + while( bh_next( world->geo_bh, &it, &idx ) ){ + u32 *ptri = &world->scene_geo.arrindices[ idx*3 ]; + v3f tri[3]; + + boxf box; + box_init_inf( box ); + + for( int j=0; j<3; j++ ){ + v3_copy( world->scene_geo.arrvertices[ptri[j]].co, tri[j] ); + box_addpt( box, tri[j] ); + } + + v3_add( (v3f){ r, r, r}, box[1], box[1] ); + v3_add( (v3f){-r,-r,-r}, box[0], box[0] ); + + if( !ray_aabb1( box, pa, dir_inv, 1.0f ) ) + continue; + + float t; + v3f n1; + if( spherecast_triangle( tri, pa, dir, r, &t, n1 ) ){ + if( t < min_t ){ + min_t = t; + hit = idx; + v3_copy( n1, n ); + } + } + } + + *t = min_t; + return hit; +} + +VG_STATIC +struct world_surface *world_tri_index_surface( world_instance *world, + u32 index ) +{ + for( int i=1; isurface_count; i++ ){ + struct world_surface *surf = &world->surfaces[i]; + + if( (index >= surf->sm_geo.vertex_start) && + (index < surf->sm_geo.vertex_start+surf->sm_geo.vertex_count ) ) + { + return surf; + } + } + + return &world->surfaces[0]; +} + +VG_STATIC struct world_surface *world_contact_surface( world_instance *world, + rb_ct *ct ) +{ + return world_tri_index_surface( world, ct->element_id ); +} + +VG_STATIC struct world_surface *ray_hit_surface( world_instance *world, + ray_hit *hit ) +{ + return world_tri_index_surface( world, hit->tri[0] ); +} + +#endif /* WORLD_PHYSICS_C */ diff --git a/world_physics.h b/world_physics.h new file mode 100644 index 0000000..3e7e595 --- /dev/null +++ b/world_physics.h @@ -0,0 +1,24 @@ +#ifndef WORLD_PHYSICS_H +#define WORLD_PHYSICS_H + +#include "world.h" + +VG_STATIC void ray_world_get_tri( world_instance *world, + ray_hit *hit, v3f tri[3] ); + +VG_STATIC int ray_world( world_instance *world, + v3f pos, v3f dir, ray_hit *hit ); + +VG_STATIC int spherecast_world( world_instance *world, + v3f pa, v3f pb, float r, float *t, v3f n ); + +VG_STATIC struct world_surface *world_tri_index_surface( world_instance *world, + u32 index ); + +VG_STATIC struct world_surface *world_contact_surface( world_instance *world, + rb_ct *ct ); + +VG_STATIC struct world_surface *ray_hit_surface( world_instance *world, + ray_hit *hit ); + +#endif /* WORLD_PHYSICS_H */ diff --git a/world_render.c b/world_render.c new file mode 100644 index 0000000..8255d53 --- /dev/null +++ b/world_render.c @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#ifndef WORLD_RENDER_C +#define WORLD_RENDER_C + +#include "world.h" +#include "world_render.h" + +VG_STATIC void async_world_render_init( void *payload, u32 size ) +{ + vg_info( "Allocate uniform buffers\n" ); + for( int i=0; i<4; i++ ){ + world_instance *world = &world_static.worlds[i]; + world->ubo_bind_point = i; + + glGenBuffers( 1, &world->ubo_lighting ); + glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); + glBufferData( GL_UNIFORM_BUFFER, sizeof(struct ub_world_lighting), + NULL, GL_DYNAMIC_DRAW ); + + glBindBufferBase( GL_UNIFORM_BUFFER, i, world->ubo_lighting ); + VG_CHECK_GL_ERR(); + } + + vg_info( "Allocate frame buffers\n" ); + for( int i=0; i<4; i++ ){ + world_instance *world = &world_static.worlds[i]; + struct framebuffer *fb = &world->heightmap; + + fb->display_name = NULL; + fb->link = NULL; + fb->fixed_w = 1024; + fb->fixed_h = 1024; + fb->resolution_div = 0; + + fb->attachments[0].display_name = NULL; + fb->attachments[0].purpose = k_framebuffer_attachment_type_texture; + fb->attachments[0].internalformat = GL_RG16F; + fb->attachments[0].format = GL_RG; + fb->attachments[0].type = GL_FLOAT; + fb->attachments[0].attachment = GL_COLOR_ATTACHMENT0; + + fb->attachments[1].purpose = k_framebuffer_attachment_type_none; + fb->attachments[2].purpose = k_framebuffer_attachment_type_none; + fb->attachments[3].purpose = k_framebuffer_attachment_type_none; + fb->attachments[4].purpose = k_framebuffer_attachment_type_none; + + render_fb_allocate( fb ); + } +} + +VG_STATIC void world_render_init(void) +{ + shader_scene_standard_register(); + shader_scene_standard_alphatest_register(); + shader_scene_vertex_blend_register(); + shader_scene_terrain_register(); + shader_scene_depth_register(); + shader_scene_position_register(); + shader_model_sky_register(); + + vg_info( "Loading default world textures\n" ); + vg_tex2d_load_qoi_async_file( "textures/garbage.qoi", + VG_TEX2D_NEAREST|VG_TEX2D_REPEAT, + &world_render.tex_terrain_noise ); + + vg_async_call( async_world_render_init, NULL, 0 ); +} + +VG_STATIC void world_link_lighting_ub( world_instance *world, GLuint shader ) +{ + GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" ); + glUniformBlockBinding( shader, idx, world->ubo_bind_point ); +} + +VG_STATIC void world_bind_position_texture( world_instance *world, + GLuint shader, GLuint location, + int slot ) +{ + render_fb_bind_texture( &world->heightmap, 0, slot ); + glUniform1i( location, slot ); +} + +VG_STATIC void world_bind_light_array( world_instance *world, + GLuint shader, GLuint location, + int slot ) +{ + glActiveTexture( GL_TEXTURE0 + slot ); + glBindTexture( GL_TEXTURE_BUFFER, world->tex_light_entities ); + glUniform1i( location, slot ); +} + +VG_STATIC void world_bind_light_index( world_instance *world, + GLuint shader, GLuint location, + int slot ) +{ + glActiveTexture( GL_TEXTURE0 + slot ); + glBindTexture( GL_TEXTURE_3D, world->tex_light_cubes ); + glUniform1i( location, slot ); +} + +VG_STATIC void render_world_depth( world_instance *world, camera *cam ); + +/* + * Rendering + */ + +VG_STATIC void bind_terrain_noise(void) +{ + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, world_render.tex_terrain_noise ); +} + +struct world_pass{ + camera *cam; + enum mdl_shader shader; + enum world_geo_type geo_type; + + void (*fn_bind_textures)( world_instance *world, + struct world_surface *mat ); + void (*fn_set_mdl)( m4x3f mdl ); + void (*fn_set_uPvmPrev)( m4x4f pvm ); +}; + +VG_STATIC void world_render_if( world_instance *world, struct world_pass *pass ) +{ + for( int i=0; isurface_count; i++ ){ + struct world_surface *mat = &world->surfaces[i]; + + if( mat->info.shader == pass->shader ){ + mdl_submesh *sm; + + if( pass->geo_type == k_world_geo_type_solid ) + sm = &mat->sm_geo; + else + sm = &mat->sm_no_collide; + + if( !sm->indice_count ) + continue; + + m4x3f mmdl; + m4x3_identity( mmdl ); + pass->fn_set_mdl( mmdl ); + pass->fn_set_uPvmPrev( pass->cam->mtx_prev.pv ); + + pass->fn_bind_textures( world, mat ); + mdl_draw_submesh( sm ); + + for( u32 j=0; jent_traffic ); j++ ){ + ent_traffic *traffic = mdl_arritm( &world->ent_traffic, j ); + + for( u32 k=0; ksubmesh_count; k++ ){ + sm = mdl_arritm( &world->meta.submeshs, + traffic->submesh_start+k ); + + q_m3x3( traffic->transform.q, mmdl ); + v3_copy( traffic->transform.co, mmdl[3] ); + + m4x4f m4mdl; + m4x3_expand( mmdl, m4mdl ); + m4x4_mul( pass->cam->mtx_prev.pv, m4mdl, m4mdl ); + + pass->fn_set_mdl( mmdl ); + pass->fn_set_uPvmPrev( m4mdl ); + + mdl_draw_submesh( sm ); + } + } + } + } +} + +VG_STATIC +void world_render_both_stages( world_instance *world, struct world_pass *pass ) +{ + mesh_bind( &world->mesh_geo ); + pass->geo_type = k_world_geo_type_solid; + world_render_if( world, pass ); + + glDisable( GL_CULL_FACE ); + mesh_bind( &world->mesh_no_collide ); + pass->geo_type = k_world_geo_type_nonsolid; + world_render_if( world, pass ); + glEnable( GL_CULL_FACE ); +} + +VG_STATIC void bindpoint_diffuse_texture1( world_instance *world, + struct world_surface *mat ) + +{ + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] ); +} + +VG_STATIC void render_world_vb( world_instance *world, camera *cam ) +{ + shader_scene_vertex_blend_use(); + shader_scene_vertex_blend_uTexGarbage(0); + shader_scene_vertex_blend_uTexGradients(1); + world_link_lighting_ub( world, _shader_scene_vertex_blend.id ); + world_bind_position_texture( world, _shader_scene_vertex_blend.id, + _uniform_scene_vertex_blend_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_vertex_blend.id, + _uniform_scene_vertex_blend_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_vertex_blend.id, + _uniform_scene_vertex_blend_uLightsIndex, 4 ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, world_render.tex_terrain_noise ); + + shader_scene_vertex_blend_uPv( cam->mtx.pv ); + shader_scene_vertex_blend_uCamera( cam->transform[3] ); + + struct world_pass pass = { + .shader = k_shader_standard_vertex_blend, + .cam = cam, + .fn_bind_textures = bindpoint_diffuse_texture1, + .fn_set_mdl = shader_scene_vertex_blend_uMdl, + .fn_set_uPvmPrev = shader_scene_vertex_blend_uPvmPrev, + }; + + world_render_both_stages( world, &pass ); +} + +VG_STATIC void render_world_standard( world_instance *world, camera *cam ) +{ + shader_scene_standard_use(); + shader_scene_standard_uTexGarbage(0); + shader_scene_standard_uTexMain(1); + shader_scene_standard_uPv( cam->mtx.pv ); + + world_link_lighting_ub( world, _shader_scene_standard.id ); + world_bind_position_texture( world, _shader_scene_standard.id, + _uniform_scene_standard_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_standard.id, + _uniform_scene_standard_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_standard.id, + _uniform_scene_standard_uLightsIndex, 4 ); + + bind_terrain_noise(); + shader_scene_standard_uCamera( cam->transform[3] ); + + struct world_pass pass = { + .shader = k_shader_standard, + .cam = cam, + .fn_bind_textures = bindpoint_diffuse_texture1, + .fn_set_mdl = shader_scene_standard_uMdl, + .fn_set_uPvmPrev = shader_scene_standard_uPvmPrev, + }; + + world_render_both_stages( world, &pass ); +} + +VG_STATIC void render_world_alphatest( world_instance *world, camera *cam ) +{ + shader_scene_standard_alphatest_use(); + shader_scene_standard_alphatest_uTexGarbage(0); + shader_scene_standard_alphatest_uTexMain(1); + shader_scene_standard_alphatest_uPv( cam->mtx.pv ); + + world_link_lighting_ub( world, _shader_scene_standard_alphatest.id ); + world_bind_position_texture( world, _shader_scene_standard_alphatest.id, + _uniform_scene_standard_alphatest_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_standard_alphatest.id, + _uniform_scene_standard_alphatest_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_standard_alphatest.id, + _uniform_scene_standard_alphatest_uLightsIndex, 4 ); + + + bind_terrain_noise(); + + + shader_scene_standard_alphatest_uCamera( cam->transform[3] ); + + glDisable(GL_CULL_FACE); + + struct world_pass pass = { + .shader = k_shader_standard_cutout, + .cam = cam, + .fn_bind_textures = bindpoint_diffuse_texture1, + .fn_set_mdl = shader_scene_standard_alphatest_uMdl, + .fn_set_uPvmPrev = shader_scene_standard_alphatest_uPvmPrev, + }; + + world_render_both_stages( world, &pass ); + + glEnable(GL_CULL_FACE); +} + +VG_STATIC void bindpoint_terrain( world_instance *world, + struct world_surface *mat ) +{ + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] ); + + shader_scene_terrain_uSandColour( mat->info.colour ); + shader_scene_terrain_uBlendOffset( mat->info.colour1 ); +} + +VG_STATIC void render_terrain( world_instance *world, camera *cam ) +{ + shader_scene_terrain_use(); + shader_scene_terrain_uTexGarbage(0); + shader_scene_terrain_uTexGradients(1); + + world_link_lighting_ub( world, _shader_scene_terrain.id ); + world_bind_position_texture( world, _shader_scene_terrain.id, + _uniform_scene_terrain_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_terrain.id, + _uniform_scene_terrain_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_terrain.id, + _uniform_scene_terrain_uLightsIndex, 4 ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, world_render.tex_terrain_noise ); + + shader_scene_terrain_uPv( cam->mtx.pv ); + shader_scene_terrain_uCamera( cam->transform[3] ); + + struct world_pass pass = { + .shader = k_shader_terrain_blend, + .cam = cam, + .fn_bind_textures = bindpoint_terrain, + .fn_set_mdl = shader_scene_terrain_uMdl, + .fn_set_uPvmPrev = shader_scene_terrain_uPvmPrev, + }; + + world_render_both_stages( world, &pass ); +} + +VG_STATIC void render_sky( world_instance *world, camera *cam ) +{ + /* + * Modify matrix to remove clipping and view translation + */ + m4x4f v, + v_prev, + pv, + pv_prev; + + m4x4_copy( cam->mtx.v, v ); + m4x4_copy( cam->mtx_prev.v, v_prev ); + v3_zero( v[3] ); + v3_zero( v_prev[3] ); + + m4x4_copy( cam->mtx.p, pv ); + m4x4_copy( cam->mtx_prev.p, pv_prev ); + m4x4_reset_clipping( pv, cam->farz, cam->nearz ); + m4x4_reset_clipping( pv_prev, cam->farz, cam->nearz ); + + m4x4_mul( pv, v, pv ); + m4x4_mul( pv_prev, v_prev, pv_prev ); + + m4x3f identity_matrix; + m4x3_identity( identity_matrix ); + + /* + * Draw + */ + shader_model_sky_use(); + shader_model_sky_uMdl( identity_matrix ); + shader_model_sky_uPv( pv ); + shader_model_sky_uPvmPrev( pv_prev ); + shader_model_sky_uTexGarbage(0); + world_link_lighting_ub( world, _shader_model_sky.id ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, world_render.tex_terrain_noise ); + + glDepthMask( GL_FALSE ); + glDisable( GL_DEPTH_TEST ); + + mesh_bind( &world_render.skydome ); + mesh_draw( &world_render.skydome ); + + glEnable( GL_DEPTH_TEST ); + glDepthMask( GL_TRUE ); +} + +VG_STATIC void render_world_gates( world_instance *world, camera *cam, + int layer_depth ) +{ + float closest = INFINITY; + struct ent_gate *gate = NULL; + + for( u32 i=0; ient_gate); i++ ){ + ent_gate *gi = mdl_arritm( &world->ent_gate, i ); + + if( gi->type == k_gate_type_unlinked ) + continue; + + float dist = v3_dist2( gi->co[0], cam->transform[3] ); + + vg_line_pt3( gi->co[0], 0.25f, VG__BLUE ); + + if( dist < closest ){ + closest = dist; + gate = gi; + } + } + + if( gate ){ + /* should really be set in fixed update since its used in the physics + * of most systems. too bad! */ + world->rendering_gate = gate; + + if( gate->type == k_gate_type_teleport ){ + render_gate( world, gate, cam, layer_depth ); + } + else if( gate->type == k_gate_type_nonlocel ){ + if( skaterift.async_op != k_async_op_world_loading && + skaterift.async_op != k_async_op_world_preloading ){ + world_instance *dest_world = &world_static.worlds[ gate->target ]; + render_gate( dest_world, gate, cam, layer_depth ); + } + } + else + world->rendering_gate = NULL; + } +} + +VG_STATIC void world_prerender( world_instance *world ) +{ + world->time += vg.time_delta * (1.0/(k_day_length*60.0)); + + struct ub_world_lighting *state = &world->ub_lighting; + + state->g_time = world->time; + state->g_realtime = vg.time; + state->g_debug_indices = k_debug_light_indices; + state->g_light_preview = k_light_preview; + state->g_debug_complexity = k_debug_light_complexity; + state->g_time_of_day = vg_fractf( world->time ); + 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 ); + + state->g_day_phase = state->g_day_phase * 0.5f + 0.5f; + state->g_sunset_phase = powf( state->g_sunset_phase * 0.5f + 0.5f, 6.0f ); + + float a = state->g_time_of_day * VG_PIf * 2.0f; + state->g_sun_dir[0] = sinf( a ); + state->g_sun_dir[1] = cosf( a ); + state->g_sun_dir[2] = 0.2f; + v3_normalize( state->g_sun_dir ); + + world->probabilities[ k_probability_curve_constant ] = 1.0f; + float dp = state->g_day_phase; + + world->probabilities[ k_probability_curve_wildlife_day ] = + (dp*dp*0.8f+state->g_sunset_phase)*0.8f; + world->probabilities[ k_probability_curve_wildlife_night ] = + 1.0f-powf(fabsf((state->g_time_of_day-0.5f)*5.0f),5.0f); + + glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); + glBufferSubData( GL_UNIFORM_BUFFER, 0, + sizeof(struct ub_world_lighting), &world->ub_lighting ); +} + +VG_STATIC void skateshop_render(void); +VG_STATIC void render_world( world_instance *world, camera *cam, + int layer_depth ) +{ + render_sky( world, cam ); + + render_world_routes( world, cam, layer_depth ); + render_world_standard( world, cam ); + render_world_vb( world, cam ); + render_world_alphatest( world, cam ); + render_terrain( world, cam ); + + if( layer_depth == 0 ){ + skateshop_render(); + + /* Render SFD's */ + u32 closest = 0; + float min_dist = INFINITY; + + if( !mdl_arrcount( &world->ent_route ) ) + return; + + for( u32 i=0; ient_route ); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + float dist = v3_dist2( route->board_transform[3], cam->pos ); + + if( dist < min_dist ){ + min_dist = dist; + closest = i; + } + } + + ent_route *route = mdl_arritm( &world->ent_route, closest ); + sfd_render( world, cam, route->board_transform ); + } +} + +VG_STATIC void render_world_depth( world_instance *world, camera *cam ) +{ + m4x3f identity_matrix; + m4x3_identity( identity_matrix ); + + shader_scene_depth_use(); + shader_scene_depth_uCamera( cam->transform[3] ); + shader_scene_depth_uPv( cam->mtx.pv ); + shader_scene_depth_uPvmPrev( cam->mtx_prev.pv ); + shader_scene_depth_uMdl( identity_matrix ); + world_link_lighting_ub( world, _shader_scene_depth.id ); + + mesh_bind( &world->mesh_geo ); + mesh_draw( &world->mesh_geo ); +} + +VG_STATIC void render_world_position( world_instance *world, camera *cam ) +{ + m4x3f identity_matrix; + m4x3_identity( identity_matrix ); + + shader_scene_position_use(); + shader_scene_position_uCamera( cam->transform[3] ); + shader_scene_position_uPv( cam->mtx.pv ); + shader_scene_position_uPvmPrev( cam->mtx_prev.pv ); + shader_scene_position_uMdl( identity_matrix ); + world_link_lighting_ub( world, _shader_scene_position.id ); + + mesh_bind( &world->mesh_geo ); + mesh_draw( &world->mesh_geo ); +} + +#endif diff --git a/world_render.h b/world_render.h index 10e4163..3590cf7 100644 --- a/world_render.h +++ b/world_render.h @@ -8,518 +8,67 @@ #include "camera.h" #include "world.h" -VG_STATIC GLuint tex_terrain_noise; +#include "shaders/scene_standard.h" +#include "shaders/scene_standard_alphatest.h" +#include "shaders/scene_vertex_blend.h" +#include "shaders/scene_terrain.h" +#include "shaders/scene_depth.h" +#include "shaders/scene_position.h" +#include "shaders/model_sky.h" -VG_STATIC void async_world_render_init( void *payload, u32 size ) -{ - vg_info( "Allocate uniform buffers\n" ); - for( int i=0; i<4; i++ ){ - world_instance *world = &world_global.worlds[i]; - world->ubo_bind_point = i; +static const float k_world_light_cube_size = 8.0f; - glGenBuffers( 1, &world->ubo_lighting ); - glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); - glBufferData( GL_UNIFORM_BUFFER, sizeof(struct ub_world_lighting), - NULL, GL_DYNAMIC_DRAW ); +struct world_render{ + GLuint tex_terrain_noise; - glBindBufferBase( GL_UNIFORM_BUFFER, i, world->ubo_lighting ); - VG_CHECK_GL_ERR(); - } - - vg_info( "Allocate frame buffers\n" ); - for( int i=0; i<4; i++ ){ - world_instance *world = &world_global.worlds[i]; - struct framebuffer *fb = &world->heightmap; - - fb->display_name = NULL; - fb->link = NULL; - fb->fixed_w = 1024; - fb->fixed_h = 1024; - fb->resolution_div = 0; - - fb->attachments[0].display_name = NULL; - fb->attachments[0].purpose = k_framebuffer_attachment_type_texture; - fb->attachments[0].internalformat = GL_RG16F; - fb->attachments[0].format = GL_RG; - fb->attachments[0].type = GL_FLOAT; - fb->attachments[0].attachment = GL_COLOR_ATTACHMENT0; + /* rendering */ + glmesh skydome; - fb->attachments[1].purpose = k_framebuffer_attachment_type_none; - fb->attachments[2].purpose = k_framebuffer_attachment_type_none; - fb->attachments[3].purpose = k_framebuffer_attachment_type_none; - fb->attachments[4].purpose = k_framebuffer_attachment_type_none; + double sky_time, sky_rate, sky_target_rate; - render_fb_allocate( fb ); + /* water rendering */ + struct{ + struct framebuffer fbreflect, fbdepth; } -} - -VG_STATIC void world_render_init(void) -{ - vg_info( "Loading default world textures\n" ); + water; + + v3f render_gate_pos; + struct timer_text{ + char text[8]; + m4x3f transform; + ent_gate *gate; + ent_route *route; + } + timer_texts[4]; + u32 timer_text_count; - vg_tex2d_load_qoi_async_file( "textures/garbage.qoi", - VG_TEX2D_NEAREST|VG_TEX2D_REPEAT, - &tex_terrain_noise ); + struct text_particle{ + rb_object obj; + m4x3f mlocal; + ent_glyph *glyph; + v4f colour; - vg_async_call( async_world_render_init, NULL, 0 ); -} - -VG_STATIC void world_link_lighting_ub( world_instance *world, GLuint shader ) -{ - GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" ); - glUniformBlockBinding( shader, idx, world->ubo_bind_point ); + m4x3f mdl; + } + text_particles[6*4]; + u32 text_particle_count; } +static world_render; +VG_STATIC void world_render_init(void); +VG_STATIC void world_link_lighting_ub( world_instance *world, GLuint shader ); VG_STATIC void world_bind_position_texture( world_instance *world, GLuint shader, GLuint location, - int slot ) -{ - render_fb_bind_texture( &world->heightmap, 0, slot ); - glUniform1i( location, slot ); -} - + int slot ); VG_STATIC void world_bind_light_array( world_instance *world, GLuint shader, GLuint location, - int slot ) -{ - glActiveTexture( GL_TEXTURE0 + slot ); - glBindTexture( GL_TEXTURE_BUFFER, world->tex_light_entities ); - glUniform1i( location, slot ); -} - + int slot ); VG_STATIC void world_bind_light_index( world_instance *world, GLuint shader, GLuint location, - int slot ) -{ - glActiveTexture( GL_TEXTURE0 + slot ); - glBindTexture( GL_TEXTURE_3D, world->tex_light_cubes ); - glUniform1i( location, slot ); -} - + int slot ); +VG_STATIC void render_world_position( world_instance *world, camera *cam ); VG_STATIC void render_world_depth( world_instance *world, camera *cam ); - -/* - * Rendering - */ - -VG_STATIC void bind_terrain_noise(void) -{ - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, tex_terrain_noise ); -} - -struct world_pass{ - camera *cam; - enum mdl_shader shader; - enum geo_type geo_type; - - void (*fn_bind_textures)( world_instance *world, - struct world_surface *mat ); - void (*fn_set_mdl)( m4x3f mdl ); - void (*fn_set_uPvmPrev)( m4x4f pvm ); -}; - -VG_STATIC void world_render_if( world_instance *world, struct world_pass *pass ) -{ - for( int i=0; isurface_count; i++ ){ - struct world_surface *mat = &world->surfaces[i]; - - if( mat->info.shader == pass->shader ){ - mdl_submesh *sm; - - if( pass->geo_type == k_geo_type_solid ) - sm = &mat->sm_geo; - else - sm = &mat->sm_no_collide; - - if( !sm->indice_count ) - continue; - - m4x3f mmdl; - m4x3_identity( mmdl ); - pass->fn_set_mdl( mmdl ); - pass->fn_set_uPvmPrev( pass->cam->mtx_prev.pv ); - - pass->fn_bind_textures( world, mat ); - mdl_draw_submesh( sm ); - - for( u32 j=0; jent_traffic ); j++ ){ - ent_traffic *traffic = mdl_arritm( &world->ent_traffic, j ); - - for( u32 k=0; ksubmesh_count; k++ ){ - sm = mdl_arritm( &world->meta.submeshs, - traffic->submesh_start+k ); - - q_m3x3( traffic->transform.q, mmdl ); - v3_copy( traffic->transform.co, mmdl[3] ); - - m4x4f m4mdl; - m4x3_expand( mmdl, m4mdl ); - m4x4_mul( pass->cam->mtx_prev.pv, m4mdl, m4mdl ); - - pass->fn_set_mdl( mmdl ); - pass->fn_set_uPvmPrev( m4mdl ); - - mdl_draw_submesh( sm ); - } - } - } - } -} - -VG_STATIC -void world_render_both_stages( world_instance *world, struct world_pass *pass ) -{ - mesh_bind( &world->mesh_geo ); - pass->geo_type = k_geo_type_solid; - world_render_if( world, pass ); - - glDisable( GL_CULL_FACE ); - mesh_bind( &world->mesh_no_collide ); - pass->geo_type = k_geo_type_nonsolid; - world_render_if( world, pass ); - glEnable( GL_CULL_FACE ); -} - -VG_STATIC void bindpoint_diffuse_texture1( world_instance *world, - struct world_surface *mat ) - -{ - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] ); -} - -VG_STATIC void render_world_vb( world_instance *world, camera *cam ) -{ - shader_scene_vertex_blend_use(); - shader_scene_vertex_blend_uTexGarbage(0); - shader_scene_vertex_blend_uTexGradients(1); - world_link_lighting_ub( world, _shader_scene_vertex_blend.id ); - world_bind_position_texture( world, _shader_scene_vertex_blend.id, - _uniform_scene_vertex_blend_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_vertex_blend.id, - _uniform_scene_vertex_blend_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_vertex_blend.id, - _uniform_scene_vertex_blend_uLightsIndex, 4 ); - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, tex_terrain_noise ); - - shader_scene_vertex_blend_uPv( cam->mtx.pv ); - shader_scene_vertex_blend_uCamera( cam->transform[3] ); - - struct world_pass pass = { - .shader = k_shader_standard_vertex_blend, - .cam = cam, - .fn_bind_textures = bindpoint_diffuse_texture1, - .fn_set_mdl = shader_scene_vertex_blend_uMdl, - .fn_set_uPvmPrev = shader_scene_vertex_blend_uPvmPrev, - }; - - world_render_both_stages( world, &pass ); -} - -VG_STATIC void render_world_standard( world_instance *world, camera *cam ) -{ - shader_scene_standard_use(); - shader_scene_standard_uTexGarbage(0); - shader_scene_standard_uTexMain(1); - shader_scene_standard_uPv( cam->mtx.pv ); - - world_link_lighting_ub( world, _shader_scene_standard.id ); - world_bind_position_texture( world, _shader_scene_standard.id, - _uniform_scene_standard_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_standard.id, - _uniform_scene_standard_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_standard.id, - _uniform_scene_standard_uLightsIndex, 4 ); - - bind_terrain_noise(); - shader_scene_standard_uCamera( cam->transform[3] ); - - struct world_pass pass = { - .shader = k_shader_standard, - .cam = cam, - .fn_bind_textures = bindpoint_diffuse_texture1, - .fn_set_mdl = shader_scene_standard_uMdl, - .fn_set_uPvmPrev = shader_scene_standard_uPvmPrev, - }; - - world_render_both_stages( world, &pass ); -} - -VG_STATIC void render_world_alphatest( world_instance *world, camera *cam ) -{ - shader_scene_standard_alphatest_use(); - shader_scene_standard_alphatest_uTexGarbage(0); - shader_scene_standard_alphatest_uTexMain(1); - shader_scene_standard_alphatest_uPv( cam->mtx.pv ); - - world_link_lighting_ub( world, _shader_scene_standard_alphatest.id ); - world_bind_position_texture( world, _shader_scene_standard_alphatest.id, - _uniform_scene_standard_alphatest_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_standard_alphatest.id, - _uniform_scene_standard_alphatest_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_standard_alphatest.id, - _uniform_scene_standard_alphatest_uLightsIndex, 4 ); - - - bind_terrain_noise(); - - - shader_scene_standard_alphatest_uCamera( cam->transform[3] ); - - glDisable(GL_CULL_FACE); - - struct world_pass pass = { - .shader = k_shader_standard_cutout, - .cam = cam, - .fn_bind_textures = bindpoint_diffuse_texture1, - .fn_set_mdl = shader_scene_standard_alphatest_uMdl, - .fn_set_uPvmPrev = shader_scene_standard_alphatest_uPvmPrev, - }; - - world_render_both_stages( world, &pass ); - - glEnable(GL_CULL_FACE); -} - -VG_STATIC void bindpoint_terrain( world_instance *world, - struct world_surface *mat ) -{ - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] ); - - shader_scene_terrain_uSandColour( mat->info.colour ); - shader_scene_terrain_uBlendOffset( mat->info.colour1 ); -} - -VG_STATIC void render_terrain( world_instance *world, camera *cam ) -{ - shader_scene_terrain_use(); - shader_scene_terrain_uTexGarbage(0); - shader_scene_terrain_uTexGradients(1); - - world_link_lighting_ub( world, _shader_scene_terrain.id ); - world_bind_position_texture( world, _shader_scene_terrain.id, - _uniform_scene_terrain_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_terrain.id, - _uniform_scene_terrain_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_terrain.id, - _uniform_scene_terrain_uLightsIndex, 4 ); - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, tex_terrain_noise ); - - shader_scene_terrain_uPv( cam->mtx.pv ); - shader_scene_terrain_uCamera( cam->transform[3] ); - - struct world_pass pass = { - .shader = k_shader_terrain_blend, - .cam = cam, - .fn_bind_textures = bindpoint_terrain, - .fn_set_mdl = shader_scene_terrain_uMdl, - .fn_set_uPvmPrev = shader_scene_terrain_uPvmPrev, - }; - - world_render_both_stages( world, &pass ); -} - -VG_STATIC void render_sky( world_instance *world, camera *cam ) -{ - /* - * Modify matrix to remove clipping and view translation - */ - m4x4f v, - v_prev, - pv, - pv_prev; - - m4x4_copy( cam->mtx.v, v ); - m4x4_copy( cam->mtx_prev.v, v_prev ); - v3_zero( v[3] ); - v3_zero( v_prev[3] ); - - m4x4_copy( cam->mtx.p, pv ); - m4x4_copy( cam->mtx_prev.p, pv_prev ); - m4x4_reset_clipping( pv, cam->farz, cam->nearz ); - m4x4_reset_clipping( pv_prev, cam->farz, cam->nearz ); - - m4x4_mul( pv, v, pv ); - m4x4_mul( pv_prev, v_prev, pv_prev ); - - m4x3f identity_matrix; - m4x3_identity( identity_matrix ); - - /* - * Draw - */ - shader_model_sky_use(); - shader_model_sky_uMdl( identity_matrix ); - shader_model_sky_uPv( pv ); - shader_model_sky_uPvmPrev( pv_prev ); - shader_model_sky_uTexGarbage(0); - world_link_lighting_ub( world, _shader_model_sky.id ); - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, tex_terrain_noise ); - - glDepthMask( GL_FALSE ); - glDisable( GL_DEPTH_TEST ); - - mesh_bind( &world_global.skydome ); - mesh_draw( &world_global.skydome ); - - glEnable( GL_DEPTH_TEST ); - glDepthMask( GL_TRUE ); -} - -VG_STATIC void render_world_gates( world_instance *world, camera *cam, - int layer_depth ) -{ - float closest = INFINITY; - struct ent_gate *gate = NULL; - - for( u32 i=0; ient_gate); i++ ){ - ent_gate *gi = mdl_arritm( &world->ent_gate, i ); - - if( gi->type == k_gate_type_unlinked ) - continue; - - float dist = v3_dist2( gi->co[0], cam->transform[3] ); - - vg_line_pt3( gi->co[0], 0.25f, VG__BLUE ); - - if( dist < closest ){ - closest = dist; - gate = gi; - } - } - - if( gate ){ - /* should really be set in fixed update since its used in the physics - * of most systems. too bad! */ - world->rendering_gate = gate; - - if( gate->type == k_gate_type_teleport ){ - render_gate( world, gate, cam, layer_depth ); - } - else if( gate->type == k_gate_type_nonlocel ){ - if( skaterift.async_op != k_async_op_world_loading && - skaterift.async_op != k_async_op_world_preloading ){ - world_instance *dest_world = &world_global.worlds[ gate->target ]; - render_gate( dest_world, gate, cam, layer_depth ); - } - } - else - world->rendering_gate = NULL; - } -} - -VG_STATIC void world_prerender( world_instance *world ) -{ - world->time += vg.time_delta * (1.0/(k_day_length*60.0)); - - struct ub_world_lighting *state = &world->ub_lighting; - - state->g_time = world->time; - state->g_realtime = vg.time; - state->g_debug_indices = k_debug_light_indices; - state->g_light_preview = k_light_preview; - state->g_debug_complexity = k_debug_light_complexity; - state->g_time_of_day = vg_fractf( world->time ); - 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 ); - - state->g_day_phase = state->g_day_phase * 0.5f + 0.5f; - state->g_sunset_phase = powf( state->g_sunset_phase * 0.5f + 0.5f, 6.0f ); - - float a = state->g_time_of_day * VG_PIf * 2.0f; - state->g_sun_dir[0] = sinf( a ); - state->g_sun_dir[1] = cosf( a ); - state->g_sun_dir[2] = 0.2f; - v3_normalize( state->g_sun_dir ); - - world->probabilities[ k_probability_curve_constant ] = 1.0f; - float dp = state->g_day_phase; - - world->probabilities[ k_probability_curve_wildlife_day ] = - (dp*dp*0.8f+state->g_sunset_phase)*0.8f; - world->probabilities[ k_probability_curve_wildlife_night ] = - 1.0f-powf(fabsf((state->g_time_of_day-0.5f)*5.0f),5.0f); - - glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting ); - glBufferSubData( GL_UNIFORM_BUFFER, 0, - sizeof(struct ub_world_lighting), &world->ub_lighting ); -} - -VG_STATIC void skateshop_render(void); VG_STATIC void render_world( world_instance *world, camera *cam, - int layer_depth ) -{ - render_sky( world, cam ); - - render_world_routes( world, cam, layer_depth ); - render_world_standard( world, cam ); - render_world_vb( world, cam ); - render_world_alphatest( world, cam ); - render_terrain( world, cam ); - - if( layer_depth == 0 ){ - skateshop_render(); - - /* Render SFD's */ - u32 closest = 0; - float min_dist = INFINITY; - - if( !mdl_arrcount( &world->ent_route ) ) - return; - - for( u32 i=0; ient_route ); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - float dist = v3_dist2( route->board_transform[3], cam->pos ); - - if( dist < min_dist ){ - min_dist = dist; - closest = i; - } - } - - ent_route *route = mdl_arritm( &world->ent_route, closest ); - sfd_render( world, cam, route->board_transform ); - } -} - -VG_STATIC void render_world_depth( world_instance *world, camera *cam ) -{ - m4x3f identity_matrix; - m4x3_identity( identity_matrix ); - - shader_scene_depth_use(); - shader_scene_depth_uCamera( cam->transform[3] ); - shader_scene_depth_uPv( cam->mtx.pv ); - shader_scene_depth_uPvmPrev( cam->mtx_prev.pv ); - shader_scene_depth_uMdl( identity_matrix ); - world_link_lighting_ub( world, _shader_scene_depth.id ); - - mesh_bind( &world->mesh_geo ); - mesh_draw( &world->mesh_geo ); -} - -VG_STATIC void render_world_position( world_instance *world, camera *cam ) -{ - m4x3f identity_matrix; - m4x3_identity( identity_matrix ); - - shader_scene_position_use(); - shader_scene_position_uCamera( cam->transform[3] ); - shader_scene_position_uPv( cam->mtx.pv ); - shader_scene_position_uPvmPrev( cam->mtx_prev.pv ); - shader_scene_position_uMdl( identity_matrix ); - world_link_lighting_ub( world, _shader_scene_position.id ); - - mesh_bind( &world->mesh_geo ); - mesh_draw( &world->mesh_geo ); -} + int layer_depth ); #endif /* WORLD_RENDER_H */ diff --git a/world_routes.c b/world_routes.c new file mode 100644 index 0000000..a66f93f --- /dev/null +++ b/world_routes.c @@ -0,0 +1,1261 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#ifndef ROUTES_C +#define ROUTES_C + +#include +#include "world_routes.h" +#include "world_gate.h" +#include "world_load.h" +#include "highscores.h" + +#include "font.h" +#include "pointcloud.h" +#include "gui.h" + +#include "shaders/scene_route.h" +#include "shaders/routeui.h" + + +VG_STATIC +void world_routes_local_set_record( world_instance *world, ent_route *route, + double lap_time ) +{ + vg_success( " NEW LAP TIME: %f\n", lap_time ); + + if( route->official_track_id != 0xffffffff ){ + double time_centiseconds = lap_time * 100.0; + if( time_centiseconds > (float)0xfffe ) /* skill issue */ + return; + + highscore_record temp; + temp.trackid = route->official_track_id; + temp.datetime = time(NULL); + temp.playerid = 0; + temp.points = 0; + temp.time = time_centiseconds; + +#if 0 + highscores_push_record( &temp ); +#endif + + struct track_info *ti = &track_infos[ route->official_track_id ]; + ti->push = 1; + + if( ti->achievement_id ){ +#if 0 + steam_set_achievement( ti->achievement_id ); + steam_store_achievements(); +#endif + } + } + else{ + vg_warn( "There is no associated track for this record...\n" ); + } +} + + +VG_STATIC void world_routes_clear( world_instance *world ) +{ + for( u32 i=0; ient_route ); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + route->active_checkpoint = 0xffff; + } + + for( u32 i=0; ient_gate ); i++ ){ + ent_gate *rg = mdl_arritm( &world->ent_gate, i ); + rg->timing_version = 0; + rg->timing_time = 0.0; + } + + world_static.current_run_version += 4; + world_static.last_use = 0.0; +} + +VG_STATIC void world_routes_time_lap( world_instance *world, ent_route *route ) +{ + vg_info( "------- time lap %s -------\n", + mdl_pstr(&world->meta,route->pstr_name) ); + + double start_time = 0.0; + u32 last_version=0; + + u32 valid_count=0; + + for( u32 i=0; icheckpoints_count; i++ ){ + u32 cpid = (i+route->active_checkpoint) % route->checkpoints_count; + cpid += route->checkpoints_start; + + ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, cpid ); + ent_gate *rg = mdl_arritm( &world->ent_gate, cp->gate_index ); + rg = mdl_arritm( &world->ent_gate, rg->target ); + + if( i == 1 ){ + route->timing_base = rg->timing_time; + } + + if( i == 0 ) + start_time = rg->timing_time; + else{ + if( last_version+1 == rg->timing_version ) valid_count ++; + else valid_count = 0; + } + + last_version = rg->timing_version; + vg_info( "%u %f\n", rg->timing_version, rg->timing_time ); + } + + if( world_static.current_run_version == last_version+1 ){ + valid_count ++; + + if( route->checkpoints_count == 1 ){ + route->timing_base = world_static.time; + } + } + else valid_count = 0; + + vg_info( "%u %f\n", world_static.current_run_version, world_static.time ); + + if( valid_count==route->checkpoints_count ){ + double lap_time = world_static.time - start_time; + world_routes_local_set_record( world, route, lap_time ); + } + + route->valid_checkpoints = valid_count+1; + + vg_info( "valid: %u\n", valid_count ); + vg_info( "----------------------------\n" ); +} + +/* + * When going through a gate this is called for bookkeeping purposes + */ +VG_STATIC void world_routes_activate_entry_gate( world_instance *world, + ent_gate *rg ) +{ + world_static.last_use = world_static.time; + + /* disable all routes and leave the world */ + if( rg->type == k_gate_type_nonlocel ){ + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + route->active_checkpoint = 0xffff; + } + return; + } + + ent_gate *dest = mdl_arritm( &world->ent_gate, rg->target ); + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + + u32 active_prev = route->active_checkpoint; + route->active_checkpoint = 0xffff; + + for( u32 j=0; j<4; j++ ){ + if( dest->routes[j] == i ){ + for( u32 k=0; kcheckpoints_count; k++ ){ + ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, + route->checkpoints_start+k ); + + ent_gate *gk = mdl_arritm( &world->ent_gate, cp->gate_index ); + gk = mdl_arritm( &world->ent_gate, gk->target ); + if( gk == dest ){ + route->active_checkpoint = k; + world_routes_time_lap( world, route ); + break; + } + } + break; + } + } + } + + dest->timing_version = world_static.current_run_version; + dest->timing_time = world_static.time; + + world_static.current_run_version ++; +} + +/* draw lines along the paths */ +VG_STATIC void world_routes_debug( world_instance *world ) +{ + for( u32 i=0; ient_route_node); i++ ){ + ent_route_node *rn = mdl_arritm(&world->ent_route_node,i); + vg_line_pt3( rn->co, 0.25f, VG__WHITE ); + } + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm(&world->ent_route, i); + + u32 colours[] = { 0xfff58142, 0xff42cbf5, 0xff42f56c, 0xfff542b3, + 0xff5442f5 }; + + u32 cc = 0xffcccccc; + if( route->active_checkpoint != 0xffff ){ + cc = colours[i%vg_list_size(colours)]; + } + + for( int i=0; icheckpoints_count; i++ ){ + int i0 = route->checkpoints_start+i, + i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); + + ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), + *c1 = mdl_arritm(&world->ent_checkpoint, i1); + + ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); + ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index ); + + v3f p0, p1; + v3_copy( start_gate->co[1], p0 ); + + for( int j=0; jpath_count; j ++ ){ + ent_path_index *index = mdl_arritm( &world->ent_path_index, + c0->path_start+j ); + + ent_route_node *rn = mdl_arritm( &world->ent_route_node, + index->index ); + + v3_copy( rn->co, p1 ); + vg_line( p0, p1, cc ); + v3_copy( p1, p0 ); + } + + v3_copy( end_gate->co[0], p1 ); + vg_line( p0, p1, cc ); + } + } +} + +VG_STATIC +void world_routes_pointcloud_spot( world_instance *world, + pointcloud_buffer *pcbuf, + v3f co, f32 radius, u32 samples, v4f colour ) +{ + v3f inv_ext; + v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); + v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); + + for( u32 j=0; jcount >= pcbuf->max ) + return; + + pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; + + v3f sample, jitter, point; + vg_rand_sphere( jitter ); + v3_muladds( co, jitter, radius, sample ); + + if( bh_closest_point( world->geo_bh, sample, point, radius*1.5f ) == -1 ){ + v3_copy( sample, point ); + } + + v3f pos; + v3_sub( point, pcbuf->boundary[0], pos ); + v3_mul( pos, inv_ext, pos ); + + for( u32 i=0; i<3; i++ ){ + vert->pos[i] = (pos[i]-0.5f) * 32767.0f; + } + + float dist = 1.0f-(v3_length(jitter)); + + for( u32 i=0; i<4; i++ ){ + vert->colour[i] = colour[i] * 255.0f * dist*dist; + } + } +} + +VG_STATIC +void world_routes_place_curve( world_instance *world, ent_route *route, + v4f h[3], v3f n0, v3f n2, scene_context *scene, + pointcloud_buffer *pcbuf ) +{ + float t; + v3f p, pd; + int last_valid=0; + + float total_length = 0.0f, + travel_length = 0.0; + + v3f last; + v3_copy( h[0], last ); + for( int it=0; it<128; it ++ ){ + t = (float)(it+1) * (1.0f/128.0f); + eval_bezier3( h[0], h[1], h[2], t, p ); + total_length += v3_dist( p, last ); + v3_copy( p, last ); + } + + float patch_size = 4.0f, + patch_count = ceilf( total_length / patch_size ); + + t = 0.0f; + v3_copy( h[0], last ); + + for( int it=0; it<128; it ++ ){ + float const k_sample_dist = 0.0025f, + k_line_width = 1.5f; + + eval_bezier3( h[0], h[1], h[2], t, p ); + eval_bezier3( h[0], h[1], h[2], t+k_sample_dist, pd ); + + travel_length += v3_dist( p, last ); + + float mod = k_sample_dist / v3_dist( p, pd ); + + v3f v0,up, right; + + v3_muls( n0, -(1.0f-t), up ); + v3_muladds( up, n2, -t, up ); + v3_normalize( up ); + + v3_sub( pd,p,v0 ); + v3_cross( up, v0, right ); + v3_normalize( right ); + + float cur_x = (1.0f-t)*h[0][3] + t*h[2][3]; + + v3f sc, sa, sb, down; + v3_muladds( p, right, cur_x * k_line_width, sc ); + v3_muladds( sc, up, 1.5f, sc ); + v3_muladds( sc, right, k_line_width*0.95f, sa ); + v3_muladds( sc, right, 0.0f, sb ); + v3_muls( up, -1.0f, down ); + + ray_hit ha, hb; + ha.dist = 8.0f; + hb.dist = 8.0f; + + int resa = ray_world( world, sa, down, &ha ), + resb = ray_world( world, sb, down, &hb ); + + if( pcbuf && resa ){ + world_routes_pointcloud_spot( world, pcbuf, ha.pos, + 12.0f, 10, route->colour ); + } + + if( resa && resb ){ + struct world_surface *surfa = ray_hit_surface( world, &ha ), + *surfb = ray_hit_surface( world, &hb ); + + if( (surfa->info.flags & k_material_flag_skate_target) && + (surfb->info.flags & k_material_flag_skate_target) ) + { + scene_vert va, vb; + + float gap = vg_fractf(cur_x*0.5f)*0.02f; + + v3_muladds( ha.pos, up, 0.06f+gap, va.co ); + v3_muladds( hb.pos, up, 0.06f+gap, vb.co ); + + scene_vert_pack_norm( &va, up ); + scene_vert_pack_norm( &vb, up ); + + float t1 = (travel_length / total_length) * patch_count; + va.uv[0] = t1; + va.uv[1] = 0.0f; + vb.uv[0] = t1; + vb.uv[1] = 1.0f; + + scene_push_vert( scene, &va ); + scene_push_vert( scene, &vb ); + + if( last_valid ){ + /* Connect them with triangles */ + scene_push_tri( scene, (u32[3]){ + last_valid+0-2, last_valid+1-2, last_valid+2-2} ); + scene_push_tri( scene, (u32[3]){ + last_valid+1-2, last_valid+3-2, last_valid+2-2} ); + } + + last_valid = scene->vertex_count; + } + else + last_valid = 0; + } + else + last_valid = 0; + + if( t == 1.0f ) + return; + + t += 1.0f*mod; + if( t > 1.0f ) + t = 1.0f; + + v3_copy( p, last ); + } +} + +VG_STATIC void world_routes_gen_meshes( world_instance *world, u32 route_id, + scene_context *sc, + pointcloud_buffer *pcbuf ) +{ + ent_route *route = mdl_arritm( &world->ent_route, route_id ); + u8 colour[4]; + colour[0] = route->colour[0] * 255.0f; + colour[1] = route->colour[1] * 255.0f; + colour[2] = route->colour[2] * 255.0f; + colour[3] = route->colour[3] * 255.0f; + + u32 last_valid = 0; + + for( int i=0; icheckpoints_count; i++ ){ + int i0 = route->checkpoints_start+i, + i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); + + ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), + *c1 = mdl_arritm(&world->ent_checkpoint, i1); + + ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); + start_gate = mdl_arritm( &world->ent_gate, start_gate->target ); + + ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index ), + *collector = mdl_arritm( &world->ent_gate, end_gate->target ); + + v4f p[3]; + + v3_add( (v3f){0.0f,0.1f,0.0f}, start_gate->co[0], p[0] ); + p[0][3] = start_gate->ref_count; + p[0][3] -= (float)start_gate->route_count * 0.5f; + start_gate->ref_count ++; + + if( !c0->path_count ) + continue; + + /* this is so that we get nice flow through the gates */ + v3f temp_alignments[2]; + ent_gate *both[] = { start_gate, end_gate }; + + for( int j=0; j<2; j++ ){ + int pi = c0->path_start + ((j==1)? c0->path_count-1: 0); + + ent_path_index *index = mdl_arritm( &world->ent_path_index, pi ); + ent_route_node *rn = mdl_arritm( &world->ent_route_node, + index->index ); + v3f v0; + v3_sub( rn->co, both[j]->co[0], v0 ); + float d = v3_dot( v0, both[j]->to_world[2] ); + + v3_muladds( both[j]->co[0], both[j]->to_world[2], d, + temp_alignments[j] ); + v3_add( (v3f){0.0f,0.1f,0.0f}, temp_alignments[j], temp_alignments[j]); + } + + + for( int j=0; jpath_count; j ++ ){ + ent_path_index *index = mdl_arritm( &world->ent_path_index, + c0->path_start+j ); + ent_route_node *rn = mdl_arritm( &world->ent_route_node, + index->index ); + if( j==0 || j==c0->path_count-1 ) + if( j == 0 ) + v3_copy( temp_alignments[0], p[1] ); + else + v3_copy( temp_alignments[1], p[1] ); + else + v3_copy( rn->co, p[1] ); + + p[1][3] = rn->ref_count; + p[1][3] -= (float)rn->ref_total * 0.5f; + rn->ref_count ++; + + if( j+1 < c0->path_count ){ + index = mdl_arritm( &world->ent_path_index, + c0->path_start+j+1 ); + rn = mdl_arritm( &world->ent_route_node, index->index ); + + if( j+1 == c0->path_count-1 ) + v3_lerp( p[1], temp_alignments[1], 0.5f, p[2] ); + else + v3_lerp( p[1], rn->co, 0.5f, p[2] ); + + p[2][3] = rn->ref_count; + p[2][3] -= (float)rn->ref_total * 0.5f; + } + else{ + v3_copy( end_gate->co[0], p[2] ); + v3_add( (v3f){0.0f,0.1f,0.0f}, p[2], p[2] ); + p[2][3] = collector->ref_count; + + if( i == route->checkpoints_count-1) + p[2][3] -= 1.0f; + + p[2][3] -= (float)collector->route_count * 0.5f; + //collector->ref_count ++; + } + + /* p0,p1,p2 bezier patch is complete + * --------------------------------------*/ + v3f surf0, surf2, n0, n2; + + if( bh_closest_point( world->geo_bh, p[0], surf0, 5.0f ) == -1 ) + v3_add( (v3f){0.0f,-0.1f,0.0f}, p[0], surf0 ); + + if( bh_closest_point( world->geo_bh, p[2], surf2, 5.0f ) == -1 ) + v3_add( (v3f){0.0f,-0.1f,0.0f}, p[2], surf2 ); + + v3_sub( surf0, p[0], n0 ); + v3_sub( surf2, p[2], n2 ); + v3_normalize( n0 ); + v3_normalize( n2 ); + + world_routes_place_curve( world, route, p, n0, n2, sc, pcbuf ); + + /* --- */ + v4_copy( p[2], p[0] ); + } + } + + scene_copy_slice( sc, &route->sm ); +} + +VG_STATIC +struct world_surface *world_tri_index_surface( world_instance *world, + u32 index ); + +VG_STATIC f64 world_routes_scatter_surface_points( world_instance *world, + pointcloud_buffer *pcbuf, + f32 rate ) +{ + static f32 densities[] = { + [k_surface_prop_concrete] = 2.0f, + [k_surface_prop_grass] = 0.8f, + [k_surface_prop_metal] = 1.0f, + [k_surface_prop_wood] = 2.5f, + [k_surface_prop_tiles] = 4.0f + }; + + /* calculate total area */ + f64 total_area = 0.0f; + for( u32 i=0; iscene_geo.indice_count/3; i++ ){ + u32 *tri = &world->scene_geo.arrindices[i*3]; + struct world_surface *surf = world_tri_index_surface( world, tri[0] ); + + if( surf->info.shader == k_shader_boundary || + surf->info.shader == k_shader_invisible ) continue; + + if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue; + + scene_vert *va = &world->scene_geo.arrvertices[tri[0]], + *vb = &world->scene_geo.arrvertices[tri[1]], + *vc = &world->scene_geo.arrvertices[tri[2]]; + + v3f v0, v1, vn; + v3_sub( vb->co, va->co, v0 ); + v3_sub( vc->co, va->co, v1 ); + v3_cross( v0, v1, vn ); + if( vn[1] < 0.0f ) continue; + + f32 density = 1.0f; + if( surf->info.surface_prop < vg_list_size(densities) ) + density = densities[surf->info.surface_prop]; + total_area += v3_length(vn)*0.5f*density; + } + + f32 accum = 0.0f; + + u8 colour[] = { 80,80,80,255 }; + v3f light_dir = {0.3f,0.8f,0.1f}; + v3_normalize( light_dir ); + + v3f inv_ext; + v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); + v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); + + for( u32 i=0; iscene_geo.indice_count/3; i++ ){ + u32 *tri = &world->scene_geo.arrindices[i*3]; + struct world_surface *surf = world_tri_index_surface( world, tri[0] ); + + if( surf->info.shader == k_shader_boundary || + surf->info.shader == k_shader_invisible ) continue; + + if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue; + + scene_vert *va = &world->scene_geo.arrvertices[tri[0]], + *vb = &world->scene_geo.arrvertices[tri[1]], + *vc = &world->scene_geo.arrvertices[tri[2]]; + + v3f v0, v1, vn; + v3_sub( vb->co, va->co, v0 ); + v3_sub( vc->co, va->co, v1 ); + v3_cross( v0, v1, vn ); + if( vn[1] < 0.0f ) continue; + + f32 density = 1.0f; + if( surf->info.surface_prop < vg_list_size(densities) ) + density = densities[surf->info.surface_prop]; + + f32 area = v3_length(vn)*0.5f*density; + accum += area; + + v3_normalize( vn ); + + while( accum > rate ){ + accum -= rate; + + if( pcbuf->count >= pcbuf->max ) return total_area; + + v2f co = { vg_randf64(), vg_randf64() }; + if( v2_length2(co) > 0.5f ){ + co[0] = 1.0f-co[0]; + co[1] = 1.0f-co[1]; + } + + v3f pt; + v3_muls( v0, co[0], pt ); + v3_muladds( pt, v1, co[1], pt ); + v3_add( va->co, pt, pt ); + + if( pt[1] < world->water.height ) continue; + pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; + + v3f pos; + v3_sub( pt, pcbuf->boundary[0], pos ); + v3_mul( pos, inv_ext, pos ); + + for( u32 i=0; i<3; i++ ){ + vert->pos[i] = (pos[i]-0.5f) * 32767.0f; + } + + static v4f colours[] = { + [k_surface_prop_concrete] = { 0.13, 0.15, 0.17, 1.0 }, + [k_surface_prop_grass] = { 0.07, 0.1, 0.14, 1.0 }, + [k_surface_prop_metal] = { 0.15, 0.19, 0.22, 1.0 }, + [k_surface_prop_wood] = { 0.1, 0.13, 0.17, 1.0 }, + [k_surface_prop_tiles] = { 0.05, 0.06, 0.07, 1.0 }, + }; + + v4f col = {0.0f,0.0f,0.0f,0.0f}; + if( surf->info.surface_prop < vg_list_size(colours) ){ + v4_copy( colours[surf->info.surface_prop], col ); + } + + f32 brightness = v3_dot(vn,light_dir)*0.5f+0.5f; + v3_muls( col, brightness, col ); + + for( u32 j=0; j<4; j++ ){ + vert->colour[j] = col[j] * 255.0f; + } + } + } + + return total_area; +} + +VG_STATIC void world_routes_surface_grid( world_instance *world, + pointcloud_buffer *pcbuf ) +{ + i32 const k_gridlines = 32, + k_gridres = 255; + + v3f inv_ext; + v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); + v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); + v4f colour = {0.2f,0.2f,0.2f,1.0f}; + v3f dir = {0.0f,-1.0f,0.0f}; + + for( u32 k=0; k<2; k++ ){ + u32 a = k*2, + b = (k^0x1)*2; + + for( i32 x=0; x<=k_gridlines; x++ ){ + f32 t = (float)x / (float)k_gridlines, + px = vg_lerpf( pcbuf->boundary[0][a], pcbuf->boundary[1][a], t ); + + for( i32 z=0; z<=k_gridres; z++ ){ + f32 tz = (float)z / (float)k_gridres, + pz = vg_lerpf(pcbuf->boundary[0][b],pcbuf->boundary[1][b], tz); + + v3f ro, hit; + ro[a] = px; + ro[1] = 1000.0f; + ro[b] = pz; + + bh_iter it; + bh_iter_init_ray( 0, &it, ro, dir, INFINITY ); + i32 idx; + + while( bh_next( world->geo_bh, &it, &idx ) ){ + u32 *tri = &world->scene_geo.arrindices[ idx*3 ]; + v3f vs[3]; + + for( u32 i=0; i<3; i++ ){ + v3_copy( world->scene_geo.arrvertices[tri[i]].co, vs[i] ); + } + + f32 t; + if( ray_tri( vs, ro, dir, &t ) ){ + v3_muladds( ro, dir, t, hit ); + struct world_surface *m1 = + world_tri_index_surface( world, tri[0] ); + + if( !(m1->info.flags & k_material_flag_preview_visibile) ) + continue; + + if( world->water.enabled ) + if( hit[1] < world->water.height ) + continue; + + if( pcbuf->count >= pcbuf->max ) return; + + pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; + + v3f co; + v3_sub( hit, pcbuf->boundary[0], co ); + v3_mul( co, inv_ext, co ); + + for( u32 i=0; i<3; i++ ){ + vert->pos[i] = (co[i]-0.5f) * 32767.0f; + } + + for( u32 i=0; i<4; i++ ){ + vert->colour[i] = colour[i] * 255.0f; + } + } + } + } + } + } +} + +/* + * Create the strips of colour that run through the world along course paths + */ +VG_STATIC void world_gen_routes_generate(void) +{ + world_instance *world = world_loading_instance(); + vg_info( "Generating route meshes\n" ); + vg_async_stall(); + + vg_rand_seed( 2000 ); + vg_async_item *call_scene = scene_alloc_async( &world->scene_lines, + &world->mesh_route_lines, + 200000, 300000 ); + + vg_async_item *call_pointcloud = NULL; + pointcloud_buffer *pcbuf = NULL; + + if( world_loader.generate_point_cloud ){ + call_pointcloud = vg_async_alloc( + sizeof(pointcloud_buffer) + + sizeof(pointcloud_vert)*POINTCLOUD_POINTS ); + pcbuf = call_pointcloud->payload; + pcbuf->count = 0; + pcbuf->max = POINTCLOUD_POINTS; + pcbuf->op = k_pointcloud_op_clear; + + v3f ext, mid, v0; + v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], ext ); + f32 maxe = v3_maxf( ext ); + v3_fill( v0, maxe * 0.5f ); + v3_muladds( world->scene_geo.bbx[0], ext, 0.5f, mid ); + v3_add( mid, v0, pcbuf->boundary[1] ); + v3_sub( mid, v0, pcbuf->boundary[0] ); + } + + for( u32 i=0; ient_gate); i++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, i ); + gate->ref_count = 0; + gate->route_count = 0; + } + + for( u32 i=0; ient_route_node); i++ ){ + ent_route_node *rn = mdl_arritm( &world->ent_route_node, i ); + rn->ref_count = 0; + rn->ref_total = 0; + } + + for( u32 k=0; kent_route); k++ ){ + ent_route *route = mdl_arritm( &world->ent_route, k ); + + for( int i=0; icheckpoints_count; i++ ){ + int i0 = route->checkpoints_start+i, + i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); + + ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), + *c1 = mdl_arritm(&world->ent_checkpoint, i1); + + ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); + start_gate = mdl_arritm( &world->ent_gate, start_gate->target ); + start_gate->route_count ++; + + if( !c0->path_count ) + continue; + + for( int j=0; jpath_count; j ++ ){ + ent_path_index *index = mdl_arritm( &world->ent_path_index, + c0->path_start+j ); + ent_route_node *rn = mdl_arritm( &world->ent_route_node, + index->index ); + rn->ref_total ++; + } + } + } + + for( u32 i=0; ient_route); i++ ){ + world_routes_gen_meshes( world, i, &world->scene_lines, pcbuf ); + } + + if( world_loader.generate_point_cloud ){ + f64 area = 0.0; + area = world_routes_scatter_surface_points( world, pcbuf, 16.0f ); + world_routes_surface_grid( world, pcbuf ); + vg_info( "Distrubuted %u points over %fkm^2!\n", + pcbuf->count, area/1e6f ); + vg_async_dispatch( call_pointcloud, async_pointcloud_sub ); + } + + vg_async_dispatch( call_scene, async_scene_upload ); + + world_routes_clear( world ); +} + +/* load all routes from model header */ +VG_STATIC void world_gen_routes_ent_init(void) +{ + world_instance *world = world_loading_instance(); + vg_info( "Initializing routes\n" ); + + for( u32 i=0; ient_gate); i++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, i ); + for( u32 j=0; j<4; j++ ){ + gate->routes[j] = 0xffff; + } + } + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm(&world->ent_route,i); + mdl_transform_m4x3( &route->transform, route->board_transform ); + + route->official_track_id = 0xffffffff; + for( u32 j=0; jmeta,route->pstr_name))){ + route->official_track_id = j; + } + } + + for( u32 j=0; jcheckpoints_count; j++ ){ + u32 id = route->checkpoints_start + j; + ent_checkpoint *cp = mdl_arritm(&world->ent_checkpoint,id); + + ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); + + for( u32 k=0; k<4; k++ ){ + if( gate->routes[k] == 0xffff ){ + gate->routes[k] = i; + break; + } + } + + if( gate->type == k_gate_type_teleport ){ + gate = mdl_arritm(&world->ent_gate, gate->target ); + + for( u32 k=0; k<4; k++ ){ + if( gate->routes[k] == i ){ + vg_error( "already assigned route to gate\n" ); + break; + } + if( gate->routes[k] == 0xffff ){ + gate->routes[k] = i; + break; + } + } + } + } + } + + for( u32 i=0; ient_gate); i++ ){ + ent_gate *gate = mdl_arritm( &world->ent_gate, i ); + } + + world_routes_clear( world ); +} + +/* + * ----------------------------------------------------------------------------- + * Events + * ----------------------------------------------------------------------------- + */ + +VG_STATIC void world_routes_init(void) +{ + world_static.current_run_version = 200; + world_static.time = 300.0; + world_static.last_use = 0.0; + + shader_scene_route_register(); + shader_routeui_register(); +} + +VG_STATIC void world_routes_update( world_instance *world ) +{ + world_static.time += vg.time_delta; + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + + int target = route->active_checkpoint == 0xffff? 0: 1; + route->factive = vg_lerpf( route->factive, target, 0.6f*vg.time_delta ); + } + + for( u32 i=0; iobj, VG__RED ); + } +} + +VG_STATIC void world_routes_fixedupdate( world_instance *world ) +{ + rb_solver_reset(); + + for( u32 i=0; iobj.rb.to_world, + &particle->obj.inf.sphere, + NULL, &world->rb_geo.inf.scene, buf ); + + for( int j=0; jobj.rb; + buf[j].rbb = &world->rb_geo.rb; + } + + rb_contact_count += l; + } + } + + rb_presolve_contacts( rb_contact_buffer, rb_contact_count ); + + for( int i=0; iobj.rb ); + } + + for( u32 i=0; iobj.rb ); + } +} + +VG_STATIC void bind_terrain_noise(void); +VG_STATIC void world_bind_light_array( world_instance *world, + GLuint shader, GLuint location, + int slot ); +VG_STATIC void world_bind_light_index( world_instance *world, + GLuint shader, GLuint location, + int slot ); + +VG_STATIC void world_routes_update_timer_texts( world_instance *world ) +{ + world_render.timer_text_count = 0; + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + + if( route->active_checkpoint != 0xffff ){ + u32 next = route->active_checkpoint+1; + next = next % route->checkpoints_count; + next += route->checkpoints_start; + + ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next ); + ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); + ent_gate *dest = mdl_arritm( &world->ent_gate, gate->target ); + + u32 j=0; + for( ; j<4; j++ ){ + if( dest->routes[j] == i ){ + break; + } + } + + float h0 = 0.8f, + h1 = 1.2f, + depth = 0.4f, + size = 0.4f; + + struct timer_text *text = + &world_render.timer_texts[ world_render.timer_text_count ++ ]; + + text->gate = gate; + text->route = route; + + if( route->valid_checkpoints >= route->checkpoints_count ){ + double lap_time = world_static.time - route->timing_base, + time_centiseconds = lap_time * 100.0; + + if( time_centiseconds > (float)0xfffe ) time_centiseconds = 0.0; + + u16 centiseconds = time_centiseconds, + seconds = centiseconds / 100, + minutes = seconds / 60; + + centiseconds %= 100; + seconds %= 60; + minutes %= 60; + + if( minutes > 9 ) minutes = 9; + + int j=0; + if( minutes ){ + highscore_intr( text->text, minutes, 1, ' ' ); j++; + text->text[j++] = ':'; + } + + if( seconds >= 10 || minutes ){ + highscore_intr( text->text+j, seconds, 2, '0' ); j+=2; + } + else{ + highscore_intr( text->text+j, seconds, 1, '0' ); j++; + } + + text->text[j++] = '.'; + highscore_intr( text->text+j, centiseconds, 1, '0' ); j++; + text->text[j] = '\0'; + } + else{ + highscore_intr( text->text, route->valid_checkpoints, 1, ' ' ); + text->text[1] = '/'; + highscore_intr( text->text+2, route->checkpoints_count+1, 1, ' ' ); + text->text[3] = '\0'; + } + + float align_r = font3d_string_width( &gui.font, 0, text->text ); + align_r *= size; + + v3f positions[] = { + { -0.92f, h0, depth }, + { 0.92f - align_r, h0, depth }, + { -0.92f, h1, depth }, + { 0.92f - align_r, h1, depth }, + }; + + if( dest->route_count == 1 ){ + positions[0][0] = -align_r*0.5f; + positions[0][1] = h1; + } + + m3x3_copy( gate->to_world, text->transform ); + float ratio = v3_length(text->transform[0]) / + v3_length(text->transform[1]); + + m3x3_scale( text->transform, (v3f){ size, size*ratio, 0.1f } ); + m4x3_mulv( gate->to_world, positions[j], text->transform[3] ); + } + } +} + +VG_STATIC void world_routes_fracture( world_instance *world, ent_gate *gate, + v3f imp_co, v3f imp_v ) +{ + world_render.text_particle_count = 0; + + for( u32 i=0; igate != gate ) continue; + + m4x3f transform; + m4x3_mul( gate->transport, text->transform, transform ); + + v3f co, s; + v4f q; + m4x3_decompose( transform, co, q, s ); + + v3f offset; + v3_zero( offset ); + + v4f colour; + float brightness = 0.3f + world->ub_lighting.g_day_phase; + v3_muls( text->route->colour, brightness, colour ); + colour[3] = 1.0f-text->route->factive; + + for( u32 j=0;; j++ ){ + char c = text->text[j]; + if( !c ) break; + + ent_glyph *glyph = font3d_glyph( &gui.font, 0, c ); + if( !glyph ) continue; + + if( c >= (u32)'0' && c <= (u32)'9' && glyph->indice_count ){ + struct text_particle *particle = + &world_render.text_particles[world_render.text_particle_count++]; + + particle->glyph = glyph; + v4_copy( colour, particle->colour ); + + v3f origin; + v2_muls( glyph->size, 0.5f, origin ); + origin[2] = -0.5f; + + v3f world_co; + + 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(), particle->obj.rb.v ); + particle->obj.rb.v[1] += 2.0f; + + v4_copy( q, particle->obj.rb.q ); + particle->obj.rb.w[0] = vg_randf64()*2.0f-1.0f; + particle->obj.rb.w[1] = vg_randf64()*2.0f-1.0f; + particle->obj.rb.w[2] = vg_randf64()*2.0f-1.0f; + + particle->obj.type = k_rb_shape_sphere; + particle->obj.inf.sphere.radius = r*0.6f; + + rb_init_object( &particle->obj ); + } + offset[0] += glyph->size[0]; + } + } +} + +VG_STATIC void render_world_routes( world_instance *world, camera *cam, + int layer_depth ) +{ + m4x3f identity_matrix; + m4x3_identity( identity_matrix ); + + shader_scene_route_use(); + shader_scene_route_uTexGarbage(0); + world_link_lighting_ub( world, _shader_scene_route.id ); + world_bind_position_texture( world, _shader_scene_route.id, + _uniform_scene_route_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_route.id, + _uniform_scene_route_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_route.id, + _uniform_scene_route_uLightsIndex, 4 ); + bind_terrain_noise(); + + shader_scene_route_uPv( cam->mtx.pv ); + shader_scene_route_uPvmPrev( cam->mtx_prev.pv ); + shader_scene_route_uMdl( identity_matrix ); + shader_scene_route_uCamera( cam->transform[3] ); + + mesh_bind( &world->mesh_route_lines ); + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + + v4f colour; + v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour ); + colour[3] = route->factive*0.2f; + + shader_scene_route_uColour( colour ); + mdl_draw_submesh( &route->sm ); + } + + /* timers + * ---------------------------------------------------- */ + if( layer_depth == 0 ){ + font3d_bind( &gui.font, cam ); + + for( u32 i=0; iub_lighting.g_day_phase; + v3_muls( text->route->colour, brightness, colour ); + colour[3] = 1.0f-text->route->factive; + + shader_model_font_uColour( colour ); + font3d_simple_draw( &gui.font, 0, text->text, cam, text->transform ); + } + + shader_model_font_uOffset( (v4f){0.0f,0.0f,0.0f,1.0f} ); + + for( u32 i=0; imdl, prev_mtx ); + m4x4_mul( cam->mtx_prev.pv, prev_mtx, prev_mtx ); + + shader_model_font_uPvmPrev( prev_mtx ); + + v4f q; + m4x3f model; + rb_extrapolate( &particle->obj.rb, model[3], q ); + q_m3x3( q, model ); + + m4x3_mul( model, particle->mlocal, particle->mdl ); + shader_model_font_uMdl( particle->mdl ); + shader_model_font_uColour( particle->colour ); + + mesh_drawn( particle->glyph->indice_start, + particle->glyph->indice_count ); + } + } + + /* gate markers + * ---------------------------------------------------- */ + + shader_model_gate_use(); + shader_model_gate_uPv( cam->mtx.pv ); + shader_model_gate_uCam( cam->pos ); + shader_model_gate_uTime( vg.time*0.25f ); + shader_model_gate_uInvRes( (v2f){ + 1.0f / (float)vg.window_x, + 1.0f / (float)vg.window_y }); + + mesh_bind( &world_gates.mesh ); + + /* skip writing into the motion vectors for this */ + glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } ); + + for( u32 i=0; ient_route); i++ ){ + ent_route *route = mdl_arritm( &world->ent_route, i ); + + if( route->active_checkpoint != 0xffff ){ + v4f colour; + float brightness = 0.3f + world->ub_lighting.g_day_phase; + v3_muls( route->colour, brightness, colour ); + colour[3] = 1.0f-route->factive; + + shader_model_gate_uColour( colour ); + + u32 next = route->active_checkpoint+1+layer_depth; + next = next % route->checkpoints_count; + next += route->checkpoints_start; + + ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next ); + ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); + shader_model_gate_uMdl( gate->to_world ); + + for( u32 j=0; j<4; j++ ){ + if( gate->routes[j] == i ){ + mdl_draw_submesh( &world_gates.sm_marker[j] ); + break; + } + } + } + } + glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } ); +} + +#endif /* ROUTES_C */ diff --git a/world_routes.h b/world_routes.h index b0151b4..c63dd54 100644 --- a/world_routes.h +++ b/world_routes.h @@ -5,1248 +5,20 @@ #ifndef ROUTES_H #define ROUTES_H -#include #include "world.h" -#include "world_gate.h" -#include "font.h" -#include "pointcloud.h" - -#if 0 -#include "shaders/vblend.h" -#endif - -#include "shaders/scene_route.h" -#include "shaders/routeui.h" - - -VG_STATIC -void world_routes_local_set_record( world_instance *world, ent_route *route, - double lap_time ) -{ - vg_success( " NEW LAP TIME: %f\n", lap_time ); - - if( route->official_track_id != 0xffffffff ){ - double time_centiseconds = lap_time * 100.0; - if( time_centiseconds > (float)0xfffe ) /* skill issue */ - return; - - highscore_record temp; - temp.trackid = route->official_track_id; - temp.datetime = time(NULL); - temp.playerid = 0; - temp.points = 0; - temp.time = time_centiseconds; - -#if 0 - highscores_push_record( &temp ); -#endif - - struct track_info *ti = &track_infos[ route->official_track_id ]; - ti->push = 1; - - if( ti->achievement_id ){ -#if 0 - steam_set_achievement( ti->achievement_id ); - steam_store_achievements(); -#endif - } - } - else{ - vg_warn( "There is no associated track for this record...\n" ); - } -} - - -VG_STATIC void world_routes_clear( world_instance *world ) -{ - for( u32 i=0; ient_route ); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - route->active_checkpoint = 0xffff; - } - - for( u32 i=0; ient_gate ); i++ ){ - ent_gate *rg = mdl_arritm( &world->ent_gate, i ); - rg->timing_version = 0; - rg->timing_time = 0.0; - } - - world_global.current_run_version += 4; - world_global.last_use = 0.0; -} - -VG_STATIC void world_routes_time_lap( world_instance *world, ent_route *route ) -{ - vg_info( "------- time lap %s -------\n", - mdl_pstr(&world->meta,route->pstr_name) ); - - double start_time = 0.0; - u32 last_version=0; - - u32 valid_count=0; - - for( u32 i=0; icheckpoints_count; i++ ){ - u32 cpid = (i+route->active_checkpoint) % route->checkpoints_count; - cpid += route->checkpoints_start; - - ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, cpid ); - ent_gate *rg = mdl_arritm( &world->ent_gate, cp->gate_index ); - rg = mdl_arritm( &world->ent_gate, rg->target ); - - if( i == 1 ){ - route->timing_base = rg->timing_time; - } - - if( i == 0 ) - start_time = rg->timing_time; - else{ - if( last_version+1 == rg->timing_version ) valid_count ++; - else valid_count = 0; - } - - last_version = rg->timing_version; - vg_info( "%u %f\n", rg->timing_version, rg->timing_time ); - } - - if( world_global.current_run_version == last_version+1 ){ - valid_count ++; - - if( route->checkpoints_count == 1 ){ - route->timing_base = world_global.time; - } - } - else valid_count = 0; - - vg_info( "%u %f\n", world_global.current_run_version, world_global.time ); - - if( valid_count==route->checkpoints_count ){ - double lap_time = world_global.time - start_time; - world_routes_local_set_record( world, route, lap_time ); - } - - route->valid_checkpoints = valid_count+1; - - vg_info( "valid: %u\n", valid_count ); - vg_info( "----------------------------\n" ); -} - -/* - * When going through a gate this is called for bookkeeping purposes - */ -VG_STATIC void world_routes_activate_entry_gate( world_instance *world, - ent_gate *rg ) -{ - world_global.last_use = world_global.time; - - /* disable all routes and leave the world */ - if( rg->type == k_gate_type_nonlocel ){ - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - route->active_checkpoint = 0xffff; - } - return; - } - - ent_gate *dest = mdl_arritm( &world->ent_gate, rg->target ); - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - - u32 active_prev = route->active_checkpoint; - route->active_checkpoint = 0xffff; - - for( u32 j=0; j<4; j++ ){ - if( dest->routes[j] == i ){ - for( u32 k=0; kcheckpoints_count; k++ ){ - ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, - route->checkpoints_start+k ); - - ent_gate *gk = mdl_arritm( &world->ent_gate, cp->gate_index ); - gk = mdl_arritm( &world->ent_gate, gk->target ); - if( gk == dest ){ - route->active_checkpoint = k; - world_routes_time_lap( world, route ); - break; - } - } - break; - } - } - } - - dest->timing_version = world_global.current_run_version; - dest->timing_time = world_global.time; - - world_global.current_run_version ++; -} - -/* draw lines along the paths */ -VG_STATIC void world_routes_debug( world_instance *world ) -{ - for( u32 i=0; ient_route_node); i++ ){ - ent_route_node *rn = mdl_arritm(&world->ent_route_node,i); - vg_line_pt3( rn->co, 0.25f, VG__WHITE ); - } - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm(&world->ent_route, i); - - u32 colours[] = { 0xfff58142, 0xff42cbf5, 0xff42f56c, 0xfff542b3, - 0xff5442f5 }; - - u32 cc = 0xffcccccc; - if( route->active_checkpoint != 0xffff ){ - cc = colours[i%vg_list_size(colours)]; - } - - for( int i=0; icheckpoints_count; i++ ){ - int i0 = route->checkpoints_start+i, - i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); - - ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), - *c1 = mdl_arritm(&world->ent_checkpoint, i1); - - ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); - ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index ); - - v3f p0, p1; - v3_copy( start_gate->co[1], p0 ); - - for( int j=0; jpath_count; j ++ ){ - ent_path_index *index = mdl_arritm( &world->ent_path_index, - c0->path_start+j ); - - ent_route_node *rn = mdl_arritm( &world->ent_route_node, - index->index ); - - v3_copy( rn->co, p1 ); - vg_line( p0, p1, cc ); - v3_copy( p1, p0 ); - } - - v3_copy( end_gate->co[0], p1 ); - vg_line( p0, p1, cc ); - } - } -} - -VG_STATIC -void world_routes_pointcloud_spot( world_instance *world, - pointcloud_buffer *pcbuf, - v3f co, f32 radius, u32 samples, v4f colour ) -{ - v3f inv_ext; - v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); - v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); - - for( u32 j=0; jcount >= pcbuf->max ) - return; - - pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; - - v3f sample, jitter, point; - vg_rand_sphere( jitter ); - v3_muladds( co, jitter, radius, sample ); - - if( bh_closest_point( world->geo_bh, sample, point, radius*1.5f ) == -1 ){ - v3_copy( sample, point ); - } - - v3f pos; - v3_sub( point, pcbuf->boundary[0], pos ); - v3_mul( pos, inv_ext, pos ); - - for( u32 i=0; i<3; i++ ){ - vert->pos[i] = (pos[i]-0.5f) * 32767.0f; - } - - float dist = 1.0f-(v3_length(jitter)); - - for( u32 i=0; i<4; i++ ){ - vert->colour[i] = colour[i] * 255.0f * dist*dist; - } - } -} - -VG_STATIC -void world_routes_place_curve( world_instance *world, ent_route *route, - v4f h[3], v3f n0, v3f n2, scene_context *scene, - pointcloud_buffer *pcbuf ) -{ - float t; - v3f p, pd; - int last_valid=0; - - float total_length = 0.0f, - travel_length = 0.0; - - v3f last; - v3_copy( h[0], last ); - for( int it=0; it<128; it ++ ){ - t = (float)(it+1) * (1.0f/128.0f); - eval_bezier3( h[0], h[1], h[2], t, p ); - total_length += v3_dist( p, last ); - v3_copy( p, last ); - } - - float patch_size = 4.0f, - patch_count = ceilf( total_length / patch_size ); - - t = 0.0f; - v3_copy( h[0], last ); - - for( int it=0; it<128; it ++ ){ - float const k_sample_dist = 0.0025f, - k_line_width = 1.5f; - - eval_bezier3( h[0], h[1], h[2], t, p ); - eval_bezier3( h[0], h[1], h[2], t+k_sample_dist, pd ); - - travel_length += v3_dist( p, last ); - - float mod = k_sample_dist / v3_dist( p, pd ); - - v3f v0,up, right; - - v3_muls( n0, -(1.0f-t), up ); - v3_muladds( up, n2, -t, up ); - v3_normalize( up ); - - v3_sub( pd,p,v0 ); - v3_cross( up, v0, right ); - v3_normalize( right ); - - float cur_x = (1.0f-t)*h[0][3] + t*h[2][3]; - - v3f sc, sa, sb, down; - v3_muladds( p, right, cur_x * k_line_width, sc ); - v3_muladds( sc, up, 1.5f, sc ); - v3_muladds( sc, right, k_line_width*0.95f, sa ); - v3_muladds( sc, right, 0.0f, sb ); - v3_muls( up, -1.0f, down ); - - ray_hit ha, hb; - ha.dist = 8.0f; - hb.dist = 8.0f; - - int resa = ray_world( world, sa, down, &ha ), - resb = ray_world( world, sb, down, &hb ); - - if( resa ){ - world_routes_pointcloud_spot( world, pcbuf, ha.pos, - 12.0f, 10, route->colour ); - } - - if( resa && resb ){ - struct world_surface *surfa = ray_hit_surface( world, &ha ), - *surfb = ray_hit_surface( world, &hb ); - - if( (surfa->info.flags & k_material_flag_skate_target) && - (surfb->info.flags & k_material_flag_skate_target) ) - { - scene_vert va, vb; - - float gap = vg_fractf(cur_x*0.5f)*0.02f; - - v3_muladds( ha.pos, up, 0.06f+gap, va.co ); - v3_muladds( hb.pos, up, 0.06f+gap, vb.co ); - - scene_vert_pack_norm( &va, up ); - scene_vert_pack_norm( &vb, up ); - - float t1 = (travel_length / total_length) * patch_count; - va.uv[0] = t1; - va.uv[1] = 0.0f; - vb.uv[0] = t1; - vb.uv[1] = 1.0f; - - scene_push_vert( scene, &va ); - scene_push_vert( scene, &vb ); - - if( last_valid ){ - /* Connect them with triangles */ - scene_push_tri( scene, (u32[3]){ - last_valid+0-2, last_valid+1-2, last_valid+2-2} ); - scene_push_tri( scene, (u32[3]){ - last_valid+1-2, last_valid+3-2, last_valid+2-2} ); - } - - last_valid = scene->vertex_count; - } - else - last_valid = 0; - } - else - last_valid = 0; - - if( t == 1.0f ) - return; - - t += 1.0f*mod; - if( t > 1.0f ) - t = 1.0f; - - v3_copy( p, last ); - } -} - -VG_STATIC void world_routes_gen_meshes( world_instance *world, u32 route_id, - scene_context *sc, - pointcloud_buffer *pcbuf ) -{ - ent_route *route = mdl_arritm( &world->ent_route, route_id ); - u8 colour[4]; - colour[0] = route->colour[0] * 255.0f; - colour[1] = route->colour[1] * 255.0f; - colour[2] = route->colour[2] * 255.0f; - colour[3] = route->colour[3] * 255.0f; - - u32 last_valid = 0; - - for( int i=0; icheckpoints_count; i++ ){ - int i0 = route->checkpoints_start+i, - i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); - - ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), - *c1 = mdl_arritm(&world->ent_checkpoint, i1); - - ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); - start_gate = mdl_arritm( &world->ent_gate, start_gate->target ); - - ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index ), - *collector = mdl_arritm( &world->ent_gate, end_gate->target ); - - v4f p[3]; - - v3_add( (v3f){0.0f,0.1f,0.0f}, start_gate->co[0], p[0] ); - p[0][3] = start_gate->ref_count; - p[0][3] -= (float)start_gate->route_count * 0.5f; - start_gate->ref_count ++; - - if( !c0->path_count ) - continue; - - /* this is so that we get nice flow through the gates */ - v3f temp_alignments[2]; - ent_gate *both[] = { start_gate, end_gate }; - - for( int j=0; j<2; j++ ){ - int pi = c0->path_start + ((j==1)? c0->path_count-1: 0); - - ent_path_index *index = mdl_arritm( &world->ent_path_index, pi ); - ent_route_node *rn = mdl_arritm( &world->ent_route_node, - index->index ); - v3f v0; - v3_sub( rn->co, both[j]->co[0], v0 ); - float d = v3_dot( v0, both[j]->to_world[2] ); - - v3_muladds( both[j]->co[0], both[j]->to_world[2], d, - temp_alignments[j] ); - v3_add( (v3f){0.0f,0.1f,0.0f}, temp_alignments[j], temp_alignments[j]); - } - - - for( int j=0; jpath_count; j ++ ){ - ent_path_index *index = mdl_arritm( &world->ent_path_index, - c0->path_start+j ); - ent_route_node *rn = mdl_arritm( &world->ent_route_node, - index->index ); - if( j==0 || j==c0->path_count-1 ) - if( j == 0 ) - v3_copy( temp_alignments[0], p[1] ); - else - v3_copy( temp_alignments[1], p[1] ); - else - v3_copy( rn->co, p[1] ); - - p[1][3] = rn->ref_count; - p[1][3] -= (float)rn->ref_total * 0.5f; - rn->ref_count ++; - - if( j+1 < c0->path_count ){ - index = mdl_arritm( &world->ent_path_index, - c0->path_start+j+1 ); - rn = mdl_arritm( &world->ent_route_node, index->index ); - - if( j+1 == c0->path_count-1 ) - v3_lerp( p[1], temp_alignments[1], 0.5f, p[2] ); - else - v3_lerp( p[1], rn->co, 0.5f, p[2] ); - - p[2][3] = rn->ref_count; - p[2][3] -= (float)rn->ref_total * 0.5f; - } - else{ - v3_copy( end_gate->co[0], p[2] ); - v3_add( (v3f){0.0f,0.1f,0.0f}, p[2], p[2] ); - p[2][3] = collector->ref_count; - - if( i == route->checkpoints_count-1) - p[2][3] -= 1.0f; - - p[2][3] -= (float)collector->route_count * 0.5f; - //collector->ref_count ++; - } - - /* p0,p1,p2 bezier patch is complete - * --------------------------------------*/ - v3f surf0, surf2, n0, n2; - - if( bh_closest_point( world->geo_bh, p[0], surf0, 5.0f ) == -1 ) - v3_add( (v3f){0.0f,-0.1f,0.0f}, p[0], surf0 ); - - if( bh_closest_point( world->geo_bh, p[2], surf2, 5.0f ) == -1 ) - v3_add( (v3f){0.0f,-0.1f,0.0f}, p[2], surf2 ); - - v3_sub( surf0, p[0], n0 ); - v3_sub( surf2, p[2], n2 ); - v3_normalize( n0 ); - v3_normalize( n2 ); - - world_routes_place_curve( world, route, p, n0, n2, sc, pcbuf ); - - /* --- */ - v4_copy( p[2], p[0] ); - } - } - - scene_copy_slice( sc, &route->sm ); -} - -VG_STATIC -struct world_surface *world_tri_index_surface( world_instance *world, - u32 index ); - -VG_STATIC f64 world_routes_scatter_surface_points( world_instance *world, - pointcloud_buffer *pcbuf, - f32 rate ) -{ - static f32 densities[] = { - [k_surface_prop_concrete] = 2.0f, - [k_surface_prop_grass] = 0.8f, - [k_surface_prop_metal] = 1.0f, - [k_surface_prop_wood] = 2.5f, - [k_surface_prop_tiles] = 4.0f - }; - - /* calculate total area */ - f64 total_area = 0.0f; - for( u32 i=0; iscene_geo.indice_count/3; i++ ){ - u32 *tri = &world->scene_geo.arrindices[i*3]; - struct world_surface *surf = world_tri_index_surface( world, tri[0] ); - - if( surf->info.shader == k_shader_boundary || - surf->info.shader == k_shader_invisible ) continue; - - if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue; - - scene_vert *va = &world->scene_geo.arrvertices[tri[0]], - *vb = &world->scene_geo.arrvertices[tri[1]], - *vc = &world->scene_geo.arrvertices[tri[2]]; - - v3f v0, v1, vn; - v3_sub( vb->co, va->co, v0 ); - v3_sub( vc->co, va->co, v1 ); - v3_cross( v0, v1, vn ); - if( vn[1] < 0.0f ) continue; - - f32 density = 1.0f; - if( surf->info.surface_prop < vg_list_size(densities) ) - density = densities[surf->info.surface_prop]; - total_area += v3_length(vn)*0.5f*density; - } - - f32 accum = 0.0f; - - u8 colour[] = { 80,80,80,255 }; - v3f light_dir = {0.3f,0.8f,0.1f}; - v3_normalize( light_dir ); - - v3f inv_ext; - v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); - v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); - - for( u32 i=0; iscene_geo.indice_count/3; i++ ){ - u32 *tri = &world->scene_geo.arrindices[i*3]; - struct world_surface *surf = world_tri_index_surface( world, tri[0] ); - - if( surf->info.shader == k_shader_boundary || - surf->info.shader == k_shader_invisible ) continue; - - if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue; - - scene_vert *va = &world->scene_geo.arrvertices[tri[0]], - *vb = &world->scene_geo.arrvertices[tri[1]], - *vc = &world->scene_geo.arrvertices[tri[2]]; - - v3f v0, v1, vn; - v3_sub( vb->co, va->co, v0 ); - v3_sub( vc->co, va->co, v1 ); - v3_cross( v0, v1, vn ); - if( vn[1] < 0.0f ) continue; - - f32 density = 1.0f; - if( surf->info.surface_prop < vg_list_size(densities) ) - density = densities[surf->info.surface_prop]; - - f32 area = v3_length(vn)*0.5f*density; - accum += area; - - v3_normalize( vn ); - - while( accum > rate ){ - accum -= rate; - - if( pcbuf->count >= pcbuf->max ) return total_area; - - v2f co = { vg_randf64(), vg_randf64() }; - if( v2_length2(co) > 0.5f ){ - co[0] = 1.0f-co[0]; - co[1] = 1.0f-co[1]; - } - - v3f pt; - v3_muls( v0, co[0], pt ); - v3_muladds( pt, v1, co[1], pt ); - v3_add( va->co, pt, pt ); - - if( pt[1] < world->water.height ) continue; - pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; - - v3f pos; - v3_sub( pt, pcbuf->boundary[0], pos ); - v3_mul( pos, inv_ext, pos ); - - for( u32 i=0; i<3; i++ ){ - vert->pos[i] = (pos[i]-0.5f) * 32767.0f; - } - - static v4f colours[] = { - [k_surface_prop_concrete] = { 0.13, 0.15, 0.17, 1.0 }, - [k_surface_prop_grass] = { 0.07, 0.1, 0.14, 1.0 }, - [k_surface_prop_metal] = { 0.15, 0.19, 0.22, 1.0 }, - [k_surface_prop_wood] = { 0.1, 0.13, 0.17, 1.0 }, - [k_surface_prop_tiles] = { 0.05, 0.06, 0.07, 1.0 }, - }; - - v4f col = {0.0f,0.0f,0.0f,0.0f}; - if( surf->info.surface_prop < vg_list_size(colours) ){ - v4_copy( colours[surf->info.surface_prop], col ); - } - - f32 brightness = v3_dot(vn,light_dir)*0.5f+0.5f; - v3_muls( col, brightness, col ); - - for( u32 j=0; j<4; j++ ){ - vert->colour[j] = col[j] * 255.0f; - } - } - } - - return total_area; -} - -VG_STATIC void world_routes_surface_grid( world_instance *world, - pointcloud_buffer *pcbuf ) -{ - i32 const k_gridlines = 32, - k_gridres = 255; - - v3f inv_ext; - v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext ); - v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext ); - v4f colour = {0.2f,0.2f,0.2f,1.0f}; - v3f dir = {0.0f,-1.0f,0.0f}; - - for( u32 k=0; k<2; k++ ){ - u32 a = k*2, - b = (k^0x1)*2; - - for( i32 x=0; x<=k_gridlines; x++ ){ - f32 t = (float)x / (float)k_gridlines, - px = vg_lerpf( pcbuf->boundary[0][a], pcbuf->boundary[1][a], t ); - - for( i32 z=0; z<=k_gridres; z++ ){ - f32 tz = (float)z / (float)k_gridres, - pz = vg_lerpf(pcbuf->boundary[0][b],pcbuf->boundary[1][b], tz); - - v3f ro, hit; - ro[a] = px; - ro[1] = 1000.0f; - ro[b] = pz; - - bh_iter it; - bh_iter_init_ray( 0, &it, ro, dir, INFINITY ); - i32 idx; - - while( bh_next( world->geo_bh, &it, &idx ) ){ - u32 *tri = &world->scene_geo.arrindices[ idx*3 ]; - v3f vs[3]; - - for( u32 i=0; i<3; i++ ){ - v3_copy( world->scene_geo.arrvertices[tri[i]].co, vs[i] ); - } - - f32 t; - if( ray_tri( vs, ro, dir, &t ) ){ - v3_muladds( ro, dir, t, hit ); - struct world_surface *m1 = - world_tri_index_surface( world, tri[0] ); - - if( !(m1->info.flags & k_material_flag_preview_visibile) ) - continue; - - if( world->water.enabled ) - if( hit[1] < world->water.height ) - continue; - - if( pcbuf->count >= pcbuf->max ) return; - - pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ]; - - v3f co; - v3_sub( hit, pcbuf->boundary[0], co ); - v3_mul( co, inv_ext, co ); - - for( u32 i=0; i<3; i++ ){ - vert->pos[i] = (co[i]-0.5f) * 32767.0f; - } - - for( u32 i=0; i<4; i++ ){ - vert->colour[i] = colour[i] * 255.0f; - } - } - } - } - } - } -} - -/* - * Create the strips of colour that run through the world along course paths - */ -VG_STATIC void world_routes_generate( world_instance *world ) -{ - vg_info( "Generating route meshes\n" ); - vg_async_stall(); - - vg_rand_seed( 2000 ); - vg_async_item *call_scene = scene_alloc_async( &world->scene_lines, - &world->mesh_route_lines, - 200000, 300000 ); - vg_async_item *call_pointcloud = vg_async_alloc( - sizeof(pointcloud_buffer) + - sizeof(pointcloud_vert)*POINTCLOUD_POINTS ); - pointcloud_buffer *pcbuf = call_pointcloud->payload; - pcbuf->count = 0; - pcbuf->max = POINTCLOUD_POINTS; - pcbuf->op = k_pointcloud_op_clear; - - v3f ext, mid, v0; - v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], ext ); - f32 maxe = v3_maxf( ext ); - v3_fill( v0, maxe * 0.5f ); - v3_muladds( world->scene_geo.bbx[0], ext, 0.5f, mid ); - v3_add( mid, v0, pcbuf->boundary[1] ); - v3_sub( mid, v0, pcbuf->boundary[0] ); - - for( u32 i=0; ient_gate); i++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, i ); - gate->ref_count = 0; - gate->route_count = 0; - } - - for( u32 i=0; ient_route_node); i++ ){ - ent_route_node *rn = mdl_arritm( &world->ent_route_node, i ); - rn->ref_count = 0; - rn->ref_total = 0; - } - - for( u32 k=0; kent_route); k++ ){ - ent_route *route = mdl_arritm( &world->ent_route, k ); - - for( int i=0; icheckpoints_count; i++ ){ - int i0 = route->checkpoints_start+i, - i1 = route->checkpoints_start+((i+1)%route->checkpoints_count); - - ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0), - *c1 = mdl_arritm(&world->ent_checkpoint, i1); - - ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index ); - start_gate = mdl_arritm( &world->ent_gate, start_gate->target ); - start_gate->route_count ++; - - if( !c0->path_count ) - continue; - - for( int j=0; jpath_count; j ++ ){ - ent_path_index *index = mdl_arritm( &world->ent_path_index, - c0->path_start+j ); - ent_route_node *rn = mdl_arritm( &world->ent_route_node, - index->index ); - rn->ref_total ++; - } - } - } - - for( u32 i=0; ient_route); i++ ){ - world_routes_gen_meshes( world, i, &world->scene_lines, pcbuf ); - } - - f64 area = 0.0; - area = world_routes_scatter_surface_points( world, pcbuf, 16.0f ); - world_routes_surface_grid( world, pcbuf ); - vg_info( "Distrubuted %u points over %fkm^2!\n", pcbuf->count, area/1e6f ); - - vg_async_dispatch( call_scene, async_scene_upload ); - vg_async_dispatch( call_pointcloud, async_pointcloud_sub ); - - world_routes_clear( world ); -} - -/* load all routes from model header */ -VG_STATIC void world_routes_ent_init( world_instance *world ) -{ - vg_info( "Initializing routes\n" ); - - for( u32 i=0; ient_gate); i++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, i ); - for( u32 j=0; j<4; j++ ){ - gate->routes[j] = 0xffff; - } - } - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm(&world->ent_route,i); - mdl_transform_m4x3( &route->transform, route->board_transform ); - - route->official_track_id = 0xffffffff; - for( u32 j=0; jmeta,route->pstr_name))){ - route->official_track_id = j; - } - } - - for( u32 j=0; jcheckpoints_count; j++ ){ - u32 id = route->checkpoints_start + j; - ent_checkpoint *cp = mdl_arritm(&world->ent_checkpoint,id); - - ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); - - for( u32 k=0; k<4; k++ ){ - if( gate->routes[k] == 0xffff ){ - gate->routes[k] = i; - break; - } - } - - if( gate->type == k_gate_type_teleport ){ - gate = mdl_arritm(&world->ent_gate, gate->target ); - - for( u32 k=0; k<4; k++ ){ - if( gate->routes[k] == i ){ - vg_error( "already assigned route to gate\n" ); - break; - } - if( gate->routes[k] == 0xffff ){ - gate->routes[k] = i; - break; - } - } - } - } - } - - for( u32 i=0; ient_gate); i++ ){ - ent_gate *gate = mdl_arritm( &world->ent_gate, i ); - } - - world_routes_clear( world ); -} - -/* - * ----------------------------------------------------------------------------- - * Events - * ----------------------------------------------------------------------------- - */ - -VG_STATIC void world_routes_init(void) -{ - world_global.current_run_version = 200; - world_global.time = 300.0; - world_global.last_use = 0.0; - - shader_scene_route_register(); - shader_routeui_register(); -} - -VG_STATIC void world_routes_update( world_instance *world ) -{ - world_global.time += vg.time_delta; - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - - int target = route->active_checkpoint == 0xffff? 0: 1; - route->factive = vg_lerpf( route->factive, target, 0.6f*vg.time_delta ); - } - - for( u32 i=0; iobj, VG__RED ); - } -} - -VG_STATIC void world_routes_fixedupdate( world_instance *world ) -{ - rb_solver_reset(); - - for( u32 i=0; iobj.rb.to_world, - &particle->obj.inf.sphere, - NULL, &world->rb_geo.inf.scene, buf ); - - for( int j=0; jobj.rb; - buf[j].rbb = &world->rb_geo.rb; - } - - rb_contact_count += l; - } - } - - rb_presolve_contacts( rb_contact_buffer, rb_contact_count ); - - for( int i=0; iobj.rb ); - } - - for( u32 i=0; iobj.rb ); - } -} - -VG_STATIC void bind_terrain_noise(void); -VG_STATIC void world_bind_light_array( world_instance *world, - GLuint shader, GLuint location, - int slot ); -VG_STATIC void world_bind_light_index( world_instance *world, - GLuint shader, GLuint location, - int slot ); - -VG_STATIC void world_routes_update_timer_texts( world_instance *world ) -{ - world_global.timer_text_count = 0; - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - - if( route->active_checkpoint != 0xffff ){ - u32 next = route->active_checkpoint+1; - next = next % route->checkpoints_count; - next += route->checkpoints_start; - - ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next ); - ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); - ent_gate *dest = mdl_arritm( &world->ent_gate, gate->target ); - - u32 j=0; - for( ; j<4; j++ ){ - if( dest->routes[j] == i ){ - break; - } - } - - float h0 = 0.8f, - h1 = 1.2f, - depth = 0.4f, - size = 0.4f; - - struct timer_text *text = - &world_global.timer_texts[ world_global.timer_text_count ++ ]; - - text->gate = gate; - text->route = route; - - if( route->valid_checkpoints >= route->checkpoints_count ){ - double lap_time = world_global.time - route->timing_base, - time_centiseconds = lap_time * 100.0; - - if( time_centiseconds > (float)0xfffe ) time_centiseconds = 0.0; - - u16 centiseconds = time_centiseconds, - seconds = centiseconds / 100, - minutes = seconds / 60; - - centiseconds %= 100; - seconds %= 60; - minutes %= 60; - - if( minutes > 9 ) minutes = 9; - - int j=0; - if( minutes ){ - highscore_intr( text->text, minutes, 1, ' ' ); j++; - text->text[j++] = ':'; - } - - if( seconds >= 10 || minutes ){ - highscore_intr( text->text+j, seconds, 2, '0' ); j+=2; - } - else{ - highscore_intr( text->text+j, seconds, 1, '0' ); j++; - } - - text->text[j++] = '.'; - highscore_intr( text->text+j, centiseconds, 1, '0' ); j++; - text->text[j] = '\0'; - } - else{ - highscore_intr( text->text, route->valid_checkpoints, 1, ' ' ); - text->text[1] = '/'; - highscore_intr( text->text+2, route->checkpoints_count+1, 1, ' ' ); - text->text[3] = '\0'; - } - - float align_r = font3d_string_width( &world_global.font, 0, - text->text ); - align_r *= size; - - v3f positions[] = { - { -0.92f, h0, depth }, - { 0.92f - align_r, h0, depth }, - { -0.92f, h1, depth }, - { 0.92f - align_r, h1, depth }, - }; - - if( dest->route_count == 1 ){ - positions[0][0] = -align_r*0.5f; - positions[0][1] = h1; - } - - m3x3_copy( gate->to_world, text->transform ); - float ratio = v3_length(text->transform[0]) / - v3_length(text->transform[1]); - - m3x3_scale( text->transform, (v3f){ size, size*ratio, 0.1f } ); - m4x3_mulv( gate->to_world, positions[j], text->transform[3] ); - } - } -} +VG_STATIC void world_routes_init(void); VG_STATIC void world_routes_fracture( world_instance *world, ent_gate *gate, - v3f imp_co, v3f imp_v ) -{ - world_global.text_particle_count = 0; - - for( u32 i=0; igate != gate ) continue; - - m4x3f transform; - m4x3_mul( gate->transport, text->transform, transform ); - - v3f co, s; - v4f q; - m4x3_decompose( transform, co, q, s ); - - v3f offset; - v3_zero( offset ); - - v4f colour; - float brightness = 0.3f + world->ub_lighting.g_day_phase; - v3_muls( text->route->colour, brightness, colour ); - colour[3] = 1.0f-text->route->factive; - - for( u32 j=0;; j++ ){ - char c = text->text[j]; - if( !c ) break; - - ent_glyph *glyph = font3d_glyph( &world_global.font, 0, c ); - if( !glyph ) continue; - - if( c >= (u32)'0' && c <= (u32)'9' && glyph->indice_count ){ - struct text_particle *particle = - &world_global.text_particles[world_global.text_particle_count++]; - - particle->glyph = glyph; - v4_copy( colour, particle->colour ); - - v3f origin; - v2_muls( glyph->size, 0.5f, origin ); - origin[2] = -0.5f; - - v3f world_co; - - 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(), particle->obj.rb.v ); - particle->obj.rb.v[1] += 2.0f; - - v4_copy( q, particle->obj.rb.q ); - particle->obj.rb.w[0] = vg_randf64()*2.0f-1.0f; - particle->obj.rb.w[1] = vg_randf64()*2.0f-1.0f; - particle->obj.rb.w[2] = vg_randf64()*2.0f-1.0f; - - particle->obj.type = k_rb_shape_sphere; - particle->obj.inf.sphere.radius = r*0.6f; - - rb_init_object( &particle->obj ); - } - offset[0] += glyph->size[0]; - } - } -} - + v3f imp_co, v3f imp_v ); +VG_STATIC void world_routes_activate_entry_gate( world_instance *world, + ent_gate *rg ); VG_STATIC void render_world_routes( world_instance *world, camera *cam, - int layer_depth ) -{ - m4x3f identity_matrix; - m4x3_identity( identity_matrix ); - - shader_scene_route_use(); - shader_scene_route_uTexGarbage(0); - world_link_lighting_ub( world, _shader_scene_route.id ); - world_bind_position_texture( world, _shader_scene_route.id, - _uniform_scene_route_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_route.id, - _uniform_scene_route_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_route.id, - _uniform_scene_route_uLightsIndex, 4 ); - bind_terrain_noise(); - - shader_scene_route_uPv( cam->mtx.pv ); - shader_scene_route_uPvmPrev( cam->mtx_prev.pv ); - shader_scene_route_uMdl( identity_matrix ); - shader_scene_route_uCamera( cam->transform[3] ); - - mesh_bind( &world->mesh_route_lines ); - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - - v4f colour; - v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour ); - colour[3] = route->factive*0.2f; - - shader_scene_route_uColour( colour ); - mdl_draw_submesh( &route->sm ); - } - - /* timers - * ---------------------------------------------------- */ - if( layer_depth == 0 ){ - font3d_bind( &world_global.font, cam ); - - for( u32 i=0; iub_lighting.g_day_phase; - v3_muls( text->route->colour, brightness, colour ); - colour[3] = 1.0f-text->route->factive; - - shader_model_font_uColour( colour ); - font3d_simple_draw( &world_global.font, 0, text->text, - cam, text->transform ); - } - - shader_model_font_uOffset( (v4f){0.0f,0.0f,0.0f,1.0f} ); - - for( u32 i=0; imdl, prev_mtx ); - m4x4_mul( cam->mtx_prev.pv, prev_mtx, prev_mtx ); - - shader_model_font_uPvmPrev( prev_mtx ); - - v4f q; - m4x3f model; - rb_extrapolate( &particle->obj.rb, model[3], q ); - q_m3x3( q, model ); - - m4x3_mul( model, particle->mlocal, particle->mdl ); - shader_model_font_uMdl( particle->mdl ); - shader_model_font_uColour( particle->colour ); - - mesh_drawn( particle->glyph->indice_start, - particle->glyph->indice_count ); - } - } - - /* gate markers - * ---------------------------------------------------- */ - - shader_model_gate_use(); - shader_model_gate_uPv( cam->mtx.pv ); - shader_model_gate_uCam( cam->pos ); - shader_model_gate_uTime( vg.time*0.25f ); - shader_model_gate_uInvRes( (v2f){ - 1.0f / (float)vg.window_x, - 1.0f / (float)vg.window_y }); - - mesh_bind( &world_global.mesh_gate ); - - /* skip writing into the motion vectors for this */ - glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } ); - - for( u32 i=0; ient_route); i++ ){ - ent_route *route = mdl_arritm( &world->ent_route, i ); - - if( route->active_checkpoint != 0xffff ){ - v4f colour; - float brightness = 0.3f + world->ub_lighting.g_day_phase; - v3_muls( route->colour, brightness, colour ); - colour[3] = 1.0f-route->factive; - - shader_model_gate_uColour( colour ); - - u32 next = route->active_checkpoint+1+layer_depth; - next = next % route->checkpoints_count; - next += route->checkpoints_start; - - ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next ); - ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index ); - shader_model_gate_uMdl( gate->to_world ); + int layer_depth ); - for( u32 j=0; j<4; j++ ){ - if( gate->routes[j] == i ){ - mdl_draw_submesh( &world_global.sm_gate_marker[j] ); - break; - } - } - } - } - glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } ); -} +VG_STATIC void world_gen_routes_ent_init(void); +VG_STATIC void world_gen_routes_generate(void); +VG_STATIC void world_routes_update_timer_texts( world_instance *world ); +VG_STATIC void world_routes_update( world_instance *world ); +VG_STATIC void world_routes_fixedupdate( world_instance *world ); #endif /* ROUTES_H */ diff --git a/world_sfd.c b/world_sfd.c new file mode 100644 index 0000000..9fe2d68 --- /dev/null +++ b/world_sfd.c @@ -0,0 +1,213 @@ +#ifndef SFD_C +#define SFD_C + +#include "world_sfd.h" +#include "shaders/scene_scoretext.h" +#include "shaders/scene_vertex_blend.h" + +static f32 sfd_encode_glyph( char c ) +{ + int value = 0; + if( c >= 'a' && c <= 'z' ) + value = c-'a'+11; + else if( c >= '0' && c <= '9' ) + value = c-'0'+1; + else if( c >= 'A' && c <= 'Z' ) + value = c-'A'+11; + else if( c >= '\x01' && c <= '\x01'+10 ) + value = 63-c; + else{ + int base = 11+26; + + switch( c ){ + case '!': value=base+0; break; + case '?': value=base+1; break; + case ',': value=base+2; break; + case '.': value=base+3; break; + case '#': value=base+4; break; + case '$': value=base+5; break; + case '%': value=base+6; break; + case '*': value=base+7; break; + case '+': value=base+8; break; + case '-': value=base+9; break; + case '/': value=base+10; break; + case ':': value=base+11; break; + default: value=0; break; + } + } + + return (float)value; +} + +VG_STATIC void sfd_encode( u32 row, const char *str ) +{ + int end=0; + u32 row_h = world_sfd.h -1 -row; + + for( int i=0; i rate ){ + *cur += rate; + if( *cur > 60.0f ) + *cur -= 60.0f; + } + else + *cur = *target; + } +} + +VG_STATIC void bind_terrain_noise(void); +VG_STATIC void sfd_render( world_instance *world, camera *cam, m4x3f transform ) +{ + mesh_bind( &world_sfd.mesh_display ); + shader_scene_scoretext_use(); + shader_scene_scoretext_uTexMain(1); + + world_link_lighting_ub( world, _shader_scene_scoretext.id ); + world_bind_position_texture( world, _shader_scene_scoretext.id, + _uniform_scene_scoretext_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_scoretext.id, + _uniform_scene_scoretext_uLightsArray, 3 ); + world_bind_light_index( world, _shader_scene_scoretext.id, + _uniform_scene_scoretext_uLightsIndex, 4 ); + + bind_terrain_noise(); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, world_sfd.tex_scoretex ); + + m4x4f pvm_prev; + m4x3_expand( transform, pvm_prev ); + m4x4_mul( cam->mtx_prev.pv, pvm_prev, pvm_prev ); + + shader_scene_scoretext_uPv( cam->mtx.pv ); + shader_scene_scoretext_uPvmPrev( pvm_prev ); + shader_scene_scoretext_uMdl( transform ); + shader_scene_scoretext_uCamera( cam->transform[3] ); + + for( int y=0;ymtx.pv ); + shader_scene_vertex_blend_uPvmPrev( pvm_prev ); + shader_scene_vertex_blend_uMdl( transform ); + shader_scene_vertex_blend_uCamera( cam->transform[3] ); + + mesh_bind( &world_sfd.mesh_base ); + mdl_draw_submesh( &world_sfd.sm_base ); +} + +VG_STATIC int world_sfd_test( int argc, const char *argv[] ) +{ + if( argc == 2 ){ + int row = vg_min( vg_max(atoi(argv[0]),0), world_sfd.h); + sfd_encode( row, argv[1] ); + } + + return 0; +} + +VG_STATIC void world_sfd_init(void) +{ + vg_info( "world_sfd_init\n" ); + shader_scene_scoretext_register(); + vg_console_reg_cmd( "sfd", world_sfd_test, NULL ); + + vg_linear_clear( vg_mem.scratch ); + + mdl_context mscoreboard; + mdl_open( &mscoreboard, "models/rs_scoretext.mdl", vg_mem.scratch ); + mdl_load_metadata_block( &mscoreboard, vg_mem.scratch ); + mdl_async_load_glmesh( &mscoreboard, &world_sfd.mesh_base ); + + mdl_load_mesh_block( &mscoreboard, vg_mem.scratch ); + + scene_context *scene = &world_sfd.scene; + vg_async_item *call = scene_alloc_async( scene, &world_sfd.mesh_display, + 3000, 8000 ); + + + mdl_mesh *m_backer = mdl_find_mesh( &mscoreboard, "backer" ), + *m_card = mdl_find_mesh( &mscoreboard, "score_card" ); + + mdl_submesh + *sm_backer = mdl_arritm( &mscoreboard.submeshs, m_backer->submesh_start ), + *sm_card = mdl_arritm( &mscoreboard.submeshs, m_card->submesh_start ); + world_sfd.sm_base = *sm_backer; + + m4x3f identity; + m4x3_identity( identity ); + + for( int i=0;i<4;i++ ){ + u32 vert_start = scene->vertex_count; + scene_add_mdl_submesh( scene, &mscoreboard, sm_card, identity ); + + for( int j=0; jvertex_count; j++ ){ + scene_vert *vert = &scene->arrvertices[ vert_start+j ]; + + float const k_glyph_uvw = 1.0f/64.0f; + vert->uv[0] -= k_glyph_uvw * (float)(i-1); + vert->norm[3] = i*42; + } + } + + vg_async_dispatch( call, async_scene_upload ); + vg_tex2d_load_qoi_async_file( "textures/scoretext.qoi", + VG_TEX2D_CLAMP|VG_TEX2D_NEAREST, + &world_sfd.tex_scoretex ); + + mdl_close( &mscoreboard ); + + int w = 27, + h = 13; + + world_sfd.w = w; + world_sfd.h = h; + world_sfd.buffer = vg_linear_alloc( vg_mem.rtmemory, 2*w*h*sizeof(float) ); + + for( int i=0; i= 'a' && c <= 'z' ) - value = c-'a'+11; - else if( c >= '0' && c <= '9' ) - value = c-'0'+1; - else if( c >= 'A' && c <= 'Z' ) - value = c-'A'+11; - else if( c >= '\x01' && c <= '\x01'+10 ) - value = 63-c; - else{ - int base = 11+26; +struct world_sfd{ + GLuint tex_scoretex; - switch( c ){ - case '!': value=base+0; break; - case '?': value=base+1; break; - case ',': value=base+2; break; - case '.': value=base+3; break; - case '#': value=base+4; break; - case '$': value=base+5; break; - case '%': value=base+6; break; - case '*': value=base+7; break; - case '+': value=base+8; break; - case '-': value=base+9; break; - case '/': value=base+10; break; - case ':': value=base+11; break; - default: value=0; break; - } - } + glmesh mesh_base, mesh_display; + mdl_submesh sm_base; + u32 active_route_board; + scene_context scene; - return (float)value; + u32 w, h; + float *buffer; } +static world_sfd; +VG_STATIC void world_sfd_init(void); -VG_STATIC void sfd_encode( u32 row, const char *str ) -{ - int end=0; - u32 row_h = world_global.sfd.h -1 -row; - - for( int i=0; i rate ){ - *cur += rate; - if( *cur > 60.0f ) - *cur -= 60.0f; - } - else - *cur = *target; - } -} - -VG_STATIC void bind_terrain_noise(void); -VG_STATIC void sfd_render( world_instance *world, camera *cam, m4x3f transform ) -{ - mesh_bind( &world_global.sfd.mesh_display ); - shader_scene_scoretext_use(); - shader_scene_scoretext_uTexMain(1); - - world_link_lighting_ub( world, _shader_scene_scoretext.id ); - world_bind_position_texture( world, _shader_scene_scoretext.id, - _uniform_scene_scoretext_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_scoretext.id, - _uniform_scene_scoretext_uLightsArray, 3 ); - world_bind_light_index( world, _shader_scene_scoretext.id, - _uniform_scene_scoretext_uLightsIndex, 4 ); - - bind_terrain_noise(); - - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, tex_scoretex ); - - m4x4f pvm_prev; - m4x3_expand( transform, pvm_prev ); - m4x4_mul( cam->mtx_prev.pv, pvm_prev, pvm_prev ); - - shader_scene_scoretext_uPv( cam->mtx.pv ); - shader_scene_scoretext_uPvmPrev( pvm_prev ); - shader_scene_scoretext_uMdl( transform ); - shader_scene_scoretext_uCamera( cam->transform[3] ); - - for( int y=0;ymtx.pv ); - shader_scene_vertex_blend_uPvmPrev( pvm_prev ); - shader_scene_vertex_blend_uMdl( transform ); - shader_scene_vertex_blend_uCamera( cam->transform[3] ); - - mesh_bind( &world_global.sfd.mesh_base ); - mdl_draw_submesh( &world_global.sfd.sm_base ); -} - -VG_STATIC int world_sfd_test( int argc, const char *argv[] ) -{ - if( argc == 2 ){ - int row = vg_min( vg_max(atoi(argv[0]),0), world_global.sfd.h); - sfd_encode( row, argv[1] ); - } - - return 0; -} - -VG_STATIC void world_sfd_init(void) -{ - vg_info( "world_sfd_init\n" ); - shader_scene_scoretext_register(); - vg_console_reg_cmd( "sfd", world_sfd_test, NULL ); - - vg_linear_clear( vg_mem.scratch ); - - mdl_context mscoreboard; - mdl_open( &mscoreboard, "models/rs_scoretext.mdl", vg_mem.scratch ); - mdl_load_metadata_block( &mscoreboard, vg_mem.scratch ); - mdl_async_load_glmesh( &mscoreboard, &world_global.sfd.mesh_base ); - - mdl_load_mesh_block( &mscoreboard, vg_mem.scratch ); - - scene_context *scene = &world_global.sfd.scene; - vg_async_item *call = scene_alloc_async( scene, - &world_global.sfd.mesh_display, - 3000, 8000 ); - - - mdl_mesh *m_backer = mdl_find_mesh( &mscoreboard, "backer" ), - *m_card = mdl_find_mesh( &mscoreboard, "score_card" ); - - mdl_submesh - *sm_backer = mdl_arritm( &mscoreboard.submeshs, m_backer->submesh_start ), - *sm_card = mdl_arritm( &mscoreboard.submeshs, m_card->submesh_start ); - world_global.sfd.sm_base = *sm_backer; - - m4x3f identity; - m4x3_identity( identity ); - - for( int i=0;i<4;i++ ){ - u32 vert_start = scene->vertex_count; - scene_add_mdl_submesh( scene, &mscoreboard, sm_card, identity ); - - for( int j=0; jvertex_count; j++ ){ - scene_vert *vert = &scene->arrvertices[ vert_start+j ]; - - float const k_glyph_uvw = 1.0f/64.0f; - vert->uv[0] -= k_glyph_uvw * (float)(i-1); - vert->norm[3] = i*42; - } - } - - vg_async_dispatch( call, async_scene_upload ); - vg_tex2d_load_qoi_async_file( "textures/scoretext.qoi", - VG_TEX2D_CLAMP|VG_TEX2D_NEAREST, - &tex_scoretex ); - - mdl_close( &mscoreboard ); - - int w = 27, - h = 13; - - world_global.sfd.w = w; - world_global.sfd.h = h; - world_global.sfd.buffer = - vg_linear_alloc( vg_mem.rtmemory, 2*w*h*sizeof(float) ); - - for( int i=0; ient_volume, item_index ); + + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f, 1.0f, 1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f, 1.0f,-1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f,-1.0f, 1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f,-1.0f,-1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f, 1.0f, 1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f, 1.0f,-1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f,-1.0f, 1.0f} ); + m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f,-1.0f,-1.0f} ); +} + +VG_STATIC float volume_vg_centroid( void *user, u32 item_index, int axis ) +{ + world_instance *world = user; + ent_volume *volume = mdl_arritm( &world->ent_volume, item_index ); + return volume->to_world[3][axis]; +} + +VG_STATIC void volume_vg_swap( void *user, u32 ia, u32 ib ) +{ + world_instance *world = user; + ent_volume *a = mdl_arritm( &world->ent_volume, ia ), + *b = mdl_arritm( &world->ent_volume, ib ), + temp; + + temp = *a; + *a = *b; + *b = temp; +} + +VG_STATIC void volume_vg_debug( void *user, u32 item_index ) +{ + world_instance *world = user; + ent_volume *volume = mdl_arritm( &world->ent_volume, item_index ); + vg_line_boxf_transformed( volume->to_world, (boxf){{-1.0f,-1.0f,-1.0f}, + { 1.0f, 1.0f, 1.0f}}, + 0xff00ff00 ); +} + +#endif /* WORLD_VOLUMES_H */ diff --git a/world_volumes.h b/world_volumes.h index da39d26..5bdc2d9 100644 --- a/world_volumes.h +++ b/world_volumes.h @@ -2,57 +2,19 @@ #define WORLD_VOLUMES_H #include "world.h" +#include "bvh.h" -/* - * BVH implementation - * ---------------------------------------------------------------------------- - */ - -VG_STATIC void volume_vg_expand_bound( void *user, boxf bound, u32 item_index ) -{ - world_instance *world = user; - - ent_volume *volume = mdl_arritm( &world->ent_volume, item_index ); - - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f, 1.0f, 1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f, 1.0f,-1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f,-1.0f, 1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){ 1.0f,-1.0f,-1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f, 1.0f, 1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f, 1.0f,-1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f,-1.0f, 1.0f} ); - m4x3_expand_aabb_point( volume->to_world, bound, (v3f){-1.0f,-1.0f,-1.0f} ); -} - -VG_STATIC float volume_vg_centroid( void *user, u32 item_index, int axis ) -{ - world_instance *world = user; - ent_volume *volume = mdl_arritm( &world->ent_volume, item_index ); - return volume->to_world[3][axis]; +struct { + int inside; } +static world_volumes; -VG_STATIC void volume_vg_swap( void *user, u32 ia, u32 ib ) -{ - world_instance *world = user; - ent_volume *a = mdl_arritm( &world->ent_volume, ia ), - *b = mdl_arritm( &world->ent_volume, ib ), - temp; - - temp = *a; - *a = *b; - *b = temp; -} - -VG_STATIC void volume_vg_debug( void *user, u32 item_index ) -{ - world_instance *world = user; - ent_volume *volume = mdl_arritm( &world->ent_volume, item_index ); - vg_line_boxf_transformed( volume->to_world, (boxf){{-1.0f,-1.0f,-1.0f}, - { 1.0f, 1.0f, 1.0f}}, - 0xff00ff00 ); -} +VG_STATIC void volume_vg_expand_bound( void *user, boxf bound, u32 item_index ); +VG_STATIC float volume_vg_centroid( void *user, u32 item_index, int axis ); +VG_STATIC void volume_vg_swap( void *user, u32 ia, u32 ib ); +VG_STATIC void volume_vg_debug( void *user, u32 item_index ); -VG_STATIC bh_system bh_system_volumes = +static bh_system bh_system_volumes = { .expand_bound = volume_vg_expand_bound, .item_centroid = volume_vg_centroid, diff --git a/world_water.c b/world_water.c new file mode 100644 index 0000000..fca3972 --- /dev/null +++ b/world_water.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved + */ + +#ifndef WATER_C +#define WATER_C + +#include "world_water.h" +#include "world_render.h" +#include "render.h" +#include "shaders/scene_water.h" +#include "shaders/scene_water_fast.h" +#include "scene.h" + +VG_STATIC void world_water_init(void) +{ + vg_info( "world_water_init\n" ); + shader_scene_water_register(); + shader_scene_water_fast_register(); + + vg_tex2d_load_qoi_async_file( "textures/water_surf.qoi", + VG_TEX2D_LINEAR|VG_TEX2D_REPEAT, + &world_water.tex_water_surf ); + + vg_success( "done\n" ); +} + +VG_STATIC void water_set_surface( world_instance *world, float height ) +{ + world->water.height = height; + v4_copy( (v4f){ 0.0f, 1.0f, 0.0f, height }, world->water.plane ); +} + +VG_STATIC void world_link_lighting_ub( world_instance *world, GLuint shader ); +VG_STATIC void world_bind_position_texture( world_instance *world, + GLuint shader, GLuint location, + int slot ); +VG_STATIC void world_bind_light_array( world_instance *world, + GLuint shader, GLuint location, + int slot ); +VG_STATIC void world_bind_light_index( world_instance *world, + GLuint shader, GLuint location, + int slot ); + +/* + * Does not write motion vectors + */ +VG_STATIC void render_water_texture( world_instance *world, camera *cam, + int layer_depth ) +{ + if( !world->water.enabled || (vg.quality_profile == k_quality_profile_low) ) + return; + + /* Draw reflection buffa */ + render_fb_bind( gpipeline.fb_water_reflection, 1 ); + glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); + + /* + * Create flipped view matrix. Don't care about motion vectors + */ + float cam_height = cam->transform[3][1] - world->water.height; + + camera water_cam; + water_cam.farz = cam->farz; + water_cam.nearz = cam->nearz; + v3_copy( cam->transform[3], water_cam.transform[3] ); + water_cam.transform[3][1] -= 2.0f * cam_height; + + m3x3f flip; + m3x3_identity( flip ); + flip[1][1] = -1.0f; + m3x3_mul( flip, cam->transform, water_cam.transform ); + + camera_update_view( &water_cam ); + + /* + * Create clipped projection + */ + v4f clippa = { 0.0f, 1.0f, 0.0f, world->water.height-0.1f }; + m4x3_mulp( water_cam.transform_inverse, clippa, clippa ); + clippa[3] *= -1.0f; + + m4x4_copy( cam->mtx.p, water_cam.mtx.p ); + m4x4_clip_projection( water_cam.mtx.p, clippa ); + + camera_finalize( &water_cam ); + + /* + * Draw world + */ + glEnable( GL_DEPTH_TEST ); + glDisable( GL_BLEND ); + glCullFace( GL_FRONT ); + render_world( world, &water_cam, layer_depth ); + glCullFace( GL_BACK ); + + /* + * Create beneath view matrix + */ + camera beneath_cam; + render_fb_bind( gpipeline.fb_water_beneath, 1 ); + glClearColor( 1.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); + + m4x3_copy( cam->transform, beneath_cam.transform ); + camera_update_view( &beneath_cam ); + + float bias = -(cam->transform[3][1]-world->water.height)*0.1f; + + v4f clippb = { 0.0f, -1.0f, 0.0f, -(world->water.height) + bias }; + m4x3_mulp( beneath_cam.transform_inverse, clippb, clippb ); + clippb[3] *= -1.0f; + + m4x4_copy( cam->mtx.p, beneath_cam.mtx.p ); + m4x4_clip_projection( beneath_cam.mtx.p, clippb ); + camera_finalize( &beneath_cam ); + + glEnable( GL_DEPTH_TEST ); + glDisable( GL_BLEND ); + render_world_depth( world, &beneath_cam ); + //glViewport( 0,0, g_render_x, g_render_y ); +} + +VG_STATIC void render_water_surface( world_instance *world, camera *cam ) +{ + if( !world->water.enabled ) + return; + + if( vg.quality_profile == k_quality_profile_high ){ + /* Draw surface */ + shader_scene_water_use(); + + render_fb_bind_texture( gpipeline.fb_water_reflection, 0, 0 ); + shader_scene_water_uTexMain( 0 ); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, world_water.tex_water_surf ); + shader_scene_water_uTexDudv( 1 ); + + shader_scene_water_uInvRes( (v2f){ + 1.0f / (float)vg.window_x, + 1.0f / (float)vg.window_y }); + + world_link_lighting_ub( world, _shader_scene_water.id ); + world_bind_position_texture( world, _shader_scene_water.id, + _uniform_scene_water_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_water.id, + _uniform_scene_water_uLightsArray, 4 ); + world_bind_light_index( world, _shader_scene_water.id, + _uniform_scene_water_uLightsIndex, 5 ); + + render_fb_bind_texture( gpipeline.fb_water_beneath, 0, 3 ); + shader_scene_water_uTexBack( 3 ); + shader_scene_water_uTime( world_static.time ); + shader_scene_water_uCamera( cam->transform[3] ); + shader_scene_water_uSurfaceY( world->water.height ); + + shader_scene_water_uPv( cam->mtx.pv ); + shader_scene_water_uPvmPrev( cam->mtx_prev.pv ); + + m4x3f full; + m4x3_identity( full ); + shader_scene_water_uMdl( full ); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + mesh_bind( &world->mesh_no_collide ); + + for( int i=0; isurface_count; i++ ){ + struct world_surface *mat = &world->surfaces[i]; + + if( mat->info.shader == k_shader_water ){ + shader_scene_water_uShoreColour( mat->info.colour ); + shader_scene_water_uOceanColour( mat->info.colour1 ); + + mdl_draw_submesh( &mat->sm_no_collide ); + } + } + + glDisable(GL_BLEND); + } + else if( vg.quality_profile == k_quality_profile_low ){ + shader_scene_water_fast_use(); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, world_water.tex_water_surf ); + shader_scene_water_fast_uTexDudv( 1 ); + + shader_scene_water_fast_uTime( world_static.time ); + shader_scene_water_fast_uCamera( cam->transform[3] ); + shader_scene_water_fast_uSurfaceY( world->water.height ); + world_link_lighting_ub( world, _shader_scene_water_fast.id ); + world_bind_position_texture( world, _shader_scene_water_fast.id, + _uniform_scene_water_fast_g_world_depth, 2 ); + world_bind_light_array( world, _shader_scene_water_fast.id, + _uniform_scene_water_fast_uLightsArray, 4 ); + + m4x3f full; + m4x3_identity( full ); + shader_scene_water_fast_uMdl( full ); + shader_scene_water_fast_uPv( cam->mtx.pv ); + shader_scene_water_fast_uPvmPrev( cam->mtx_prev.pv ); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + mesh_bind( &world->mesh_no_collide ); + + for( int i=0; isurface_count; i++ ){ + struct world_surface *mat = &world->surfaces[i]; + + if( mat->info.shader == k_shader_water ){ + shader_scene_water_fast_uShoreColour( mat->info.colour ); + shader_scene_water_fast_uOceanColour( mat->info.colour1 ); + + mdl_draw_submesh( &mat->sm_no_collide ); + } + } + + glDisable(GL_BLEND); + } +} + +#endif /* WATER_C */ diff --git a/world_water.h b/world_water.h index 14e3cd7..ad446d6 100644 --- a/world_water.h +++ b/world_water.h @@ -1,228 +1,21 @@ /* - * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved + * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved */ #ifndef WATER_H #define WATER_H #include "world.h" -#include "render.h" -#include "shaders/scene_water.h" -#include "shaders/scene_water_fast.h" -#include "scene.h" -VG_STATIC GLuint tex_water_surf; - -VG_STATIC void world_water_init(void) -{ - vg_info( "world_water_init\n" ); - shader_scene_water_register(); - shader_scene_water_fast_register(); - - vg_tex2d_load_qoi_async_file( "textures/water_surf.qoi", - VG_TEX2D_LINEAR|VG_TEX2D_REPEAT, - &tex_water_surf ); - - vg_success( "done\n" ); +struct world_water{ + VG_STATIC GLuint tex_water_surf; } +static world_water; +VG_STATIC void world_water_init(void); -VG_STATIC void water_set_surface( world_instance *world, float height ) -{ - world->water.height = height; - v4_copy( (v4f){ 0.0f, 1.0f, 0.0f, height }, world->water.plane ); -} - -VG_STATIC void world_link_lighting_ub( world_instance *world, GLuint shader ); -VG_STATIC void world_bind_position_texture( world_instance *world, - GLuint shader, GLuint location, - int slot ); -VG_STATIC void world_bind_light_array( world_instance *world, - GLuint shader, GLuint location, - int slot ); -VG_STATIC void world_bind_light_index( world_instance *world, - GLuint shader, GLuint location, - int slot ); - -/* - * Does not write motion vectors - */ +VG_STATIC void water_set_surface( world_instance *world, float height ); VG_STATIC void render_water_texture( world_instance *world, camera *cam, - int layer_depth ) -{ - if( !world->water.enabled || (vg.quality_profile == k_quality_profile_low) ) - return; - - /* Draw reflection buffa */ - render_fb_bind( gpipeline.fb_water_reflection, 1 ); - glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); - - /* - * Create flipped view matrix. Don't care about motion vectors - */ - float cam_height = cam->transform[3][1] - world->water.height; - - camera water_cam; - water_cam.farz = cam->farz; - water_cam.nearz = cam->nearz; - v3_copy( cam->transform[3], water_cam.transform[3] ); - water_cam.transform[3][1] -= 2.0f * cam_height; - - m3x3f flip; - m3x3_identity( flip ); - flip[1][1] = -1.0f; - m3x3_mul( flip, cam->transform, water_cam.transform ); - - camera_update_view( &water_cam ); - - /* - * Create clipped projection - */ - v4f clippa = { 0.0f, 1.0f, 0.0f, world->water.height-0.1f }; - m4x3_mulp( water_cam.transform_inverse, clippa, clippa ); - clippa[3] *= -1.0f; - - m4x4_copy( cam->mtx.p, water_cam.mtx.p ); - m4x4_clip_projection( water_cam.mtx.p, clippa ); - - camera_finalize( &water_cam ); - - /* - * Draw world - */ - glEnable( GL_DEPTH_TEST ); - glDisable( GL_BLEND ); - glCullFace( GL_FRONT ); - render_world( world, &water_cam, layer_depth ); - glCullFace( GL_BACK ); - - /* - * Create beneath view matrix - */ - camera beneath_cam; - render_fb_bind( gpipeline.fb_water_beneath, 1 ); - glClearColor( 1.0f, 0.0f, 0.0f, 0.0f ); - glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); - - m4x3_copy( cam->transform, beneath_cam.transform ); - camera_update_view( &beneath_cam ); - - float bias = -(cam->transform[3][1]-world->water.height)*0.1f; - - v4f clippb = { 0.0f, -1.0f, 0.0f, -(world->water.height) + bias }; - m4x3_mulp( beneath_cam.transform_inverse, clippb, clippb ); - clippb[3] *= -1.0f; - - m4x4_copy( cam->mtx.p, beneath_cam.mtx.p ); - m4x4_clip_projection( beneath_cam.mtx.p, clippb ); - camera_finalize( &beneath_cam ); - - glEnable( GL_DEPTH_TEST ); - glDisable( GL_BLEND ); - render_world_depth( world, &beneath_cam ); - //glViewport( 0,0, g_render_x, g_render_y ); -} - -VG_STATIC void render_water_surface( world_instance *world, camera *cam ) -{ - if( !world->water.enabled ) - return; - - if( vg.quality_profile == k_quality_profile_high ){ - /* Draw surface */ - shader_scene_water_use(); - - render_fb_bind_texture( gpipeline.fb_water_reflection, 0, 0 ); - shader_scene_water_uTexMain( 0 ); - - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, tex_water_surf ); - shader_scene_water_uTexDudv( 1 ); - - shader_scene_water_uInvRes( (v2f){ - 1.0f / (float)vg.window_x, - 1.0f / (float)vg.window_y }); - - world_link_lighting_ub( world, _shader_scene_water.id ); - world_bind_position_texture( world, _shader_scene_water.id, - _uniform_scene_water_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_water.id, - _uniform_scene_water_uLightsArray, 4 ); - world_bind_light_index( world, _shader_scene_water.id, - _uniform_scene_water_uLightsIndex, 5 ); - - render_fb_bind_texture( gpipeline.fb_water_beneath, 0, 3 ); - shader_scene_water_uTexBack( 3 ); - shader_scene_water_uTime( world_global.time ); - shader_scene_water_uCamera( cam->transform[3] ); - shader_scene_water_uSurfaceY( world->water.height ); - - shader_scene_water_uPv( cam->mtx.pv ); - shader_scene_water_uPvmPrev( cam->mtx_prev.pv ); - - m4x3f full; - m4x3_identity( full ); - shader_scene_water_uMdl( full ); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); - - mesh_bind( &world->mesh_no_collide ); - - for( int i=0; isurface_count; i++ ){ - struct world_surface *mat = &world->surfaces[i]; - - if( mat->info.shader == k_shader_water ){ - shader_scene_water_uShoreColour( mat->info.colour ); - shader_scene_water_uOceanColour( mat->info.colour1 ); - - mdl_draw_submesh( &mat->sm_no_collide ); - } - } - - glDisable(GL_BLEND); - } - else if( vg.quality_profile == k_quality_profile_low ){ - shader_scene_water_fast_use(); - - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, tex_water_surf ); - shader_scene_water_fast_uTexDudv( 1 ); - - shader_scene_water_fast_uTime( world_global.time ); - shader_scene_water_fast_uCamera( cam->transform[3] ); - shader_scene_water_fast_uSurfaceY( world->water.height ); - world_link_lighting_ub( world, _shader_scene_water_fast.id ); - world_bind_position_texture( world, _shader_scene_water_fast.id, - _uniform_scene_water_fast_g_world_depth, 2 ); - world_bind_light_array( world, _shader_scene_water_fast.id, - _uniform_scene_water_fast_uLightsArray, 4 ); - - m4x3f full; - m4x3_identity( full ); - shader_scene_water_fast_uMdl( full ); - shader_scene_water_fast_uPv( cam->mtx.pv ); - shader_scene_water_fast_uPvmPrev( cam->mtx_prev.pv ); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); - - mesh_bind( &world->mesh_no_collide ); - - for( int i=0; isurface_count; i++ ){ - struct world_surface *mat = &world->surfaces[i]; - - if( mat->info.shader == k_shader_water ){ - shader_scene_water_fast_uShoreColour( mat->info.colour ); - shader_scene_water_fast_uOceanColour( mat->info.colour1 ); - - mdl_draw_submesh( &mat->sm_no_collide ); - } - } - - glDisable(GL_BLEND); - } -} + int layer_depth ); +VG_STATIC void render_water_surface( world_instance *world, camera *cam ); #endif /* WATER_H */ -- 2.25.1