#include "gui.h"
#include "menu.h"
#include "pointcloud.h"
+#include "highscores.h"
/*
* Checks string equality but does a hash check first
}
}
-/* generic reciever */
-VG_STATIC void workshop_async_any_complete( void *data, u32 size )
-{
- skaterift_end_op();
-}
-
/*
* op/subroutine: k_workshop_op_item_load
* -----------------------------------------------------------------------------
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;
if( !global_skateshop.active ) return;
- world_instance *world = get_active_world();
+ world_instance *world = world_current_instance();
ent_skateshop *shop = global_skateshop.ptr_ent;
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 ) ){
}
}
- 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;
}
}
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);
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
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{
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();
* ----------------------------------------------------------------- */
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)
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,
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;
}
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 );
#ifndef ENTITY_C
#define ENTITY_C
-#include "entity.h"
#include "world.h"
+#include "entity.h"
+#include "world_entity.h"
#include "ent_skateshop.c"
};
#include "world.h"
+VG_STATIC void entity_call( world_instance *world, ent_call *call );
#endif /* ENTITY_H */
helpers[4];
u32 helper_count;
- float factive;
+ f32 factive;
+ font3d font;
}
static gui;
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;
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 );
helper->text = text;
}
+VG_STATIC void gui_init(void)
+{
+ font3d_load( &gui.font, "models/rs_font.mdl", vg_mem.rtmemory );
+}
+
#endif /* GUI_H */
#include "player.h"
#include "conf.h"
#include "shaders/model_menu.h"
+#include "audio.h"
+#include "input.h"
#define MENU_STACK_SIZE 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[] )
{
}
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;
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,
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 );
}
+/* 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 */
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 */
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 */
}
}
-static v3f TEMP_TPV_EXTRA;
-
VG_STATIC void player__cam_iterate( player_instance *player )
{
struct player_avatar *av = player->playeravatar;
#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 */
*/
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 ++;
#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"
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 );
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;
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 )
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 );
#include "model.h"
#include "skeleton.h"
+#include "camera.h"
+#include "world.h"
struct player_avatar
{
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 */
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 );
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 );
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;
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 ];
}
}
}
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 );
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;
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;
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 );
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
#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
#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;
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();
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 );
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();
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" );
}
{
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();
}
return;
}
- for( u32 i=0; i<vg_list_size(world_global.worlds); i++ ){
- if( world_global.worlds[i].status == k_world_status_loaded ){
- world_prerender( &world_global.worlds[i] );
+ for( u32 i=0; i<vg_list_size(world_static.worlds); i++ ){
+ if( world_static.worlds[i].status == k_world_status_loaded ){
+ world_prerender( &world_static.worlds[i] );
}
}
#if 0
player__im_gui( &localplayer );
#endif
- world_instance *world = get_active_world();
+ world_instance *world = world_current_instance();
workshop_form_gui();
render_view_framebuffer_ui();
}
-static void async_skaterift_world_loaded( void *payload, u32 size )
-{
- skaterift_end_op();
-}
-
-static void skaterift_world_changer_thread( void *data )
-{
- const char *path = data;
- world_load( 1, world_global.load_target );
- vg_async_call( async_skaterift_world_loaded, NULL, 0 );
-}
-
-/* holding pattern before we can start loading the new world, since we might be
- * waiting for audio to stop */
-static void skaterift_change_world_preupdate(void)
-{
- for( u32 i=1; i<vg_list_size(world_global.worlds); i++ ){
- world_instance *inst = &world_global.worlds[i];
-
- if( inst->status == 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; i<vg_list_size(world_global.worlds); i++ ){
- world_instance *inst = &world_global.worlds[i];
-
- if( inst->status == 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
#define SKATERIFT_H
#include "common.h"
+#include "world.h"
struct{
enum async_operation{
--- /dev/null
+#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; j<len; j++ ){
+ manifold[j].rba = rb;
+ manifold[j].rbb = &world_current_instance()->rb_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 */
#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,
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;
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; j<len; j++ ){
- manifold[j].rba = rb;
- manifold[j].rbb = &get_active_world()->rb_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 */
#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
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 =
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 */
--- /dev/null
+/*
+ * 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; i<mdl_arrcount( &world->ent_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; i<mdl_arrcount( &world->ent_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; j<random_ticks; j++ ){
+ ent_call basecall;
+ basecall.id = mdl_entity_id( k_ent_volume, idx );
+ basecall.data = NULL;
+
+ entity_call( world, &basecall );
+ }
+ }
+ }
+ world_static.in_volume = in_volume;
+
+#if 0
+ if( k_debug_light_indices )
+ {
+ for( int i=0; i<world->light_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 */
/*
- * 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
* -------------------------------------------------------
*/
*
* the following arrays index somewhere into this linear
* allocator
- *
- * (world_gen.h)
* --------------------------------------------------------------------------
*/
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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<audio->clip_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; i<AUDIO_CHANNELS; i++ ){
- audio_channel *ch = &vg_audio.channels[i];
-
- if( ch->allocated && (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; i<mdl_arrcount( &world->ent_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; i<mdl_arrcount( &world->ent_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; j<random_ticks; j++ ){
- ent_call basecall;
- basecall.id = mdl_entity_id( k_ent_volume, idx );
- basecall.data = NULL;
-
- entity_call( world, &basecall );
- }
- }
- }
- world_global.in_volume = in_volume;
-
-#if 0
- if( k_debug_light_indices )
- {
- for( int i=0; i<world->light_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; i<world->surface_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 */
--- /dev/null
+#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; i<AUDIO_CHANNELS; i++ ){
+ audio_channel *ch = &vg_audio.channels[i];
+
+ if( ch->allocated && (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 */
--- /dev/null
+#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 */
--- /dev/null
+#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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_audio); j++ ){
+ ent_audio *audio = mdl_arritm( &world->ent_audio, j );
+
+ for( u32 k=0; k<audio->clip_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<audio->clip_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 */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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; i<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; i<vg_list_size(world_static.worlds); i++ ){
+ world_instance *other = &world_static.worlds[i];
+ if( other == world ) continue;
+ if( other->status != k_world_status_loaded ) continue;
+ vg_info( "Checking world %u for key matches\n", i );
+
+ for( u32 j=0; j<mdl_arrcount( &other->ent_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 */
/*
- * 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; i<mdl_arrcount(&world->ent_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 */
--- /dev/null
+/*
+ * 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; i<mdl_arrcount(&mdl->meshs); i++ ){
+ mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
+
+ for( u32 j=0; j<mesh->submesh_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; i<vg_list_size(verts); i++ )
+ {
+ scene_vert *pvert = &dst_verts[ i ],
+ *src = &verts[ i ];
+
+ m4x3_mulv( transform, src->co, pvert->co );
+ scene_vert_pack_norm( pvert, transform[1] );
+
+ v2_copy( ref->uv, pvert->uv );
+ }
+
+ for( u32 i=0; i<vg_list_size(indices); i++ )
+ dst_indices[i] = indices[i] + scene->vertex_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; i<particles; i++ ){
+ v3f pos;
+ v3_mul( volume, (v3f){ vg_randf64(), 1000.0f, vg_randf64() }, pos );
+ pos[1] = 1000.0f;
+ v3_add( pos, world->scene_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; i<world->surface_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; i<world->surface_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; i<mdl_arrcount( &world->ent_traffic ); i++ ){
+ ent_traffic *vehc = mdl_arritm( &world->ent_traffic, i );
+
+ for( u32 j=0; j<vehc->submesh_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; iz<icubes_count[2]; iz ++ ){
+ for( int iy = 0; iy<icubes_count[1]; iy++ ){
+ for( int ix = 0; ix<icubes_count[0]; ix++ ){
+ boxf bbx;
+ v3_div( (v3f){ ix, iy, iz }, world->ub_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; j<mdl_arrcount(&world->ent_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<N; j++ )
+ if( influences[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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->meta.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; i<mdl_arrcount(&world->meta.materials); i++ ){
+ world->surfaces[i+1].info =
+ *(mdl_material *)mdl_arritm( &world->meta.materials, i );
+ }
+}
+
+#endif /* WORLD_GEN_C */
/*
- * 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
#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; i<mdl_arrcount(&mdl->meshs); i++ ){
- mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
-
- for( u32 j=0; j<mesh->submesh_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; i<vg_list_size(verts); i++ )
- {
- scene_vert *pvert = &dst_verts[ i ],
- *src = &verts[ i ];
-
- m4x3_mulv( transform, src->co, pvert->co );
- scene_vert_pack_norm( pvert, transform[1] );
-
- v2_copy( ref->uv, pvert->uv );
- }
-
- for( u32 i=0; i<vg_list_size(indices); i++ )
- dst_indices[i] = indices[i] + scene->vertex_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; i<particles; i++ ){
- v3f pos;
- v3_mul( volume, (v3f){ vg_randf64(), 1000.0f, vg_randf64() }, pos );
- pos[1] = 1000.0f;
- v3_add( pos, world->scene_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; i<world->surface_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; i<world->surface_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; i<mdl_arrcount( &world->ent_traffic ); i++ ){
- ent_traffic *vehc = mdl_arritm( &world->ent_traffic, i );
-
- for( u32 j=0; j<vehc->submesh_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; iz<icubes_count[2]; iz ++ ){
- for( int iy = 0; iy<icubes_count[1]; iy++ ){
- for( int ix = 0; ix<icubes_count[0]; ix++ ){
- boxf bbx;
- v3_div( (v3f){ ix, iy, iz }, world->ub_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; j<mdl_arrcount(&world->ent_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<N; j++ )
- if( influences[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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->meta.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; i<mdl_arrcount(&world->meta.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; i<vg_list_size(world_global.worlds); i++ ){
- if( world_global.worlds[i].status != k_world_status_unloaded ){
- return 0;
- }
- }
-
- int freeable = 1;
- audio_lock();
- for( u32 i=0; i<AUDIO_CHANNELS; i++ ){
- audio_channel *ch = &vg_audio.channels[i];
-
- if( ch->allocated && (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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; i<vg_list_size(world_global.worlds); i++ ){
- world_instance *other = &world_global.worlds[i];
- if( other == world ) continue;
- if( other->status != k_world_status_loaded ) continue;
- vg_info( "Checking world %u for key matches\n", i );
-
- for( u32 j=0; j<mdl_arrcount( &other->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_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; j<mdl_arrcount(&world->ent_audio); j++ ){
- ent_audio *audio = mdl_arritm( &world->ent_audio, j );
-
- for( u32 k=0; k<audio->clip_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 */
#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. */
--- /dev/null
+#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; j<i; j++ ){
+ vg_str name = { .buffer = worlds[j], .i=strlen(worlds[j]),
+ sizeof(worlds[j]) };
+ char *fname = vg_strch( &name, '/' );
+ if( fname ){
+ if( !strcmp( fname+1, "main.mdl" ) ){
+ first_index = j;
+ }
+ }
+ }
+
+ world_loader.generate_point_cloud = 1;
+ world_loader.world_index = 1;
+ world_load_mdl( worlds[first_index] );
+
+ world_loader.generate_point_cloud = 0;
+ for( u32 j=0; j<i; j++ ){
+ if( j != first_index ){
+ world_loader.world_index = j+1;
+ world_load_mdl( worlds[j] );
+ }
+ }
+
+ vg_async_call( async_skaterift_world_loaded, NULL, 0 );
+}
+
+/* holding pattern before we can start loading the new world, since we might be
+ * waiting for audio to stop */
+static void skaterift_change_world_preupdate(void)
+{
+ for( u32 i=1; i<vg_list_size(world_static.worlds); i++ ){
+ world_instance *inst = &world_static.worlds[i];
+
+ if( inst->status == 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; i<vg_list_size(world_static.worlds); i++ ){
+ world_instance *inst = &world_static.worlds[i];
+
+ if( inst->status == 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; i<vg_list_size(world_static.worlds); i++ ){
+ if( world_static.worlds[i].status != k_world_status_unloaded ){
+ return 0;
+ }
+ }
+
+ int freeable = 1;
+ audio_lock();
+ for( u32 i=0; i<AUDIO_CHANNELS; i++ ){
+ audio_channel *ch = &vg_audio.channels[i];
+
+ if( ch->allocated && (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 */
--- /dev/null
+#ifndef WORLD_LOAD_H
+#define WORLD_LOAD_H
+
+#include <time.h>
+
+#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 */
--- /dev/null
+#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; i<world->surface_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 */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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; i<world->surface_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; j<mdl_arrcount( &world->ent_traffic ); j++ ){
+ ent_traffic *traffic = mdl_arritm( &world->ent_traffic, j );
+
+ for( u32 k=0; k<traffic->submesh_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount( &world->ent_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
#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; i<world->surface_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; j<mdl_arrcount( &world->ent_traffic ); j++ ){
- ent_traffic *traffic = mdl_arritm( &world->ent_traffic, j );
-
- for( u32 k=0; k<traffic->submesh_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount( &world->ent_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 */
--- /dev/null
+/*
+ * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
+ */
+
+#ifndef ROUTES_C
+#define ROUTES_C
+
+#include <time.h>
+#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; i<mdl_arrcount( &world->ent_route ); i++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, i );
+ route->active_checkpoint = 0xffff;
+ }
+
+ for( u32 i=0; i<mdl_arrcount( &world->ent_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; i<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; k<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<route->checkpoints_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; j<c0->path_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; j<samples; j++ ){
+ if( pcbuf->count >= 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; i<route->checkpoints_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; j<c0->path_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; i<world->scene_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; i<world->scene_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; i<mdl_arrcount(&world->ent_gate); i++ ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, i );
+ gate->ref_count = 0;
+ gate->route_count = 0;
+ }
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_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; k<mdl_arrcount(&world->ent_route); k++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, k );
+
+ for( int i=0; i<route->checkpoints_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; j<c0->path_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; j<vg_list_size(track_infos); j ++ ){
+ if( !strcmp(track_infos[j].name,
+ mdl_pstr(&world->meta,route->pstr_name))){
+ route->official_track_id = j;
+ }
+ }
+
+ for( u32 j=0; j<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<world_render.text_particle_count; i++ ){
+ struct text_particle *particle = &world_render.text_particles[i];
+ rb_object_debug( &particle->obj, VG__RED );
+ }
+}
+
+VG_STATIC void world_routes_fixedupdate( world_instance *world )
+{
+ rb_solver_reset();
+
+ for( u32 i=0; i<world_render.text_particle_count; i++ ){
+ struct text_particle *particle = &world_render.text_particles[i];
+
+ if( rb_global_has_space() ){
+ rb_ct *buf = rb_global_buffer();
+
+ int l = rb_sphere__scene( particle->obj.rb.to_world,
+ &particle->obj.inf.sphere,
+ NULL, &world->rb_geo.inf.scene, buf );
+
+ for( int j=0; j<l; j++ ){
+ buf[j].rba = &particle->obj.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; i<rb_contact_count; i++ ){
+ rb_contact_restitution( rb_contact_buffer+i, vg_randf64() );
+ }
+
+ for( int i=0; i<6; i++ ){
+ rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+ }
+
+ for( u32 i=0; i<world_render.text_particle_count; i++ ){
+ struct text_particle *particle = &world_render.text_particles[i];
+ rb_iter( &particle->obj.rb );
+ }
+
+ for( u32 i=0; i<world_render.text_particle_count; i++ ){
+ struct text_particle *particle = &world_render.text_particles[i];
+ rb_update_transform( &particle->obj.rb );
+ }
+}
+
+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; i<mdl_arrcount(&world->ent_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; i<world_render.timer_text_count; i++ ){
+ struct timer_text *text = &world_render.timer_texts[i];
+
+ if( text->gate != 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; i<mdl_arrcount(&world->ent_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; i<world_render.timer_text_count; i++ ){
+ struct timer_text *text = &world_render.timer_texts[i];
+
+ 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;
+
+ 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; i<world_render.text_particle_count; i++ ){
+ struct text_particle *particle = &world_render.text_particles[i];
+
+ m4x4f prev_mtx;
+
+ m4x3_expand( particle->mdl, 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; i<mdl_arrcount(&world->ent_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 */
#ifndef ROUTES_H
#define ROUTES_H
-#include <time.h>
#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; i<mdl_arrcount( &world->ent_route ); i++ ){
- ent_route *route = mdl_arritm( &world->ent_route, i );
- route->active_checkpoint = 0xffff;
- }
-
- for( u32 i=0; i<mdl_arrcount( &world->ent_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; i<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; k<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<route->checkpoints_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; j<c0->path_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; j<samples; j++ ){
- if( pcbuf->count >= 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; i<route->checkpoints_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; j<c0->path_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; i<world->scene_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; i<world->scene_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; i<mdl_arrcount(&world->ent_gate); i++ ){
- ent_gate *gate = mdl_arritm( &world->ent_gate, i );
- gate->ref_count = 0;
- gate->route_count = 0;
- }
-
- for( u32 i=0; i<mdl_arrcount(&world->ent_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; k<mdl_arrcount(&world->ent_route); k++ ){
- ent_route *route = mdl_arritm( &world->ent_route, k );
-
- for( int i=0; i<route->checkpoints_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; j<c0->path_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; j<vg_list_size(track_infos); j ++ ){
- if( !strcmp(track_infos[j].name,
- mdl_pstr(&world->meta,route->pstr_name))){
- route->official_track_id = j;
- }
- }
-
- for( u32 j=0; j<route->checkpoints_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; i<mdl_arrcount(&world->ent_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; i<mdl_arrcount(&world->ent_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; i<world_global.text_particle_count; i++ ){
- struct text_particle *particle = &world_global.text_particles[i];
- rb_object_debug( &particle->obj, VG__RED );
- }
-}
-
-VG_STATIC void world_routes_fixedupdate( world_instance *world )
-{
- rb_solver_reset();
-
- for( u32 i=0; i<world_global.text_particle_count; i++ ){
- struct text_particle *particle = &world_global.text_particles[i];
-
- if( rb_global_has_space() ){
- rb_ct *buf = rb_global_buffer();
-
- int l = rb_sphere__scene( particle->obj.rb.to_world,
- &particle->obj.inf.sphere,
- NULL, &world->rb_geo.inf.scene, buf );
-
- for( int j=0; j<l; j++ ){
- buf[j].rba = &particle->obj.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; i<rb_contact_count; i++ ){
- rb_contact_restitution( rb_contact_buffer+i, vg_randf64() );
- }
-
- for( int i=0; i<6; i++ ){
- rb_solve_contacts( rb_contact_buffer, rb_contact_count );
- }
-
- for( u32 i=0; i<world_global.text_particle_count; i++ ){
- struct text_particle *particle = &world_global.text_particles[i];
- rb_iter( &particle->obj.rb );
- }
-
- for( u32 i=0; i<world_global.text_particle_count; i++ ){
- struct text_particle *particle = &world_global.text_particles[i];
- rb_update_transform( &particle->obj.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; i<mdl_arrcount(&world->ent_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; i<world_global.timer_text_count; i++ ){
- struct timer_text *text = &world_global.timer_texts[i];
-
- if( text->gate != 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; i<mdl_arrcount(&world->ent_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; i<world_global.timer_text_count; i++ ){
- struct timer_text *text = &world_global.timer_texts[i];
-
- 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;
-
- 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; i<world_global.text_particle_count; i++ ){
- struct text_particle *particle = &world_global.text_particles[i];
-
- m4x4f prev_mtx;
-
- m4x3_expand( particle->mdl, 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; i<mdl_arrcount(&world->ent_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 */
--- /dev/null
+#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<world_sfd.w; i++ ){
+ u32 idx = (world_sfd.w*row_h + i) * 2;
+
+ if( end ){
+ world_sfd.buffer[idx] = 0.0f;
+ }
+ else{
+ if( !str[i] )
+ end = 1;
+
+ world_sfd.buffer[idx] = sfd_encode_glyph( str[i] );
+ }
+ }
+}
+
+VG_STATIC void sfd_update(void)
+{
+ for( int i=0; i<world_sfd.w*world_sfd.h; i++ ){
+ float *target = &world_sfd.buffer[i*2+0],
+ *cur = &world_sfd.buffer[i*2+1];
+
+ float const rate = vg.time_delta * 15.2313131414f;
+ float d1 = *target-*cur;
+
+ if( fabsf(d1) > 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;y<world_sfd.h; y++ ){
+ for( int x=0; x<world_sfd.w; x++ ){
+ float value = world_sfd.buffer[(y*world_sfd.w+x)*2+1];
+ shader_scene_scoretext_uInfo( (v3f){ x,y, value } );
+ mesh_draw( &world_sfd.mesh_display );
+ }
+ }
+
+ 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 );
+ bind_terrain_noise();
+ glActiveTexture( GL_TEXTURE1 );
+ glBindTexture( GL_TEXTURE_2D, world_sfd.tex_scoretex );
+
+ shader_scene_vertex_blend_uPv( cam->mtx.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; j<sm_card->vertex_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<w*h*2; i++ )
+ world_sfd.buffer[i] = 0.0f;
+}
+
+#endif /* WORLD_SFD_C */
#define SFD_H
#include "world.h"
-#include "shaders/scene_scoretext.h"
-#include "shaders/scene_vertex_blend.h"
-GLuint tex_scoretex;
-float 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;
+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<world_global.sfd.w; i++ ){
- u32 idx = (world_global.sfd.w*row_h + i) * 2;
-
- if( end ){
- world_global.sfd.buffer[idx] = 0.0f;
- }
- else{
- if( !str[i] )
- end = 1;
-
- world_global.sfd.buffer[idx] = sfd_encode_glyph( str[i] );
- }
- }
-}
-
-VG_STATIC void sfd_update(void)
-{
- for( int i=0; i<world_global.sfd.w*world_global.sfd.h; i++ ){
- float *target = &world_global.sfd.buffer[i*2+0],
- *cur = &world_global.sfd.buffer[i*2+1];
-
- float const rate = vg.time_delta * 15.2313131414f;
- float d1 = *target-*cur;
-
- if( fabsf(d1) > 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;y<world_global.sfd.h; y++ ){
- for( int x=0; x<world_global.sfd.w; x++ ){
- float value = world_global.sfd.buffer[(y*world_global.sfd.w+x)*2+1];
- shader_scene_scoretext_uInfo( (v3f){ x,y, value } );
- mesh_draw( &world_global.sfd.mesh_display );
- }
- }
-
- 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 );
- bind_terrain_noise();
- glActiveTexture( GL_TEXTURE1 );
- glBindTexture( GL_TEXTURE_2D, tex_scoretex );
-
- shader_scene_vertex_blend_uPv( cam->mtx.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; j<sm_card->vertex_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; i<w*h*2; i++ )
- world_global.sfd.buffer[i] = 0.0f;
-}
+VG_STATIC void sfd_render( world_instance *world, camera *cam,
+ m4x3f transform );
#endif /* SFD_H */
--- /dev/null
+#ifndef WORLD_VOLUMES_C
+#define WORLD_VOLUMES_C
+
+#include "world_volumes.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];
+}
+
+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 */
#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,
--- /dev/null
+/*
+ * 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; i<world->surface_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; i<world->surface_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 */
/*
- * 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; i<world->surface_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; i<world->surface_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 */