2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
9 #include "world_render.h"
12 #include "world_map.h"
13 #include "ent_miniworld.h"
14 #include "player_remote.h"
15 #include "ent_skateshop.h"
16 #include "shaders/model_entity.h"
18 static int ccmd_set_time( int argc
, const char *argv
[] ){
19 world_instance
*world
= world_current_instance();
21 world
->time
= atof( argv
[0] );
23 vg_error( "Usage set_time <0-1.0> (current time: %f)\n", world
->time
);
27 static void async_world_render_init( void *payload
, u32 size
){
28 vg_info( "Allocate uniform buffers\n" );
29 for( int i
=0; i
<k_world_max
; i
++ ){
30 world_instance
*world
= &world_static
.instances
[i
];
31 world
->ubo_bind_point
= i
;
33 glGenBuffers( 1, &world
->ubo_lighting
);
34 glBindBuffer( GL_UNIFORM_BUFFER
, world
->ubo_lighting
);
35 glBufferData( GL_UNIFORM_BUFFER
, sizeof(struct ub_world_lighting
),
36 NULL
, GL_DYNAMIC_DRAW
);
38 glBindBufferBase( GL_UNIFORM_BUFFER
, i
, world
->ubo_lighting
);
42 vg_info( "Allocate frame buffers\n" );
43 for( int i
=0; i
<k_world_max
; i
++ ){
44 world_instance
*world
= &world_static
.instances
[i
];
45 struct framebuffer
*fb
= &world
->heightmap
;
47 fb
->display_name
= NULL
;
51 fb
->resolution_div
= 0;
53 fb
->attachments
[0].display_name
= NULL
;
54 fb
->attachments
[0].purpose
= k_framebuffer_attachment_type_texture
;
55 fb
->attachments
[0].internalformat
= GL_RG16F
;
56 fb
->attachments
[0].format
= GL_RG
;
57 fb
->attachments
[0].type
= GL_FLOAT
;
58 fb
->attachments
[0].attachment
= GL_COLOR_ATTACHMENT0
;
60 fb
->attachments
[1].purpose
= k_framebuffer_attachment_type_none
;
61 fb
->attachments
[2].purpose
= k_framebuffer_attachment_type_none
;
62 fb
->attachments
[3].purpose
= k_framebuffer_attachment_type_none
;
63 fb
->attachments
[4].purpose
= k_framebuffer_attachment_type_none
;
65 render_fb_allocate( fb
);
69 static void world_render_init(void){
70 VG_VAR_F32( k_day_length
);
71 VG_VAR_I32( k_debug_light_indices
);
72 VG_VAR_I32( k_debug_light_complexity
);
73 VG_VAR_I32( k_light_preview
);
74 VG_VAR_I32( k_light_editor
);
75 vg_console_reg_cmd( "set_time", ccmd_set_time
, NULL
);
77 world_render
.sky_rate
= 1.0;
78 world_render
.sky_target_rate
= 1.0;
80 shader_scene_standard_register();
81 shader_scene_standard_alphatest_register();
82 shader_scene_foliage_register();
83 shader_scene_override_register();
84 shader_scene_cubemapped_register();
85 shader_scene_fxglow_register();
86 shader_scene_vertex_blend_register();
87 shader_scene_terrain_register();
88 shader_scene_depth_register();
89 shader_scene_position_register();
90 shader_model_sky_register();
91 shader_model_sky_space_register();
93 vg_info( "Loading world resources\n" );
94 vg_linear_clear( vg_mem
.scratch
);
97 mdl_open( &msky
, "models/rs_skydome.mdl", vg_mem
.scratch
);
98 mdl_load_metadata_block( &msky
, vg_mem
.scratch
);
99 mdl_async_load_glmesh( &msky
, &world_render
.skydome
, NULL
);
102 vg_info( "Loading default world textures\n" );
103 vg_tex2d_load_qoi_async_file( "textures/garbage.qoi",
104 VG_TEX2D_NEAREST
|VG_TEX2D_REPEAT
,
105 &world_render
.tex_terrain_noise
);
107 vg_async_call( async_world_render_init
, NULL
, 0 );
111 * standard uniform bindings
112 * ----------------------------------------------------------------------------
114 static void world_link_lighting_ub( world_instance
*world
, GLuint shader
){
115 GLuint idx
= glGetUniformBlockIndex( shader
, "ub_world_lighting" );
116 glUniformBlockBinding( shader
, idx
, world
->ubo_bind_point
);
119 static void world_bind_position_texture( world_instance
*world
,
120 GLuint shader
, GLuint location
,
122 render_fb_bind_texture( &world
->heightmap
, 0, slot
);
123 glUniform1i( location
, slot
);
126 static void world_bind_light_array( world_instance
*world
,
127 GLuint shader
, GLuint location
,
129 glActiveTexture( GL_TEXTURE0
+ slot
);
130 glBindTexture( GL_TEXTURE_BUFFER
, world
->tex_light_entities
);
131 glUniform1i( location
, slot
);
134 static void world_bind_light_index( world_instance
*world
,
135 GLuint shader
, GLuint location
,
137 glActiveTexture( GL_TEXTURE0
+ slot
);
138 glBindTexture( GL_TEXTURE_3D
, world
->tex_light_cubes
);
139 glUniform1i( location
, slot
);
142 static void bind_terrain_noise(void){
143 glActiveTexture( GL_TEXTURE0
);
144 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
148 * Get OpenGL texture name from texture ID.
150 static GLuint
world_get_texture( world_instance
*world
, u32 id
){
151 if( id
& 0x80000000 ) return skaterift
.rt_textures
[id
& ~0x80000000];
152 else return world
->textures
[ id
];
155 static void bindpoint_diffuse_texture1( world_instance
*world
,
156 struct world_surface
*mat
){
157 glActiveTexture( GL_TEXTURE1
);
158 glBindTexture( GL_TEXTURE_2D
,
159 world_get_texture(world
,mat
->info
.tex_diffuse
) );
164 * ----------------------------------------------------------------------------
169 enum mdl_shader shader
;
170 enum world_geo_type geo_type
;
172 void (*fn_bind_textures
)( world_instance
*world
,
173 struct world_surface
*mat
);
174 void (*fn_set_mdl
)( m4x3f mdl
);
175 void (*fn_set_uPvmPrev
)( m4x4f pvm
);
176 void (*fn_set_uNormalMtx
)( m3x3f mnorm
);
179 static void render_world_depth( world_instance
*world
, camera
*cam
);
182 * Render a run of submeshes, only of those which match material_id
184 static void world_render_submeshes( world_instance
*world
,
185 struct world_pass
*pass
,
186 mdl_transform
*transform
,
187 u32 start
, u32 count
, u32 material_id
){
188 for( u32 k
=0; k
<count
; k
++ ){
189 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
, start
+k
);
190 if( sm
->material_id
!= material_id
) continue;
193 mdl_transform_m4x3( transform
, mmdl
);
196 m4x3_expand( mmdl
, m4mdl
);
197 m4x4_mul( pass
->cam
->mtx_prev
.pv
, m4mdl
, m4mdl
);
199 pass
->fn_set_mdl( mmdl
);
200 pass
->fn_set_uPvmPrev( m4mdl
);
202 mdl_draw_submesh( sm
);
207 * Render props attached to this material
209 static void world_render_props( world_instance
*world
, u32 material_id
,
210 struct world_pass
*pass
){
211 struct world_surface
*mat
= &world
->surfaces
[ material_id
];
212 if( !(mat
->flags
& WORLD_SURFACE_HAS_PROPS
) ) return;
214 pass
->fn_bind_textures( world
, mat
);
216 for( u32 j
=0; j
<mdl_arrcount( &world
->ent_prop
); j
++ ){
217 ent_prop
*prop
= mdl_arritm( &world
->ent_prop
, j
);
218 if( prop
->flags
& 0x1 ) continue;
220 world_render_submeshes( world
, pass
, &prop
->transform
,
221 prop
->submesh_start
, prop
->submesh_count
, material_id
);
226 * Render traffic models attactched to this material
228 static void world_render_traffic( world_instance
*world
, u32 material_id
,
229 struct world_pass
*pass
){
230 struct world_surface
*mat
= &world
->surfaces
[ material_id
];
231 if( !(mat
->flags
& WORLD_SURFACE_HAS_TRAFFIC
) ) return;
233 pass
->fn_bind_textures( world
, mat
);
235 for( u32 j
=0; j
<mdl_arrcount( &world
->ent_traffic
); j
++ ){
236 ent_traffic
*traffic
= mdl_arritm( &world
->ent_traffic
, j
);
238 world_render_submeshes( world
, pass
, &traffic
->transform
,
239 traffic
->submesh_start
, traffic
->submesh_count
,
245 * Iterate and render all materials which match the passes shader and geometry
246 * type. Includes props/traffic.
248 static void world_render_pass( world_instance
*world
, struct world_pass
*pass
){
249 for( int i
=0; i
<world
->surface_count
; i
++ ){
250 struct world_surface
*mat
= &world
->surfaces
[i
];
252 if( mat
->info
.shader
== pass
->shader
){
255 if( pass
->geo_type
== k_world_geo_type_solid
){
259 world_render_traffic( world
, i
, pass
);
260 world_render_props( world
, i
, pass
);
261 sm
= &mat
->sm_no_collide
;
264 if( !sm
->indice_count
)
268 m4x3_identity( mmdl
);
269 pass
->fn_set_mdl( mmdl
);
270 pass
->fn_set_uPvmPrev( pass
->cam
->mtx_prev
.pv
);
272 pass
->fn_bind_textures( world
, mat
);
273 mdl_draw_submesh( sm
);
279 * Specific shader instructions
280 * ----------------------------------------------------------------------------
283 static void world_render_both_stages( world_instance
*world
,
284 struct world_pass
*pass
){
285 mesh_bind( &world
->mesh_geo
);
286 pass
->geo_type
= k_world_geo_type_solid
;
287 world_render_pass( world
, pass
);
289 glDisable( GL_CULL_FACE
);
290 mesh_bind( &world
->mesh_no_collide
);
291 pass
->geo_type
= k_world_geo_type_nonsolid
;
292 world_render_pass( world
, pass
);
293 glEnable( GL_CULL_FACE
);
296 static void render_world_vb( world_instance
*world
, camera
*cam
){
297 shader_scene_vertex_blend_use();
298 shader_scene_vertex_blend_uTexGarbage(0);
299 shader_scene_vertex_blend_uTexGradients(1);
300 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_vertex_blend
);
302 glActiveTexture( GL_TEXTURE0
);
303 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
305 shader_scene_vertex_blend_uPv( cam
->mtx
.pv
);
306 shader_scene_vertex_blend_uCamera( cam
->transform
[3] );
308 struct world_pass pass
= {
309 .shader
= k_shader_standard_vertex_blend
,
311 .fn_bind_textures
= bindpoint_diffuse_texture1
,
312 .fn_set_mdl
= shader_scene_vertex_blend_uMdl
,
313 .fn_set_uPvmPrev
= shader_scene_vertex_blend_uPvmPrev
,
316 world_render_both_stages( world
, &pass
);
319 static void world_shader_standard_bind( world_instance
*world
, camera
*cam
){
320 shader_scene_standard_use();
321 shader_scene_standard_uTexGarbage(0);
322 shader_scene_standard_uTexMain(1);
323 shader_scene_standard_uPv( cam
->mtx
.pv
);
324 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_standard
);
326 bind_terrain_noise();
327 shader_scene_standard_uCamera( cam
->transform
[3] );
330 static void render_world_standard( world_instance
*world
, camera
*cam
){
331 world_shader_standard_bind( world
, cam
);
332 struct world_pass pass
= {
333 .shader
= k_shader_standard
,
335 .fn_bind_textures
= bindpoint_diffuse_texture1
,
336 .fn_set_mdl
= shader_scene_standard_uMdl
,
337 .fn_set_uPvmPrev
= shader_scene_standard_uPvmPrev
,
340 world_render_both_stages( world
, &pass
);
343 static void bindpoint_diffuse1_and_cubemap10( world_instance
*world
,
344 struct world_surface
*mat
){
345 glActiveTexture( GL_TEXTURE1
);
346 glBindTexture( GL_TEXTURE_2D
,
347 world_get_texture(world
,mat
->info
.tex_diffuse
) );
349 u32 cubemap_id
= mat
->info
.tex_none0
,
352 if( mdl_entity_id_type( cubemap_id
) == k_ent_cubemap
){
353 cubemap_index
= mdl_entity_id_id( cubemap_id
);
356 ent_cubemap
*cm
= mdl_arritm( &world
->ent_cubemap
, cubemap_index
);
357 glActiveTexture( GL_TEXTURE10
);
358 glBindTexture( GL_TEXTURE_CUBE_MAP
, cm
->texture_id
);
360 shader_scene_cubemapped_uColour( mat
->info
.colour
);
363 static void render_world_cubemapped( world_instance
*world
, camera
*cam
,
365 if( !mdl_arrcount( &world
->ent_cubemap
) )
369 world_shader_standard_bind( world
, cam
);
371 struct world_pass pass
= {
372 .shader
= k_shader_cubemap
,
374 .fn_bind_textures
= bindpoint_diffuse_texture1
,
375 .fn_set_mdl
= shader_scene_standard_uMdl
,
376 .fn_set_uPvmPrev
= shader_scene_standard_uPvmPrev
,
379 world_render_both_stages( world
, &pass
);
382 shader_scene_cubemapped_use();
383 shader_scene_cubemapped_uTexGarbage(0);
384 shader_scene_cubemapped_uTexMain(1);
385 shader_scene_cubemapped_uTexCubemap(10);
386 shader_scene_cubemapped_uPv( cam
->mtx
.pv
);
388 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_cubemapped
);
390 bind_terrain_noise();
391 shader_scene_cubemapped_uCamera( cam
->transform
[3] );
393 struct world_pass pass
= {
394 .shader
= k_shader_cubemap
,
396 .fn_bind_textures
= bindpoint_diffuse1_and_cubemap10
,
397 .fn_set_mdl
= shader_scene_cubemapped_uMdl
,
398 .fn_set_uPvmPrev
= shader_scene_cubemapped_uPvmPrev
,
401 world_render_both_stages( world
, &pass
);
405 static void render_world_alphatest( world_instance
*world
, camera
*cam
){
406 shader_scene_standard_alphatest_use();
407 shader_scene_standard_alphatest_uTexGarbage(0);
408 shader_scene_standard_alphatest_uTexMain(1);
409 shader_scene_standard_alphatest_uPv( cam
->mtx
.pv
);
411 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_standard_alphatest
);
413 bind_terrain_noise();
414 shader_scene_standard_alphatest_uCamera( cam
->transform
[3] );
415 glDisable(GL_CULL_FACE
);
417 struct world_pass pass
= {
418 .shader
= k_shader_standard_cutout
,
420 .fn_bind_textures
= bindpoint_diffuse_texture1
,
421 .fn_set_mdl
= shader_scene_standard_alphatest_uMdl
,
422 .fn_set_uPvmPrev
= shader_scene_standard_alphatest_uPvmPrev
,
425 world_render_both_stages( world
, &pass
);
426 glEnable(GL_CULL_FACE
);
429 static void render_world_foliage( world_instance
*world
, camera
*cam
){
430 shader_scene_foliage_use();
431 shader_scene_foliage_uTexGarbage(0);
432 shader_scene_foliage_uTexMain(1);
433 shader_scene_foliage_uPv( cam
->mtx
.pv
);
434 shader_scene_foliage_uTime( vg
.time
);
436 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_foliage
);
437 bind_terrain_noise();
439 shader_scene_foliage_uCamera( cam
->transform
[3] );
440 glDisable(GL_CULL_FACE
);
441 struct world_pass pass
= {
442 .shader
= k_shader_foliage
,
444 .fn_bind_textures
= bindpoint_diffuse_texture1
,
445 .fn_set_mdl
= shader_scene_foliage_uMdl
,
446 .fn_set_uPvmPrev
= shader_scene_foliage_uPvmPrev
,
448 world_render_both_stages( world
, &pass
);
449 glEnable(GL_CULL_FACE
);
452 static void world_render_challenges( world_instance
*world
,
453 struct world_pass
*pass
, v3f pos
){
455 if( skaterift
.activity
== k_skaterift_replay
) return;
456 if( world
!= world_current_instance() ) return;
461 u32 objective_list
[ 32 ],
462 challenge_list
[ 16 ];
464 v2f objective_uv_offsets
[ 32 ];
466 u32 objective_count
= 0,
469 ent_challenge
*active_challenge
= NULL
;
471 if( mdl_entity_id_type( world_static
.focused_entity
) == k_ent_challenge
){
472 if( (skaterift
.activity
== k_skaterift_default
) &&
473 world_static
.challenge_target
){
477 if( !((skaterift
.activity
!= k_skaterift_ent_focus
) &&
478 !world_static
.challenge_target
) ){
479 world_instance
*challenge_world
= world_current_instance();
480 u32 index
= mdl_entity_id_id( world_static
.focused_entity
);
481 active_challenge
= mdl_arritm(&challenge_world
->ent_challenge
, index
);
485 if( active_challenge
){
486 shader_scene_fxglow_uUvOffset( (v2f
){ 8.0f
/256.0f
, 0.0f
} );
487 challenge_list
[ challenge_count
++ ] = world_static
.focused_entity
;
489 u32 next
= active_challenge
->first
;
490 while( mdl_entity_id_type(next
) == k_ent_objective
){
491 u32 index
= mdl_entity_id_id( next
);
492 objective_list
[ objective_count
++ ] = index
;
494 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
495 next
= objective
->id_next
;
501 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
503 bh_iter_init_range( 0, &it
, pos
, radius
+10.0f
);
505 while( bh_next( world
->entity_bh
, &it
, &idx
) ){
506 u32 id
= world
->entity_list
[ idx
],
507 type
= mdl_entity_id_type( id
),
508 index
= mdl_entity_id_id( id
);
510 if( type
== k_ent_objective
) {
511 if( objective_count
< vg_list_size(objective_list
) )
512 objective_list
[ objective_count
++ ] = index
;
514 else if( type
== k_ent_challenge
){
515 if( challenge_count
< vg_list_size(challenge_list
) )
516 challenge_list
[ challenge_count
++ ] = index
;
521 /* render objectives */
522 glDisable( GL_CULL_FACE
);
523 mesh_bind( &world
->mesh_no_collide
);
524 u32 last_material
= 0;
525 for( u32 i
=0; i
<objective_count
; i
++ ){
526 u32 index
= objective_list
[ i
];
527 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
528 if( (objective
->flags
& k_ent_objective_hidden
) &&
529 !active_challenge
) continue;
534 u32 passed
= objective
->flags
& k_ent_objective_passed
;
535 f32 target
= passed
? 0.0f
: 1.0f
;
536 vg_slewf(&objective
->transform
.s
[0], target
, vg
.time_frame_delta
*4.0f
);
537 scale
= vg_smoothstepf( objective
->transform
.s
[0] );
539 if( (objective
== world_static
.challenge_target
) || passed
)
540 shader_scene_fxglow_uUvOffset( (v2f
){ 16.0f
/256.0f
, 0.0f
} );
542 shader_scene_fxglow_uUvOffset( (v2f
){ 8.0f
/256.0f
, 0.0f
} );
545 f32 dist
= v3_dist( objective
->transform
.co
, pos
) * (1.0f
/radius
);
546 scale
= vg_smoothstepf( vg_clampf( 5.0f
-dist
*5.0f
, 0.0f
,1.0f
) );
550 q_m3x3( objective
->transform
.q
, mmdl
);
551 m3x3_scalef( mmdl
, scale
);
552 v3_copy( objective
->transform
.co
, mmdl
[3] );
553 shader_scene_fxglow_uMdl( mmdl
);
555 for( u32 j
=0; j
<objective
->submesh_count
; j
++ ){
556 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
557 objective
->submesh_start
+ j
);
559 if( sm
->material_id
!= last_material
){
560 last_material
= sm
->material_id
;
561 pass
->fn_bind_textures( world
, &world
->surfaces
[sm
->material_id
] );
563 mdl_draw_submesh( sm
);
568 font3d_bind( &gui
.font
, k_font_shader_world
, 0, world
, &skaterift
.cam
);
572 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ )
574 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, i
);
575 if( challenge
->status
) count
++;
580 vg_strnull( &str
, buf
, sizeof(buf
) );
581 vg_strcati32( &str
, count
);
582 vg_strcatch( &str
, '/' );
583 vg_strcati32( &str
, mdl_arrcount(&world
->ent_challenge
) );
585 f32 w
= font3d_string_width( 1, buf
);
587 m3x3_identity( mlocal
);
588 mlocal
[3][0] = -w
*0.5f
;
592 for( u32 i
=0; i
<challenge_count
; i
++ )
594 u32 index
= challenge_list
[ i
];
595 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
597 mdl_transform_m4x3( &challenge
->transform
, mmdl
);
598 m4x3_mul( mmdl
, mlocal
, mmdl
);
600 vg_line_point( challenge
->transform
.co
, 0.25f
, VG__RED
);
602 f32 dist
= v3_dist( challenge
->transform
.co
, pos
) * (1.0f
/radius
),
603 scale
= vg_smoothstepf( vg_clampf( 10.0f
-dist
*10.0f
, 0.0f
,1.0f
) ),
606 if( challenge
->status
)
609 shader_scene_font_uOpacity( scale
);
610 shader_scene_font_uColourize( colour
);
611 font3d_simple_draw( 1, buf
, &skaterift
.cam
, mmdl
);
615 static void render_world_fxglow( world_instance
*host_world
,
616 world_instance
*world
, camera
*cam
,
618 int generic
, int challenges
, int regions
){
619 shader_scene_fxglow_use();
620 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
621 shader_scene_fxglow_uTexMain(1);
622 shader_scene_fxglow_uPv( cam
->mtx
.pv
);
623 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_fxglow
);
625 shader_scene_fxglow_uCamera( cam
->transform
[3] );
626 glDisable(GL_CULL_FACE
);
628 struct world_pass pass
= {
629 .shader
= k_shader_fxglow
,
631 .fn_bind_textures
= bindpoint_diffuse_texture1
,
632 .fn_set_mdl
= shader_scene_fxglow_uMdl
,
633 .fn_set_uPvmPrev
= shader_scene_fxglow_uPvmPrev
,
637 world_render_both_stages( world
, &pass
);
640 mesh_bind( &world
->mesh_no_collide
);
642 u32 last_material
= 0;
643 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_region
); i
++ ){
644 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
645 ent_region
*region
= mdl_arritm( &world
->ent_region
, i
);
648 if( region
->flags
& k_ent_route_flag_achieve_gold
)
650 else if( region
->flags
& k_ent_route_flag_achieve_silver
)
653 shader_scene_fxglow_uUvOffset( (v2f
){ (8.0f
/256.0f
)*offset
, 0.0f
} );
656 mdl_transform_m4x3( ®ion
->transform
, mmdl
);
657 m4x3_mul( world_mmdl
, mmdl
, mmdl
);
658 shader_scene_fxglow_uMdl( mmdl
);
660 for( u32 j
=0; j
<region
->submesh_count
; j
++ ){
661 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
662 region
->submesh_start
+ j
);
664 if( sm
->material_id
!= last_material
){
665 last_material
= sm
->material_id
;
666 pass
.fn_bind_textures(world
,&world
->surfaces
[sm
->material_id
]);
668 mdl_draw_submesh( sm
);
674 world_render_challenges( world
, &pass
, cam
->pos
);
676 glEnable(GL_CULL_FACE
);
679 static void bindpoint_terrain( world_instance
*world
,
680 struct world_surface
*mat
)
682 glActiveTexture( GL_TEXTURE1
);
683 glBindTexture( GL_TEXTURE_2D
,
684 world_get_texture(world
,mat
->info
.tex_diffuse
) );
686 shader_scene_terrain_uSandColour( mat
->info
.colour
);
687 shader_scene_terrain_uBlendOffset( mat
->info
.colour1
);
690 static void bindpoint_override( world_instance
*world
,
691 struct world_surface
*mat
){
692 if( mat
->info
.flags
& k_material_flag_collision
){
693 shader_scene_override_uAlphatest(0);
696 glActiveTexture( GL_TEXTURE1
);
697 glBindTexture( GL_TEXTURE_2D
,
698 world_get_texture(world
,mat
->info
.tex_diffuse
) );
699 shader_scene_override_uAlphatest(1);
703 static void render_terrain( world_instance
*world
, camera
*cam
){
704 shader_scene_terrain_use();
705 shader_scene_terrain_uTexGarbage(0);
706 shader_scene_terrain_uTexGradients(1);
708 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_terrain
);
709 glActiveTexture( GL_TEXTURE0
);
710 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
712 shader_scene_terrain_uPv( cam
->mtx
.pv
);
713 shader_scene_terrain_uCamera( cam
->transform
[3] );
715 struct world_pass pass
= {
716 .shader
= k_shader_terrain_blend
,
718 .fn_bind_textures
= bindpoint_terrain
,
719 .fn_set_mdl
= shader_scene_terrain_uMdl
,
720 .fn_set_uPvmPrev
= shader_scene_terrain_uPvmPrev
,
723 world_render_both_stages( world
, &pass
);
726 static void render_sky( world_instance
*world
, camera
*cam
){
728 * Modify matrix to remove clipping and view translation
735 m4x4_copy( cam
->mtx
.v
, v
);
736 m4x4_copy( cam
->mtx_prev
.v
, v_prev
);
738 for( int i
=0; i
<3; i
++ ){
740 v3_normalize(v_prev
[i
]);
743 v3_zero( v_prev
[3] );
745 m4x4_copy( cam
->mtx
.p
, pv
);
746 m4x4_copy( cam
->mtx_prev
.p
, pv_prev
);
747 m4x4_reset_clipping( pv
, 100.0f
, 0.1f
);
748 m4x4_reset_clipping( pv_prev
, 100.0f
, 0.1f
);
750 m4x4_mul( pv
, v
, pv
);
751 m4x4_mul( pv_prev
, v_prev
, pv_prev
);
753 m4x3f identity_matrix
;
754 m4x3_identity( identity_matrix
);
759 if( world
->skybox
== k_skybox_default
){
760 shader_model_sky_use();
761 shader_model_sky_uMdl( identity_matrix
);
762 shader_model_sky_uPv( pv
);
763 shader_model_sky_uPvmPrev( pv_prev
);
764 shader_model_sky_uTexGarbage(0);
765 world_link_lighting_ub( world
, _shader_model_sky
.id
);
767 glActiveTexture( GL_TEXTURE0
);
768 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
770 else if( world
->skybox
== k_skybox_space
){
771 shader_model_sky_space_use();
773 shader_model_sky_space_uMdl( identity_matrix
);
774 shader_model_sky_space_uPv( pv
);
775 shader_model_sky_space_uPvmPrev( pv_prev
);
776 shader_model_sky_space_uTexGarbage(0);
777 world_link_lighting_ub( world
, _shader_model_sky_space
.id
);
779 glActiveTexture( GL_TEXTURE0
);
780 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
786 glDepthMask( GL_FALSE
);
787 glDisable( GL_DEPTH_TEST
);
789 mesh_bind( &world_render
.skydome
);
790 mesh_draw( &world_render
.skydome
);
792 glEnable( GL_DEPTH_TEST
);
793 glDepthMask( GL_TRUE
);
796 static void render_world_gates( world_instance
*world
, camera
*cam
){
797 float closest
= INFINITY
;
798 struct ent_gate
*gate
= NULL
;
800 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_gate
); i
++ ){
801 ent_gate
*gi
= mdl_arritm( &world
->ent_gate
, i
);
803 if( !(gi
->flags
& k_ent_gate_nonlocal
) )
804 if( !(gi
->flags
& k_ent_gate_linked
) )
807 float dist
= v3_dist2( gi
->co
[0], cam
->transform
[3] );
809 vg_line_point( gi
->co
[0], 0.25f
, VG__BLUE
);
811 if( dist
< closest
){
817 world
->rendering_gate
= gate
;
820 if( gate
->flags
& k_ent_gate_locked
){
821 world
->rendering_gate
= NULL
;
825 if( gate
->flags
& k_ent_gate_nonlocal
){
826 if( !(gate
->flags
& k_ent_gate_linked
) ||
827 (world_static
.load_state
!= k_world_loader_none
) ){
828 world
->rendering_gate
= NULL
;
829 render_gate_unlinked( world
, gate
, cam
);
833 world_instance
*dest_world
= &world_static
.instances
[ gate
->target
];
834 render_gate( world
, dest_world
, gate
, cam
);
837 render_gate( world
, world
, gate
, cam
);
841 static void world_prerender( world_instance
*world
){
842 if( mdl_arrcount( &world
->ent_light
) ){
843 f32 rate
= vg_maxf(0.1f
, fabsf(k_day_length
)) * vg_signf(k_day_length
);
844 world
->time
+= vg
.time_frame_delta
* (1.0/(rate
*60.0));
850 if( world
->info
.flags
& 0x1 ){
851 world
->time
= world
->info
.timezone
;
854 struct ub_world_lighting
*state
= &world
->ub_lighting
;
856 state
->g_time
= world
->time
;
857 state
->g_realtime
= vg
.time_real
;
858 state
->g_debug_indices
= k_debug_light_indices
;
859 state
->g_light_preview
= k_light_preview
;
860 state
->g_debug_complexity
= k_debug_light_complexity
;
861 state
->g_time_of_day
= vg_fractf( world
->time
);
863 if( vg
.quality_profile
== k_quality_profile_high
)
864 state
->g_shadow_samples
= 8;
865 else if( vg
.quality_profile
== k_quality_profile_low
)
866 state
->g_shadow_samples
= 2;
868 state
->g_shadow_samples
= 0;
870 state
->g_day_phase
= cosf( state
->g_time_of_day
* VG_PIf
* 2.0f
);
871 state
->g_sunset_phase
= cosf( state
->g_time_of_day
* VG_PIf
* 4.0f
+ VG_PIf
);
873 state
->g_day_phase
= state
->g_day_phase
* 0.5f
+ 0.5f
;
874 state
->g_sunset_phase
= powf( state
->g_sunset_phase
* 0.5f
+ 0.5f
, 6.0f
);
876 float a
= state
->g_time_of_day
* VG_PIf
* 2.0f
;
877 state
->g_sun_dir
[0] = sinf( a
);
878 state
->g_sun_dir
[1] = cosf( a
);
879 state
->g_sun_dir
[2] = 0.2f
;
880 v3_normalize( state
->g_sun_dir
);
882 world
->probabilities
[ k_probability_curve_constant
] = 1.0f
;
883 float dp
= state
->g_day_phase
;
885 world
->probabilities
[ k_probability_curve_wildlife_day
] =
886 (dp
*dp
*0.8f
+state
->g_sunset_phase
)*0.8f
;
887 world
->probabilities
[ k_probability_curve_wildlife_night
] =
888 1.0f
-powf(fabsf((state
->g_time_of_day
-0.5f
)*5.0f
),5.0f
);
890 glBindBuffer( GL_UNIFORM_BUFFER
, world
->ubo_lighting
);
891 glBufferSubData( GL_UNIFORM_BUFFER
, 0,
892 sizeof(struct ub_world_lighting
), &world
->ub_lighting
);
895 static void render_other_entities( world_instance
*world
, camera
*cam
){
898 bh_iter_init_range( 0, &it
, cam
->pos
, radius
+10.0f
);
904 while( bh_next( world
->entity_bh
, &it
, &idx
) ){
905 u32 id
= world
->entity_list
[ idx
],
906 type
= mdl_entity_id_type( id
),
907 index
= mdl_entity_id_id( id
);
909 if( type
== k_ent_glider
) {
910 if( glider_count
< vg_list_size(glider_list
) )
911 glider_list
[ glider_count
++ ] = index
;
915 shader_model_entity_use();
916 shader_model_entity_uTexMain( 0 );
917 shader_model_entity_uCamera( cam
->transform
[3] );
918 shader_model_entity_uPv( cam
->mtx
.pv
);
920 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_entity
);
922 for( u32 j
=0; j
<glider_count
; j
++ ){
923 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, glider_list
[j
] );
925 if( !(glider
->flags
& 0x1) )
929 mdl_transform_m4x3( &glider
->transform
, mdl
);
931 f32 dist
= v3_dist( glider
->transform
.co
, cam
->pos
) * (1.0f
/radius
),
932 scale
= vg_smoothstepf( vg_clampf( 5.0f
-dist
*5.0f
, 0.0f
,1.0f
) );
933 m3x3_scalef( mdl
, scale
);
935 render_glider_model( cam
, world
, mdl
, k_board_shader_entity
);
939 static void render_world( world_instance
*world
, camera
*cam
,
940 int stenciled
, int viewing_from_gate
,
941 int with_water
, int with_cubemaps
){
943 glClear( GL_DEPTH_BUFFER_BIT
);
944 glStencilFunc( GL_EQUAL
, 1, 0xFF );
945 glStencilMask( 0x00 );
946 glEnable( GL_CULL_FACE
);
947 glEnable( GL_STENCIL_TEST
);
950 glStencilMask( 0xFF );
951 glStencilFunc( GL_ALWAYS
, 1, 0xFF );
952 glDisable( GL_STENCIL_TEST
);
955 render_sky( world
, cam
);
958 m4x3_identity(identity
);
959 render_world_routes( world
, world
, identity
, cam
, viewing_from_gate
, 0 );
960 render_world_standard( world
, cam
);
961 render_world_cubemapped( world
, cam
, with_cubemaps
);
963 render_world_vb( world
, cam
);
964 render_world_alphatest( world
, cam
);
965 render_world_foliage( world
, cam
);
966 render_terrain( world
, cam
);
968 if( !viewing_from_gate
){
969 world_entity_focus_render();
973 float min_dist
= INFINITY
;
975 if( mdl_arrcount( &world
->ent_route
) ){
976 for( u32 i
=0; i
<mdl_arrcount( &world
->ent_route
); i
++ ){
977 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
978 float dist
= v3_dist2( route
->board_transform
[3], cam
->pos
);
980 if( dist
< min_dist
){
986 ent_route
*route
= mdl_arritm( &world
->ent_route
, closest
);
987 sfd_render( world
, cam
, route
->board_transform
);
991 if( !viewing_from_gate
){
993 if( mdl_entity_id_type(world_static
.focused_entity
) == k_ent_challenge
)
994 greyout
= world_static
.focus_strength
;
996 if( greyout
> 0.0f
){
997 glDrawBuffers( 1, (GLenum
[]){ GL_COLOR_ATTACHMENT0
} );
999 glDisable(GL_DEPTH_TEST
);
1000 glDepthMask(GL_FALSE
);
1001 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1002 glBlendEquation(GL_FUNC_ADD
);
1004 shader_blitcolour_use();
1005 shader_blitcolour_uColour( (v4f
){ 0.5f
, 0.5f
, 0.5f
, greyout
*0.56f
} );
1008 glDisable(GL_BLEND
);
1009 glEnable(GL_DEPTH_TEST
);
1010 glDepthMask(GL_TRUE
);
1011 glDrawBuffers( 2, (GLenum
[]){ GL_COLOR_ATTACHMENT0
,
1012 GL_COLOR_ATTACHMENT1
} );
1015 render_world_fxglow( world
, world
, cam
, NULL
, 1, 1, 0 );
1019 render_water_texture( world
, cam
);
1020 render_fb_bind( gpipeline
.fb_main
, 1 );
1024 glStencilFunc( GL_EQUAL
, 1, 0xFF );
1025 glStencilMask( 0x00 );
1026 glEnable( GL_CULL_FACE
);
1027 glEnable( GL_STENCIL_TEST
);
1031 render_water_surface( world
, cam
);
1034 render_remote_players( world
, cam
);
1035 render_other_entities( world
, cam
);
1036 ent_miniworld_render( world
, cam
);
1039 glStencilMask( 0xFF );
1040 glStencilFunc( GL_ALWAYS
, 1, 0xFF );
1041 glDisable( GL_STENCIL_TEST
);
1046 static void render_world_override_pass( world_instance
*world
,
1047 struct world_pass
*pass
,
1048 m4x3f mmdl
, m3x3f mnormal
,
1050 for( int i
=0; i
<world
->surface_count
; i
++ ){
1051 struct world_surface
*mat
= &world
->surfaces
[i
];
1052 if( mat
->info
.flags
& k_material_flag_ghosts
) continue;
1055 if( pass
->geo_type
== k_world_geo_type_solid
)
1058 sm
= &mat
->sm_no_collide
;
1060 if( !sm
->indice_count
)
1063 pass
->fn_set_mdl( mmdl
);
1064 pass
->fn_set_uNormalMtx( mnormal
);
1065 pass
->fn_set_uPvmPrev( mpvm_prev
);
1066 pass
->fn_bind_textures( world
, mat
);
1067 mdl_draw_submesh( sm
);
1071 static void render_world_override( world_instance
*world
,
1072 world_instance
*lighting_source
,
1075 ent_spawn
*dest_spawn
, v4f map_info
){
1076 struct world_pass pass
= {
1078 .fn_bind_textures
= bindpoint_override
,
1079 .fn_set_mdl
= shader_scene_override_uMdl
,
1080 .fn_set_uPvmPrev
= shader_scene_override_uPvmPrev
,
1081 .fn_set_uNormalMtx
= shader_scene_override_uNormalMtx
,
1082 .shader
= k_shader_override
1085 shader_scene_override_use();
1086 shader_scene_override_uTexGarbage(0);
1087 shader_scene_override_uTexMain(1);
1088 shader_scene_override_uPv( pass
.cam
->mtx
.pv
);
1089 shader_scene_override_uMapInfo( map_info
);
1091 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( lighting_source
, scene_override
);
1092 bind_terrain_noise();
1094 shader_scene_override_uCamera( pass
.cam
->transform
[3] );
1097 m4x3_expand( mmdl
, mpvm_prev
);
1098 m4x4_mul( cam
->mtx_prev
.pv
, mpvm_prev
, mpvm_prev
);
1101 m3x3_inv( mmdl
, mnormal
);
1102 m3x3_transpose( mnormal
, mnormal
);
1103 v3_normalize( mnormal
[0] );
1104 v3_normalize( mnormal
[1] );
1105 v3_normalize( mnormal
[2] );
1107 v4f uPlayerPos
, uSpawnPos
;
1108 v4_zero( uPlayerPos
);
1109 v4_zero( uSpawnPos
);
1111 v3_copy( world
->player_co
, uPlayerPos
);
1113 if( dest_spawn
&& (v3_dist2(dest_spawn
->transform
.co
,uPlayerPos
) > 0.1f
) )
1114 v3_copy( dest_spawn
->transform
.co
, uSpawnPos
);
1116 v3_add( uPlayerPos
, (v3f
){0,-1,0}, uSpawnPos
);
1118 uPlayerPos
[3] = v3_dist(uPlayerPos
,uSpawnPos
);
1119 uSpawnPos
[3] = 1.0f
/uPlayerPos
[3];
1121 shader_scene_override_uPlayerPos( uPlayerPos
);
1122 shader_scene_override_uSpawnPos( uSpawnPos
);
1125 glDisable( GL_CULL_FACE
);
1126 mesh_bind( &world
->mesh_geo
);
1127 pass
.geo_type
= k_world_geo_type_solid
;
1128 render_world_override_pass( world
, &pass
, mmdl
, mnormal
, mpvm_prev
);
1129 mesh_bind( &world
->mesh_no_collide
);
1130 pass
.geo_type
= k_world_geo_type_nonsolid
;
1131 render_world_override_pass( world
, &pass
, mmdl
, mnormal
, mpvm_prev
);
1132 glEnable( GL_CULL_FACE
);
1134 render_world_fxglow( world
, world
, cam
, mmdl
, 0, 0, 1 );
1137 static void render_cubemap_side( world_instance
*world
, ent_cubemap
*cm
,
1140 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
1141 GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ side
, cm
->texture_id
, 0 );
1142 glClear( GL_DEPTH_BUFFER_BIT
);
1145 { -1.0f
, 0.0f
, 0.0f
},
1146 { 1.0f
, 0.0f
, 0.0f
},
1147 { 0.0f
, -1.0f
, 0.0f
},
1148 { 0.0f
, 1.0f
, 0.0f
},
1149 { 0.0f
, 0.0f
, -1.0f
},
1150 { 0.0f
, 0.0f
, 1.0f
}
1153 { 0.0f
, -1.0f
, 0.0f
},
1154 { 0.0f
, -1.0f
, 0.0f
},
1155 { 0.0f
, 0.0f
, 1.0f
},
1156 { 0.0f
, 0.0f
, -1.0f
},
1157 { 0.0f
, -1.0f
, 0.0f
},
1158 { 0.0f
, -1.0f
, 0.0f
}
1161 v3_zero( cam
.angles
);
1162 v3_copy( cm
->co
, cam
.pos
);
1164 v3_copy( forward
[side
], cam
.transform
[2] );
1165 v3_copy( up
[side
], cam
.transform
[1] );
1166 v3_cross( up
[side
], forward
[side
], cam
.transform
[0] );
1167 v3_copy( cm
->co
, cam
.transform
[3] );
1168 m4x3_invert_affine( cam
.transform
, cam
.transform_inverse
);
1170 camera_update_view( &cam
);
1175 m4x4_copy( cam
.mtx
.p
, cam
.mtx_prev
.p
);
1176 m4x4_projection( cam
.mtx
.p
, cam
.fov
, 1.0f
, cam
.nearz
, cam
.farz
);
1177 camera_finalize( &cam
);
1178 camera_finalize( &cam
);
1180 render_world( world
, &cam
, 0, 1, 1, 0 );
1183 static void render_world_cubemaps( world_instance
*world
){
1184 if( world
->cubemap_cooldown
)
1185 world
->cubemap_cooldown
--;
1187 world
->cubemap_cooldown
= 60;
1189 glViewport( 0, 0, WORLD_CUBEMAP_RES
, WORLD_CUBEMAP_RES
);
1190 for( u32 i
=0; i
<mdl_arrcount( &world
->ent_cubemap
); i
++ ){
1191 ent_cubemap
*cm
= mdl_arritm( &world
->ent_cubemap
, i
);
1192 glBindFramebuffer( GL_FRAMEBUFFER
, cm
->framebuffer_id
);
1194 world
->cubemap_side
++;
1195 if( world
->cubemap_side
>= 6 )
1196 world
->cubemap_side
= 0;
1198 render_cubemap_side( world
, cm
, world
->cubemap_side
);
1205 * ---------------------------------------------
1208 static void render_world_depth( world_instance
*world
, camera
*cam
){
1209 m4x3f identity_matrix
;
1210 m4x3_identity( identity_matrix
);
1212 shader_scene_depth_use();
1213 shader_scene_depth_uCamera( cam
->transform
[3] );
1214 shader_scene_depth_uPv( cam
->mtx
.pv
);
1215 shader_scene_depth_uPvmPrev( cam
->mtx_prev
.pv
);
1216 shader_scene_depth_uMdl( identity_matrix
);
1217 world_link_lighting_ub( world
, _shader_scene_depth
.id
);
1219 mesh_bind( &world
->mesh_geo
);
1220 mesh_draw( &world
->mesh_geo
);
1223 static void render_world_position( world_instance
*world
, camera
*cam
){
1224 m4x3f identity_matrix
;
1225 m4x3_identity( identity_matrix
);
1227 shader_scene_position_use();
1228 shader_scene_position_uCamera( cam
->transform
[3] );
1229 shader_scene_position_uPv( cam
->mtx
.pv
);
1230 shader_scene_position_uPvmPrev( cam
->mtx_prev
.pv
);
1231 shader_scene_position_uMdl( identity_matrix
);
1232 world_link_lighting_ub( world
, _shader_scene_position
.id
);
1234 mesh_bind( &world
->mesh_geo
);
1235 mesh_draw( &world
->mesh_geo
);
1238 struct ui_enum_opt skybox_setting_options
[] = {
1239 { 0, "g_daysky_colour" },
1240 { 1, "g_nightsky_colour" },
1241 { 2, "g_sunset_colour" },
1242 { 3, "g_ambient_colour" },
1243 { 4, "g_sun_colour" },
1246 static f32
*skybox_prop_location( world_instance
*world
, i32 index
){
1248 case 0: return world
->ub_lighting
.g_daysky_colour
; break;
1249 case 1: return world
->ub_lighting
.g_nightsky_colour
; break;
1250 case 2: return world
->ub_lighting
.g_sunset_colour
; break;
1251 case 3: return world
->ub_lighting
.g_ambient_colour
; break;
1252 case 4: return world
->ub_lighting
.g_sun_colour
; break;
1253 default: return NULL
;
1257 static void imgui_world_light_edit( world_instance
*world
){
1258 ui_rect panel
= { vg
.window_x
-400, 0, 400, vg
.window_y
};
1259 ui_fill( panel
, ui_colour( k_ui_bg
+1 ) );
1260 ui_outline( panel
, 1, ui_colour( k_ui_bg
+7 ), 0 );
1261 ui_rect_pad( panel
, (ui_px
[2]){ 8, 8 } );
1262 vg_ui
.wants_mouse
= 1;
1264 static i32 option_to_edit
= 0;
1265 ui_enum( panel
, "option", skybox_setting_options
, 5, &option_to_edit
);
1266 ui_colourpicker( panel
, "colour",
1267 skybox_prop_location( world
, option_to_edit
) );
1269 if( ui_button( panel
, "save tweaker file ('/tmp/tweaker.txt')\n" ) == 1 ){
1270 FILE *fp
= fopen( "/tmp/tweaker.txt", "w" );
1272 for( i32 i
=0; i
<5; i
++ ){
1273 struct ui_enum_opt
*opt
= &skybox_setting_options
[i
];
1274 f32
*val
= skybox_prop_location( world
, i
);
1275 fprintf( fp
, "%s = {%.3ff, %.3ff, %.3ff, %.3ff},\n",
1276 opt
->alias
, val
[0], val
[1], val
[2], val
[3] );