completed all glider stuff
authorhgn <hgodden00@gmail.com>
Thu, 8 Feb 2024 02:12:09 +0000 (02:12 +0000)
committerhgn <hgodden00@gmail.com>
Thu, 8 Feb 2024 02:12:09 +0000 (02:12 +0000)
18 files changed:
blender_export.py
ent_glider.c [new file with mode: 0644]
ent_glider.h [new file with mode: 0644]
ent_region.c
entity.c
entity.h
gui.h
maps_src/mp_mtzero/main.mdl
model.h
models_src/rs_icons.mdl
player_glide.c
player_glide.h
player_replay.c
world.h
world_entity.c
world_load.c
world_map.c
world_render.c

index 695fff05d7eee6303b7333e48ffd8b333c0936ed..421cf5d796e75ebbc8e21123f0ef080f80e66759 100644 (file)
@@ -43,13 +43,14 @@ sr_entity_list = [
    ('ent_miniworld',    'Mini World',     '', 22 ),
    ('ent_prop',         'Prop',           '', 23 ),
    ('ent_list',         'Entity List',    '', 24 ),
-   ('ent_region',       'Region',         '', 25 )
+   ('ent_region',       'Region',         '', 25 ),
+   ('ent_glider',       'Glider',         '', 26 ),
 ]
 
-MDL_VERSION_NR = 104
+MDL_VERSION_NR = 105
 SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_challenge', \
                    'ent_relay', 'ent_skateshop', 'ent_objective', 'ent_route',\
-                   'ent_miniworld', 'ent_region' ]
+                   'ent_miniworld', 'ent_region', 'ent_glider', 'ent_list' ]
 
 def get_entity_enum_id( alias ):
 #{
@@ -244,12 +245,26 @@ class union_file_audio_clip(Union):
                ("reserved",vg_audio_clip)]
 #}
 
+# NOTE: not really an entity. no reason for ent_ -- makes more sense as file_,
+#       but then again, too late to change because compat.
 class ent_audio_clip(Structure):
 #{
    _fields_ = [("_anon",union_file_audio_clip),
                ("probability",c_float)]
 #}
 
+class ent_list(Structure):
+#{
+   _fields_ = [("entity_ref_start",c_uint32),
+               ("entity_ref_count",c_uint32)]
+#}
+
+# used in ent_list
+class file_entity_ref(Structure):
+#{
+   _fields_ = [("index",c_uint32)]
+#}
+
 class ent_checkpoint(Structure):
 #{
    _fields_ = [("gate_index",c_uint16),
@@ -279,6 +294,14 @@ class ent_list(Structure):#{
    _fields_ = [("start",c_uint16),("count",c_uint16)]
 #}
 
+class ent_glider(Structure):#{
+   _fields_ = [("transform",mdl_transform),
+               ("flags",c_uint32),
+               ("cooldown",c_float)]
+   sr_functions = { 0: 'unlock',
+                    1: 'equip' }
+#}
+
 class ent_water(Structure):
 #{
    _fields_ = [("transform",mdl_transform),
@@ -528,7 +551,9 @@ class ent_region(Structure):#{
                ("submesh_start",c_uint32), ("submesh_count",c_uint32),
                ("pstr_title",c_uint32),
                ("flags",c_uint32),
-               ("zone_volume",c_uint32)]
+               ("zone_volume",c_uint32),
+               #105+
+               ("target0",c_uint32*2)]
    sr_functions = { 0: 'enter', 1: 'leave' }
 #}
 
@@ -1729,6 +1754,7 @@ def sr_compile( collection ):
    #}
 
    audio_clip_count = 0
+   entity_file_ref_count = 0
 
    for ent_type, arr in sr_compile.entities.items():#{
       print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
@@ -2003,6 +2029,8 @@ def sr_compile( collection ):
                   sr_compile_mesh_internal( obj )
             region.pstr_title = sr_compile_string( obj_data.title )
             region.zone_volume = sr_entity_id( obj_data.zone_volume )
+            region.target0[0] = sr_entity_id( obj_data.target0 )
+            region.target0[1] = obj_data.target0_event
             sr_ent_push( region )
          #}
          elif ent_type == 'ent_relay':#{
@@ -2018,6 +2046,27 @@ def sr_compile( collection ):
             relay.targets[3][1] = obj_data.target3_event
             sr_ent_push( relay )
          #}
+         # elif ent_type == 'ent_list':#{
+         #    lista = ent_list()
+         #    obj_data = obj.SR_data.ent_list[0]
+
+         #    lista.entity_ref_start = entity_file_ref_count
+         #    lista.entity_ref_count = len( obj_data.entities )
+         #    entity_file_ref_count += lista.entity_ref_count
+
+         #    for child in obj_data.entities:#{
+         #       reference_struct = file_entity_ref()
+         #       reference_struct.index = sr_entity_id( child.target )
+         #       sr_ent_push( reference_struct )
+         #    #}
+
+         #    sr_ent_push( lista )
+         # #}
+         elif ent_type == 'ent_glider':#{
+            glider = ent_glider()
+            compile_obj_transform( obj, glider.transform )
+            sr_ent_push( glider )
+         #}
          elif ent_type == 'ent_cubemap':#{
             cubemap = ent_cubemap()
             co = obj.matrix_world @ Vector((0,0,0))
@@ -3095,6 +3144,9 @@ class SR_OBJECT_ENT_LIST(bpy.types.PropertyGroup):#{
    #}
 #}
 
+class SR_OBJECT_ENT_GLIDER(bpy.types.PropertyGroup):#{
+   nothing: bpy.props.StringProperty()
+#}
 
 class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup):#{
    subtype: bpy.props.EnumProperty(
@@ -3532,6 +3584,18 @@ class SR_OBJECT_ENT_REGION(bpy.types.PropertyGroup):#{
    zone_volume: bpy.props.PointerProperty(
             type=bpy.types.Object, name="Zone Volume", \
             poll=lambda self,obj: sr_filter_ent_type(obj,['ent_volume']))
+
+   target0: bpy.props.PointerProperty( \
+            type=bpy.types.Object, name="Triger on unlock", \
+            poll=lambda self,obj: sr_filter_ent_type(obj,SR_TRIGGERABLE))
+   target0_event: bpy.props.IntProperty( name="Event/Method" )
+
+   @staticmethod
+   def sr_inspector( layout, data ):#{
+      layout.prop( data[0], 'title' )
+      layout.prop( data[0], 'zone_volume' )
+      SR_OBJECT_ENT_VOLUME.inspect_target( layout, data, 'target0' )
+   #}
 #}
 
 class SR_OBJECT_ENT_RELAY(bpy.types.PropertyGroup):#{
@@ -3586,6 +3650,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    ent_relay: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_RELAY)
    ent_miniworld: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MINIWORLD)
    ent_list: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_LIST)
+   ent_glider: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLIDER)
 
    ent_type: bpy.props.EnumProperty(
       name="Type",
@@ -4210,6 +4275,14 @@ def draw_skeleton_helpers( obj ):
    #}
 #}
 
+def cv_draw_wireframe( mdl, points, colour ):#{
+   for i in range(len(points)//2):#{
+      p0 = mdl@points[i*2+0]
+      p1 = mdl@points[i*2+1]
+      cv_draw_line( p0, p1, colour )
+   #}
+#}
+
 def cv_ent_gate( obj ):
 #{
    global cv_view_verts, cv_view_colours
@@ -4624,6 +4697,20 @@ def cv_draw():#{
                #}
             #}
          #}
+         elif ent_type == 'ent_glider':#{
+            mesh = [Vector((-1.13982, 0.137084, -0.026358)), \
+                    Vector(( 1.13982, 0.137084, -0.026358)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector(( 0.0, -3.0, 1.0)), \
+                    Vector(( -3.45, -1.78, 0.9)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector((  3.45, -1.78, 0.9)), \
+                    Vector(( 0.0, 1.6, 1.0)), \
+                    Vector((  3.45, -1.78, 0.9)), \
+                    Vector(( -3.45, -1.78, 0.9))]
+
+            cv_draw_wireframe( obj.matrix_world, mesh, (1,1,1) )
+         #}
          elif ent_type == 'ent_skateshop':#{
             data = obj.SR_data.ent_skateshop[0]
             display = data.mark_display
@@ -4691,6 +4778,22 @@ def cv_draw():#{
             if display1:
                cv_draw_ucube(display1.matrix_world, cc1, display_cu, display_co)
          #}
+         # elif ent_type == 'ent_list':#{
+         #    data = obj.SR_data.ent_list[0]
+         #    for child in data.entities:#{
+         #       if child.target:#{
+         #          cv_draw_arrow( obj.location, child.target.location, \
+         #                         (.5,.5,.5), 0.1 )
+         #       #}
+         #    #}
+         # #}
+         elif ent_type == 'ent_region':#{
+            data = obj.SR_data.ent_region[0]
+            if data.target0:#{
+               cv_draw_arrow( obj.location, data.target0.location, \
+                              (.5,.5,.5), 0.1 )
+            #}
+         #}
          elif ent_type == 'ent_menuitem':#{
             for i,col in enumerate(obj.users_collection):#{
                colour32 = hash_djb2( col.name )
@@ -4827,6 +4930,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
             SR_OBJECT_ENT_RELAY,SR_OBJECT_ENT_MINIWORLD,\
             SR_OBJECT_ENT_LIST_ENTRY, SR_UL_ENT_LIST, SR_OBJECT_ENT_LIST, \
             SR_OT_ENT_LIST_NEW_ITEM, SR_OT_ENT_LIST_DEL_ITEM,\
+            SR_OBJECT_ENT_GLIDER, \
             \
             SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, 
             SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \
diff --git a/ent_glider.c b/ent_glider.c
new file mode 100644 (file)
index 0000000..d1e2a36
--- /dev/null
@@ -0,0 +1,21 @@
+#pragma once
+#include "entity.h"
+#include "player_glide.h"
+
+static void ent_glider_call( world_instance *world, ent_call *call ){
+   u32 index = mdl_entity_id_id( call->id );
+   ent_glider *glider = mdl_arritm( &world->ent_glider, index );
+
+   if( call->function == 0 ){
+      glider->flags |= 0x1;
+   }
+   else if( call->function == 1 ){
+      if( glider->flags & 0x1 ){
+         player_glide_equip_glider();
+      }
+   }
+   else {
+      vg_print_backtrace();
+      vg_error( "Unhandled function id: %i\n", call->function );
+   }
+}
diff --git a/ent_glider.h b/ent_glider.h
new file mode 100644 (file)
index 0000000..e61bdd0
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+#include "entity.h"
+
+static void ent_glider_call( world_instance *world, ent_call *call );
index fd24f4e1cd3b6f3dc98bfc6b025475b011de76f7..44d49a096637bff1f4e6777aedb38e7820fcf12f 100644 (file)
@@ -113,6 +113,20 @@ static void ent_region_re_eval( world_instance *world ){
 
       region->flags = combined;
       world_total &= combined;
+
+      /* run unlock triggers. v105+ */
+      if( world->meta.info.version >= 105 ){
+         if( region->flags & (k_ent_route_flag_achieve_gold|
+                              k_ent_route_flag_achieve_silver) ){
+            if( region->target0[0] ){
+               ent_call call;
+               call.data = NULL;
+               call.id = region->target0[0];
+               call.function = region->target0[1];
+               entity_call( world, &call );
+            }
+         }
+      }
    }
 
    u32 instance_id = world - world_static.instances;
index 2ff0cab4f448241783850eb201a854560acd71a6..ce6d98eb76ecd959f572160c8f6c08f14b66d928 100644 (file)
--- a/entity.c
+++ b/entity.c
@@ -13,6 +13,7 @@
 #include "ent_miniworld.c"
 #include "ent_region.c"
 #include "ent_traffic.c"
+#include "ent_glider.c"
 
 typedef void (*fn_entity_call_handler)( world_instance *, ent_call *);
 
@@ -30,7 +31,8 @@ static void entity_call( world_instance *world, ent_call *call ){
       [k_ent_challenge] = ent_challenge_call,
       [k_ent_route]     = ent_route_call,
       [k_ent_miniworld] = ent_miniworld_call,
-      [k_ent_region]    = ent_region_call
+      [k_ent_region]    = ent_region_call,
+      [k_ent_glider]    = ent_glider_call
    };
 
    if( type >= vg_list_size(table) ){
index 1c11f38ce3ec3544d7c82ac935df6f71c09463ca..1adeb7e6095643d31c5e4654e02d60d13438f5e0 100644 (file)
--- a/entity.h
+++ b/entity.h
@@ -33,6 +33,8 @@ typedef struct ent_cubemap ent_cubemap;
 typedef struct ent_miniworld ent_miniworld;
 typedef struct ent_prop ent_prop;
 typedef struct ent_region ent_region;
+typedef struct ent_list ent_list;
+typedef struct ent_glider ent_glider;
 
 enum entity_alias{
    k_ent_none        = 0,
@@ -60,7 +62,8 @@ enum entity_alias{
    k_ent_miniworld   = 22,
    k_ent_prop        = 23,
    k_ent_list        = 24,
-   k_ent_region      = 25
+   k_ent_region      = 25,
+   k_ent_glider      = 26
 };
 
 static u32 mdl_entity_id_type( u32 entity_id ){
@@ -548,7 +551,16 @@ struct ent_prop {
 
 struct ent_region {
    mdl_transform transform;
-   u32 submesh_start, submesh_count, pstr_title, flags, zone_volume;
+   u32 submesh_start, submesh_count, pstr_title, flags, zone_volume,
+
+       /* 105+ */
+       target0[2];
+};
+
+struct ent_glider {
+   mdl_transform transform;
+   u32 flags;
+   f32 cooldown;
 };
 
 #include "world.h"
diff --git a/gui.h b/gui.h
index 1414bd587f6edb2513dba20a2dc0dd7da24c05d0..67f78b5df9829e4b66e7405cb1015e8cac3765cb 100644 (file)
--- a/gui.h
+++ b/gui.h
@@ -19,6 +19,7 @@ enum gui_icon {
    k_gui_icon_player,
    k_gui_icon_rift_run_gold,
    k_gui_icon_rift_run_silver,
+   k_gui_icon_glider,
 
    k_gui_icon_count,
 };
@@ -329,6 +330,7 @@ static void gui_init(void){
    gui.icons[ k_gui_icon_rift_run_2d ] = gui_find_icon( "icon_rift_run2d" );
    gui.icons[ k_gui_icon_friend ] = gui_find_icon( "icon_friend" );
    gui.icons[ k_gui_icon_player ] = gui_find_icon( "icon_player" );
+   gui.icons[ k_gui_icon_glider ] = gui_find_icon( "icon_glider" );
    gui.icons[ k_gui_icon_rift_run_gold ] =
       gui_find_icon("icon_rift_run_medal_gold");
    gui.icons[ k_gui_icon_rift_run_silver]=
index 76139c8b6bbdce0b417c8e8813377a7f68687a42..b743ebe1d3ad25a49fceff01160248b8f02c06da 100644 (file)
Binary files a/maps_src/mp_mtzero/main.mdl and b/maps_src/mp_mtzero/main.mdl differ
diff --git a/model.h b/model.h
index 62438dcf0f84f4924fa0a449677cb9b715ff5f68..ba1c3f228c8cc827176bc9a8b4a92126c82cba8a 100644 (file)
--- a/model.h
+++ b/model.h
@@ -8,7 +8,7 @@
 #include "skaterift.h"
 
 #define MDL_VERSION_MIN 101
-#define MDL_VERSION_NR 104
+#define MDL_VERSION_NR 105
 
 enum mdl_shader{
    k_shader_standard                = 0,
index e3ee8b5d351052d3637f8ae815721cc0145691d0..7dec1221f5716be7830cc5e05801cbd1ec261b34 100644 (file)
Binary files a/models_src/rs_icons.mdl and b/models_src/rs_icons.mdl differ
index 331565c58f8b58e4eb982ce22afe7ab7df2528d6..af44ea6e806ed6f30088f811d679fc1e841c271c 100644 (file)
@@ -289,10 +289,21 @@ static void player_glide_im_gui(void){
                            player_glide.info_drag[2] );
 }
 
+static void player_glide_equip_glider(void){
+   if( !localplayer.have_glider ){
+      localplayer.have_glider = 1;
+      localplayer.glider_orphan = 0;
+      player_glide.t = -1.0f;
+   }
+}
+
 static int ccmd_player_glider_spawn( int argc, const char *argv[] ){
-   localplayer.have_glider = 1;
-   localplayer.glider_orphan = 0;
-   player_glide.t = -1.0f;
+   if( vg_console.cheats ){
+      player_glide_equip_glider();
+   }
+   else {
+      vg_error( "Can't spawn without cheats enabled.\n" );
+   }
    return 0;
 }
 
index fdf34bcabb5f2a80ddb4fa2529ada3a8b1684c24..f8f6dd99dffee4de2a9d85f47d91c8c6b025826f 100644 (file)
@@ -122,6 +122,7 @@ static void render_glider_model( camera *cam, world_instance *world,
                                  m4x3f mmdl, enum board_shader shader );
 static void 
 player_glide_remote_animator_exchange( bitpack_ctx *ctx, void *data );
+static void player_glide_equip_glider(void);
 
 struct player_subsystem_interface static player_subsystem_glide = {
    .pre_update = player_glide_pre_update,
index a013f7ce4059dc9222adb2145ec2f9c919def399..f5a2ab9327ab03c9b7c0705c89596316d758d4ba 100644 (file)
@@ -427,10 +427,10 @@ void skaterift_restore_frame( replay_frame *frame ){
       rb_update_matrices( rb );
    }
 
+   localplayer.subsystem = frame->system;
+
    /* restore the seperated glider data if we have it */
    if( frame->data_table[ k_replay_framedata_glider ][1] ){
-      localplayer.subsystem = frame->system;
-
       struct replay_glider_data *inf = 
          replay_frame_data( frame, k_replay_framedata_glider );
 
diff --git a/world.h b/world.h
index 9e9387b658d9eefc05239da2e8549f5706c42059..6ff3afbc00f295d7a459eec32d0eff75bcc7e719 100644 (file)
--- a/world.h
+++ b/world.h
@@ -188,7 +188,8 @@ struct world_instance {
                  ent_cubemap,
                  ent_miniworld,
                  ent_prop,
-                 ent_region;
+                 ent_region,
+                 ent_glider;
 
    enum skybox {
       k_skybox_default,
index 440229102ecc8d548635c70dc1d100b5af76b186..831e7eb674c4c728d0be1b01c17ccf0635370d25 100644 (file)
@@ -12,6 +12,7 @@
 #include "ent_skateshop.h"
 #include "ent_route.h"
 #include "ent_traffic.h"
+#include "ent_glider.h"
 
 static void world_entity_focus( u32 entity_id ){
    localplayer.immobile = 1;
@@ -194,7 +195,8 @@ static void world_gen_entities_init( world_instance *world ){
       { k_ent_gate, &world->ent_gate },
       { k_ent_objective, &world->ent_objective },
       { k_ent_volume, &world->ent_volume },
-      { k_ent_challenge, &world->ent_challenge }
+      { k_ent_challenge, &world->ent_challenge },
+      { k_ent_glider, &world->ent_glider }
    };
 
    for( u32 i=0; i<vg_list_size(indexables); i++ )
@@ -482,6 +484,13 @@ entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
       mdl_transform_m4x3( &challenge->transform, transform );
       m4x3_expand_aabb_aabb( transform, bound, box );
    }
+   else if( type == k_ent_glider ){
+      ent_glider *glider = mdl_arritm( &world->ent_glider, index );
+      m4x3f transform;
+      mdl_transform_m4x3( &glider->transform, transform );
+      m4x3_expand_aabb_aabb( transform, bound,
+                            (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
+   }
    else{
       vg_fatal_error( "Programming error\n" );
    }
@@ -510,6 +519,10 @@ static float entity_bh_centroid( void *user, u32 item_index, int axis ){
       ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
       return challenge->transform.co[axis];
    }
+   else if( type == k_ent_glider ){
+      ent_glider *glider = mdl_arritm( &world->ent_glider, index );
+      return glider->transform.co[axis];
+   }
    else {
       vg_fatal_error( "Programming error\n" );
       return INFINITY;
index 6055d887acbe96ed7b7a5d68b53a2fc52765a2b3..ece92972c080625f6390e4252640363355572921 100644 (file)
@@ -69,6 +69,7 @@ static void world_instance_load_mdl( u32 instance_id, const char *path ){
    MDL_LOAD_ARRAY( meta, &world->ent_miniworld, ent_miniworld,  heap );
    MDL_LOAD_ARRAY( meta, &world->ent_prop,      ent_prop,       heap );
    MDL_LOAD_ARRAY( meta, &world->ent_region,    ent_region,     heap );
+   MDL_LOAD_ARRAY( meta, &world->ent_glider,    ent_glider,     heap );
 
    mdl_array_ptr infos;
    MDL_LOAD_ARRAY( meta, &infos, ent_worldinfo, vg_mem.scratch );
index e2fbe7a2f8dc83f8b76dca47251ff76897743ab6..c7f6ec82ce4dbb249abf9937c06b75f57f4ec2e4 100644 (file)
@@ -173,6 +173,18 @@ static void world_map_pre_update(void){
       respawn_map_draw_icon( cam, k_gui_icon_rift_run_2d, 
                              route->board_transform[3] );
    }
+
+   for( u32 i=0; i<mdl_arrcount(&world->ent_glider); i ++ ){
+      ent_glider *glider = mdl_arritm( &world->ent_glider, i );
+
+      v4f colour = { 1,1,1,1 };
+
+      if( !(glider->flags & 0x1) )
+         v3_muls( colour, 0.5f, colour );
+      gui_icon_setcolour( colour );
+
+      respawn_map_draw_icon( cam, k_gui_icon_glider, glider->transform.co );
+   }
 }
 
 static void world_map_enter(void){
index 103304196783e864dbdd7fe8e3e9600c6d011c01..2355e20bf204bd942870515167bbe75d2366e502 100644 (file)
@@ -13,6 +13,7 @@
 #include "ent_miniworld.h"
 #include "player_remote.h"
 #include "ent_skateshop.h"
+#include "shaders/model_entity.h"
 
 static int ccmd_set_time( int argc, const char *argv[] ){
    world_instance *world = world_current_instance();
@@ -891,6 +892,50 @@ static void world_prerender( world_instance *world ){
                     sizeof(struct ub_world_lighting), &world->ub_lighting );
 }
 
+static void render_other_entities( world_instance *world, camera *cam ){
+   f32 radius = 40.0f;
+   bh_iter it;
+   bh_iter_init_range( 0, &it, cam->pos, radius+10.0f );
+
+   u32 glider_list[4],
+       glider_count = 0;
+
+   i32 idx;
+   while( bh_next( world->entity_bh, &it, &idx ) ){
+      u32 id    = world->entity_list[ idx ],
+          type  = mdl_entity_id_type( id ),
+          index = mdl_entity_id_id( id );
+
+      if( type == k_ent_glider ) {
+         if( glider_count < vg_list_size(glider_list) )
+            glider_list[ glider_count ++ ] = index;
+      }
+   }
+
+   shader_model_entity_use();
+   shader_model_entity_uTexMain( 0 );
+   shader_model_entity_uCamera( cam->transform[3] );
+   shader_model_entity_uPv( cam->mtx.pv );
+   
+   WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_entity );
+
+   for( u32 j=0; j<glider_count; j ++ ){
+      ent_glider *glider = mdl_arritm( &world->ent_glider, glider_list[j] );
+
+      if( !(glider->flags & 0x1) )
+         continue;
+
+      m4x3f mdl;
+      mdl_transform_m4x3( &glider->transform, mdl );
+
+      f32 dist  = v3_dist( glider->transform.co, cam->pos ) * (1.0f/radius),
+          scale = vg_smoothstepf( vg_clampf( 5.0f-dist*5.0f, 0.0f,1.0f ) );
+      m3x3_scalef( mdl, scale );
+
+      render_glider_model( cam, world, mdl, k_board_shader_entity );
+   }
+}
+
 static void render_world( world_instance *world, camera *cam,
                           int stenciled, int viewing_from_gate, 
                           int with_water, int with_cubemaps ){
@@ -987,6 +1032,7 @@ static void render_world( world_instance *world, camera *cam,
    }
 
    render_remote_players( world, cam );
+   render_other_entities( world, cam );
    ent_miniworld_render( world, cam );
 
    if( stenciled ){