2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
6 #include "world_render.h"
10 #include "ent_miniworld.h"
11 #include "player_remote.h"
12 #include "ent_skateshop.h"
14 #include "shaders/model_entity.h"
16 struct world_render world_render
;
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
)
29 vg_info( "Allocate uniform buffers\n" );
30 for( int i
=0; i
<k_world_max
; i
++ )
32 world_instance
*world
= &world_static
.instances
[i
];
33 world
->ubo_bind_point
= i
;
35 glGenBuffers( 1, &world
->ubo_lighting
);
36 glBindBuffer( GL_UNIFORM_BUFFER
, world
->ubo_lighting
);
37 glBufferData( GL_UNIFORM_BUFFER
, sizeof(struct ub_world_lighting
),
38 NULL
, GL_DYNAMIC_DRAW
);
40 glBindBufferBase( GL_UNIFORM_BUFFER
, i
, world
->ubo_lighting
);
45 void world_render_init(void)
47 VG_VAR_F32( k_day_length
);
48 VG_VAR_I32( k_debug_light_indices
);
49 VG_VAR_I32( k_debug_light_complexity
);
50 VG_VAR_I32( k_light_preview
);
51 VG_VAR_I32( k_light_editor
);
52 vg_console_reg_cmd( "set_time", ccmd_set_time
, NULL
);
54 world_render
.sky_rate
= 1.0;
55 world_render
.sky_target_rate
= 1.0;
57 vg_info( "Loading world resources\n" );
58 vg_linear_clear( vg_mem
.scratch
);
61 mdl_open( &msky
, "models/rs_skydome.mdl", vg_mem
.scratch
);
62 mdl_load_metadata_block( &msky
, vg_mem
.scratch
);
63 mdl_async_load_glmesh( &msky
, &world_render
.skydome
, NULL
);
66 vg_info( "Loading default world textures\n" );
67 vg_tex2d_load_qoi_async_file( "textures/garbage.qoi",
68 VG_TEX2D_NEAREST
|VG_TEX2D_REPEAT
,
69 &world_render
.tex_terrain_noise
);
71 vg_info( "Allocate frame buffers\n" );
72 for( int i
=0; i
<k_world_max
; i
++ )
74 world_instance
*world
= &world_static
.instances
[i
];
75 world
->heightmap
= vg_framebuffer_allocate( vg_mem
.rtmemory
, 1, 0 );
76 world
->heightmap
->display_name
= NULL
;
77 world
->heightmap
->fixed_w
= 1024;
78 world
->heightmap
->fixed_h
= 1024;
79 world
->heightmap
->resolution_div
= 0;
80 world
->heightmap
->attachments
[0] = (vg_framebuffer_attachment
)
82 NULL
, k_framebuffer_attachment_type_texture
,
83 .internalformat
= GL_RG16F
,
86 .attachment
= GL_COLOR_ATTACHMENT0
88 vg_framebuffer_create( world
->heightmap
);
91 vg_async_call( async_world_render_init
, NULL
, 0 );
95 * standard uniform bindings
96 * ----------------------------------------------------------------------------
98 void world_link_lighting_ub( world_instance
*world
, GLuint shader
)
100 GLuint idx
= glGetUniformBlockIndex( shader
, "ub_world_lighting" );
101 glUniformBlockBinding( shader
, idx
, world
->ubo_bind_point
);
104 void world_bind_position_texture( world_instance
*world
,
105 GLuint shader
, GLuint location
,
108 vg_framebuffer_bind_texture( world
->heightmap
, 0, slot
);
109 glUniform1i( location
, slot
);
112 void world_bind_light_array( world_instance
*world
,
113 GLuint shader
, GLuint location
,
116 glActiveTexture( GL_TEXTURE0
+ slot
);
117 glBindTexture( GL_TEXTURE_BUFFER
, world
->tex_light_entities
);
118 glUniform1i( location
, slot
);
121 void world_bind_light_index( world_instance
*world
,
122 GLuint shader
, GLuint location
,
125 glActiveTexture( GL_TEXTURE0
+ slot
);
126 glBindTexture( GL_TEXTURE_3D
, world
->tex_light_cubes
);
127 glUniform1i( location
, slot
);
130 void bind_terrain_noise(void)
132 glActiveTexture( GL_TEXTURE0
);
133 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
137 * Get OpenGL texture name from texture ID.
139 static GLuint
world_get_texture( world_instance
*world
, u32 id
){
140 if( id
& 0x80000000 ) return skaterift
.rt_textures
[id
& ~0x80000000];
141 else return world
->textures
[ id
];
146 * ----------------------------------------------------------------------------
152 enum mdl_shader shader
;
153 enum world_geo_type geo_type
;
155 void (*fn_bind
)( world_instance
*world
, struct world_surface
*mat
);
156 void (*fn_set_mdl
)( m4x3f mdl
);
157 void (*fn_set_uPvmPrev
)( m4x4f pvm
);
158 void (*fn_set_uNormalMtx
)( m3x3f mnorm
);
161 void render_world_depth( world_instance
*world
, vg_camera
*cam
);
164 * Render a run of submeshes, only of those which match material_id
166 static void world_render_submeshes( world_instance
*world
,
167 struct world_pass
*pass
,
168 mdl_transform
*transform
,
169 u32 start
, u32 count
, u32 material_id
)
171 for( u32 k
=0; k
<count
; k
++ )
173 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
, start
+k
);
174 if( sm
->material_id
!= material_id
)
178 mdl_transform_m4x3( transform
, mmdl
);
181 m4x3_expand( mmdl
, m4mdl
);
182 m4x4_mul( pass
->cam
->mtx_prev
.pv
, m4mdl
, m4mdl
);
184 pass
->fn_set_mdl( mmdl
);
185 pass
->fn_set_uPvmPrev( m4mdl
);
187 mdl_draw_submesh( sm
);
192 * Render props attached to this material
194 static void world_render_props( world_instance
*world
, u32 material_id
,
195 struct world_pass
*pass
)
197 struct world_surface
*mat
= &world
->surfaces
[ material_id
];
198 if( !(mat
->flags
& WORLD_SURFACE_HAS_PROPS
) ) return;
200 pass
->fn_bind( world
, mat
);
202 for( u32 j
=0; j
<mdl_arrcount( &world
->ent_prop
); j
++ ){
203 ent_prop
*prop
= mdl_arritm( &world
->ent_prop
, j
);
204 if( prop
->flags
& 0x1 ) continue;
206 world_render_submeshes( world
, pass
, &prop
->transform
,
207 prop
->submesh_start
, prop
->submesh_count
, material_id
);
212 * Render traffic models attactched to this material
214 static void world_render_traffic( world_instance
*world
, u32 material_id
,
215 struct world_pass
*pass
)
217 struct world_surface
*mat
= &world
->surfaces
[ material_id
];
218 if( !(mat
->flags
& WORLD_SURFACE_HAS_TRAFFIC
) ) return;
220 pass
->fn_bind( world
, mat
);
222 for( u32 j
=0; j
<mdl_arrcount( &world
->ent_traffic
); j
++ ){
223 ent_traffic
*traffic
= mdl_arritm( &world
->ent_traffic
, j
);
225 world_render_submeshes( world
, pass
, &traffic
->transform
,
226 traffic
->submesh_start
, traffic
->submesh_count
,
232 * Iterate and render all materials which match the passes shader and geometry
233 * type. Includes props/traffic.
235 static void world_render_pass( world_instance
*world
, struct world_pass
*pass
)
237 for( int i
=0; i
<world
->surface_count
; i
++ )
239 struct world_surface
*mat
= &world
->surfaces
[i
];
241 if( mat
->info
.shader
== pass
->shader
)
245 if( pass
->geo_type
== k_world_geo_type_solid
)
251 world_render_traffic( world
, i
, pass
);
252 world_render_props( world
, i
, pass
);
253 sm
= &mat
->sm_no_collide
;
256 if( !sm
->indice_count
)
260 m4x3_identity( mmdl
);
261 pass
->fn_set_mdl( mmdl
);
262 pass
->fn_set_uPvmPrev( pass
->cam
->mtx_prev
.pv
);
263 pass
->fn_bind( world
, mat
);
264 mdl_draw_submesh( sm
);
270 * Specific shader instructions
271 * ----------------------------------------------------------------------------
274 static void world_render_both_stages( world_instance
*world
,
275 struct world_pass
*pass
)
277 mesh_bind( &world
->mesh_geo
);
278 pass
->geo_type
= k_world_geo_type_solid
;
279 world_render_pass( world
, pass
);
281 glDisable( GL_CULL_FACE
);
282 mesh_bind( &world
->mesh_no_collide
);
283 pass
->geo_type
= k_world_geo_type_nonsolid
;
284 world_render_pass( world
, pass
);
285 glEnable( GL_CULL_FACE
);
288 static void bindpoint_world_vb( world_instance
*world
,
289 struct world_surface
*mat
)
291 struct shader_props_vertex_blend
*props
= mat
->info
.props
.compiled
;
293 glActiveTexture( GL_TEXTURE1
);
294 glBindTexture( GL_TEXTURE_2D
, world_get_texture(world
, props
->tex_diffuse
) );
297 shader_scene_vertex_blend_uOffset( props
->blend_offset
);
301 static void render_world_vb( world_instance
*world
, vg_camera
*cam
)
303 shader_scene_vertex_blend_use();
304 shader_scene_vertex_blend_uTexGarbage(0);
305 shader_scene_vertex_blend_uTexGradients(1);
306 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_vertex_blend
);
308 glActiveTexture( GL_TEXTURE0
);
309 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
311 shader_scene_vertex_blend_uPv( cam
->mtx
.pv
);
312 shader_scene_vertex_blend_uCamera( cam
->transform
[3] );
314 struct world_pass pass
=
316 .shader
= k_shader_standard_vertex_blend
,
318 .fn_bind
= bindpoint_world_vb
,
319 .fn_set_mdl
= shader_scene_vertex_blend_uMdl
,
320 .fn_set_uPvmPrev
= shader_scene_vertex_blend_uPvmPrev
,
323 world_render_both_stages( world
, &pass
);
326 static void world_shader_standard_bind( world_instance
*world
, vg_camera
*cam
)
328 shader_scene_standard_use();
329 shader_scene_standard_uTexGarbage(0);
330 shader_scene_standard_uTexMain(1);
331 shader_scene_standard_uPv( cam
->mtx
.pv
);
332 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_standard
);
334 bind_terrain_noise();
335 shader_scene_standard_uCamera( cam
->transform
[3] );
338 static void bindpoint_standard( world_instance
*world
,
339 struct world_surface
*mat
)
341 struct shader_props_standard
*props
= mat
->info
.props
.compiled
;
343 glActiveTexture( GL_TEXTURE1
);
344 glBindTexture( GL_TEXTURE_2D
, world_get_texture(world
, props
->tex_diffuse
) );
347 static void render_world_standard( world_instance
*world
, vg_camera
*cam
)
349 world_shader_standard_bind( world
, cam
);
350 struct world_pass pass
=
352 .shader
= k_shader_standard
,
354 .fn_bind
= bindpoint_standard
,
355 .fn_set_mdl
= shader_scene_standard_uMdl
,
356 .fn_set_uPvmPrev
= shader_scene_standard_uPvmPrev
,
359 world_render_both_stages( world
, &pass
);
362 static void bindpoint_world_cubemapped( world_instance
*world
,
363 struct world_surface
*mat
)
365 struct shader_props_cubemapped
*props
= mat
->info
.props
.compiled
;
367 glActiveTexture( GL_TEXTURE1
);
368 glBindTexture( GL_TEXTURE_2D
,
369 world_get_texture( world
,props
->tex_diffuse
) );
371 u32 cubemap_id
= props
->cubemap_entity
,
374 if( mdl_entity_id_type( cubemap_id
) == k_ent_cubemap
)
376 cubemap_index
= mdl_entity_id_id( cubemap_id
);
379 ent_cubemap
*cm
= mdl_arritm( &world
->ent_cubemap
, cubemap_index
);
380 glActiveTexture( GL_TEXTURE10
);
381 glBindTexture( GL_TEXTURE_CUBE_MAP
, cm
->texture_id
);
383 shader_scene_cubemapped_uColour( props
->tint
);
386 static void bindpoint_world_cubemapped_disabled( world_instance
*world
,
387 struct world_surface
*mat
)
389 struct shader_props_cubemapped
*props
= mat
->info
.props
.compiled
;
391 glActiveTexture( GL_TEXTURE1
);
392 glBindTexture( GL_TEXTURE_2D
,
393 world_get_texture( world
, props
->tex_diffuse
) );
396 static void render_world_cubemapped( world_instance
*world
, vg_camera
*cam
,
399 if( !mdl_arrcount( &world
->ent_cubemap
) )
404 world_shader_standard_bind( world
, cam
);
406 struct world_pass pass
=
408 .shader
= k_shader_cubemap
,
410 .fn_bind
= bindpoint_world_cubemapped_disabled
,
411 .fn_set_mdl
= shader_scene_standard_uMdl
,
412 .fn_set_uPvmPrev
= shader_scene_standard_uPvmPrev
,
415 world_render_both_stages( world
, &pass
);
419 shader_scene_cubemapped_use();
420 shader_scene_cubemapped_uTexGarbage(0);
421 shader_scene_cubemapped_uTexMain(1);
422 shader_scene_cubemapped_uTexCubemap(10);
423 shader_scene_cubemapped_uPv( cam
->mtx
.pv
);
425 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_cubemapped
);
427 bind_terrain_noise();
428 shader_scene_cubemapped_uCamera( cam
->transform
[3] );
430 struct world_pass pass
=
432 .shader
= k_shader_cubemap
,
434 .fn_bind
= bindpoint_world_cubemapped
,
435 .fn_set_mdl
= shader_scene_cubemapped_uMdl
,
436 .fn_set_uPvmPrev
= shader_scene_cubemapped_uPvmPrev
,
439 world_render_both_stages( world
, &pass
);
443 static void render_world_alphatest( world_instance
*world
, vg_camera
*cam
)
445 shader_scene_standard_alphatest_use();
446 shader_scene_standard_alphatest_uTexGarbage(0);
447 shader_scene_standard_alphatest_uTexMain(1);
448 shader_scene_standard_alphatest_uPv( cam
->mtx
.pv
);
450 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_standard_alphatest
);
452 bind_terrain_noise();
453 shader_scene_standard_alphatest_uCamera( cam
->transform
[3] );
454 glDisable(GL_CULL_FACE
);
456 struct world_pass pass
=
458 .shader
= k_shader_standard_cutout
,
460 .fn_bind
= bindpoint_standard
,
461 .fn_set_mdl
= shader_scene_standard_alphatest_uMdl
,
462 .fn_set_uPvmPrev
= shader_scene_standard_alphatest_uPvmPrev
,
465 world_render_both_stages( world
, &pass
);
466 glEnable(GL_CULL_FACE
);
469 static void render_world_foliage( world_instance
*world
, vg_camera
*cam
)
471 shader_scene_foliage_use();
472 shader_scene_foliage_uTexGarbage(0);
473 shader_scene_foliage_uTexMain(1);
474 shader_scene_foliage_uPv( cam
->mtx
.pv
);
475 shader_scene_foliage_uTime( vg
.time
);
477 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_foliage
);
478 bind_terrain_noise();
480 shader_scene_foliage_uCamera( cam
->transform
[3] );
481 glDisable(GL_CULL_FACE
);
482 struct world_pass pass
=
484 .shader
= k_shader_foliage
,
486 .fn_bind
= bindpoint_standard
,
487 .fn_set_mdl
= shader_scene_foliage_uMdl
,
488 .fn_set_uPvmPrev
= shader_scene_foliage_uPvmPrev
,
490 world_render_both_stages( world
, &pass
);
491 glEnable(GL_CULL_FACE
);
494 static void world_render_challenges( world_instance
*world
,
495 struct world_pass
*pass
, v3f pos
)
498 if( skaterift
.activity
== k_skaterift_replay
) return;
499 if( world
!= world_current_instance() ) return;
504 u32 objective_list
[ 32 ],
505 challenge_list
[ 16 ];
507 v2f objective_uv_offsets
[ 32 ];
509 u32 objective_count
= 0,
512 ent_challenge
*active_challenge
= NULL
;
514 if( mdl_entity_id_type( world_static
.focused_entity
) == k_ent_challenge
){
515 if( (skaterift
.activity
== k_skaterift_default
) &&
516 world_static
.challenge_target
){
520 if( !((skaterift
.activity
!= k_skaterift_ent_focus
) &&
521 !world_static
.challenge_target
) ){
522 world_instance
*challenge_world
= world_current_instance();
523 u32 index
= mdl_entity_id_id( world_static
.focused_entity
);
524 active_challenge
= mdl_arritm(&challenge_world
->ent_challenge
, index
);
528 if( active_challenge
){
529 shader_scene_fxglow_uUvOffset( (v2f
){ 8.0f
/256.0f
, 0.0f
} );
530 challenge_list
[ challenge_count
++ ] = world_static
.focused_entity
;
532 u32 next
= active_challenge
->first
;
533 while( mdl_entity_id_type(next
) == k_ent_objective
){
534 u32 index
= mdl_entity_id_id( next
);
535 objective_list
[ objective_count
++ ] = index
;
537 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
538 next
= objective
->id_next
;
544 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
546 bh_iter_init_range( 0, &it
, pos
, radius
+10.0f
);
548 while( bh_next( world
->entity_bh
, &it
, &idx
) ){
549 u32 id
= world
->entity_list
[ idx
],
550 type
= mdl_entity_id_type( id
),
551 index
= mdl_entity_id_id( id
);
553 if( type
== k_ent_objective
) {
554 if( objective_count
< vg_list_size(objective_list
) )
555 objective_list
[ objective_count
++ ] = index
;
557 else if( type
== k_ent_challenge
){
558 if( challenge_count
< vg_list_size(challenge_list
) )
559 challenge_list
[ challenge_count
++ ] = index
;
564 /* render objectives */
565 glDisable( GL_CULL_FACE
);
566 mesh_bind( &world
->mesh_no_collide
);
567 u32 last_material
= 0;
568 for( u32 i
=0; i
<objective_count
; i
++ )
570 u32 index
= objective_list
[ i
];
571 ent_objective
*objective
= mdl_arritm( &world
->ent_objective
, index
);
572 if( (objective
->flags
& k_ent_objective_hidden
) &&
573 !active_challenge
) continue;
579 u32 passed
= objective
->flags
& k_ent_objective_passed
;
580 f32 target
= passed
? 0.0f
: 1.0f
;
581 vg_slewf(&objective
->transform
.s
[0], target
, vg
.time_frame_delta
*4.0f
);
582 scale
= vg_smoothstepf( objective
->transform
.s
[0] );
584 if( (objective
== world_static
.challenge_target
) || passed
)
585 shader_scene_fxglow_uUvOffset( (v2f
){ 16.0f
/256.0f
, 0.0f
} );
587 shader_scene_fxglow_uUvOffset( (v2f
){ 8.0f
/256.0f
, 0.0f
} );
591 f32 dist
= v3_dist( objective
->transform
.co
, pos
) * (1.0f
/radius
);
592 scale
= vg_smoothstepf( vg_clampf( 5.0f
-dist
*5.0f
, 0.0f
,1.0f
) );
596 q_m3x3( objective
->transform
.q
, mmdl
);
597 m3x3_scalef( mmdl
, scale
);
598 v3_copy( objective
->transform
.co
, mmdl
[3] );
599 shader_scene_fxglow_uMdl( mmdl
);
601 for( u32 j
=0; j
<objective
->submesh_count
; j
++ )
603 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
604 objective
->submesh_start
+ j
);
606 if( sm
->material_id
!= last_material
)
608 last_material
= sm
->material_id
;
609 pass
->fn_bind( world
, &world
->surfaces
[sm
->material_id
] );
611 mdl_draw_submesh( sm
);
616 font3d_bind( &gui
.font
, k_font_shader_world
, 0, world
, &skaterift
.cam
);
620 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_challenge
); i
++ )
622 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, i
);
623 if( challenge
->status
) count
++;
628 vg_strnull( &str
, buf
, sizeof(buf
) );
629 vg_strcati32( &str
, count
);
630 vg_strcatch( &str
, '/' );
631 vg_strcati32( &str
, mdl_arrcount(&world
->ent_challenge
) );
633 f32 w
= font3d_string_width( 1, buf
);
635 m3x3_identity( mlocal
);
636 mlocal
[3][0] = -w
*0.5f
;
640 for( u32 i
=0; i
<challenge_count
; i
++ )
642 u32 index
= challenge_list
[ i
];
643 ent_challenge
*challenge
= mdl_arritm( &world
->ent_challenge
, index
);
645 mdl_transform_m4x3( &challenge
->transform
, mmdl
);
646 m4x3_mul( mmdl
, mlocal
, mmdl
);
648 vg_line_point( challenge
->transform
.co
, 0.25f
, VG__RED
);
650 f32 dist
= v3_dist( challenge
->transform
.co
, pos
) * (1.0f
/radius
),
651 scale
= vg_smoothstepf( vg_clampf( 10.0f
-dist
*10.0f
, 0.0f
,1.0f
) ),
654 if( challenge
->status
)
657 shader_scene_font_uOpacity( scale
);
658 shader_scene_font_uColourize( colour
);
659 font3d_simple_draw( 1, buf
, &skaterift
.cam
, mmdl
);
663 static void bindpoint_fxglow( world_instance
*world
,
664 struct world_surface
*mat
)
666 struct shader_props_standard
*props
= mat
->info
.props
.compiled
;
668 glActiveTexture( GL_TEXTURE1
);
669 glBindTexture( GL_TEXTURE_2D
, world_get_texture(world
, props
->tex_diffuse
) );
672 static void render_world_fxglow( world_instance
*host_world
,
673 world_instance
*world
, vg_camera
*cam
,
675 int generic
, int challenges
, int regions
)
677 shader_scene_fxglow_use();
678 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
679 shader_scene_fxglow_uTexMain(1);
680 shader_scene_fxglow_uPv( cam
->mtx
.pv
);
681 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_fxglow
);
683 shader_scene_fxglow_uCamera( cam
->transform
[3] );
684 glDisable(GL_CULL_FACE
);
686 struct world_pass pass
=
688 .shader
= k_shader_fxglow
,
690 .fn_bind
= bindpoint_fxglow
,
691 .fn_set_mdl
= shader_scene_fxglow_uMdl
,
692 .fn_set_uPvmPrev
= shader_scene_fxglow_uPvmPrev
,
696 world_render_both_stages( world
, &pass
);
699 mesh_bind( &world
->mesh_no_collide
);
701 u32 last_material
= 0;
702 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_region
); i
++ ){
703 shader_scene_fxglow_uUvOffset( (v2f
){ 0.0f
, 0.0f
} );
704 ent_region
*region
= mdl_arritm( &world
->ent_region
, i
);
707 if( region
->flags
& k_ent_route_flag_achieve_gold
)
709 else if( region
->flags
& k_ent_route_flag_achieve_silver
)
712 shader_scene_fxglow_uUvOffset( (v2f
){ (8.0f
/256.0f
)*offset
, 0.0f
} );
715 mdl_transform_m4x3( ®ion
->transform
, mmdl
);
716 m4x3_mul( world_mmdl
, mmdl
, mmdl
);
717 shader_scene_fxglow_uMdl( mmdl
);
719 for( u32 j
=0; j
<region
->submesh_count
; j
++ )
721 mdl_submesh
*sm
= mdl_arritm( &world
->meta
.submeshs
,
722 region
->submesh_start
+ j
);
724 if( sm
->material_id
!= last_material
)
726 last_material
= sm
->material_id
;
727 pass
.fn_bind( world
, &world
->surfaces
[sm
->material_id
] );
729 mdl_draw_submesh( sm
);
735 world_render_challenges( world
, &pass
, cam
->pos
);
737 glEnable(GL_CULL_FACE
);
740 static void bindpoint_terrain( world_instance
*world
,
741 struct world_surface
*mat
)
743 struct shader_props_terrain
*props
= mat
->info
.props
.compiled
;
745 glActiveTexture( GL_TEXTURE1
);
746 glBindTexture( GL_TEXTURE_2D
, world_get_texture(world
, props
->tex_diffuse
) );
747 shader_scene_terrain_uBlendOffset( props
->blend_offset
);
748 shader_scene_terrain_uSandColour( props
->sand_colour
);
751 static void bindpoint_override( world_instance
*world
,
752 struct world_surface
*mat
)
754 if( mat
->info
.flags
& k_material_flag_collision
)
756 shader_scene_override_uAlphatest(0);
760 glActiveTexture( GL_TEXTURE1
);
761 glBindTexture( GL_TEXTURE_2D
, world_get_texture(world
, mat
->alpha_tex
) );
762 shader_scene_override_uAlphatest(1);
766 static void render_terrain( world_instance
*world
, vg_camera
*cam
)
768 shader_scene_terrain_use();
769 shader_scene_terrain_uTexGarbage(0);
770 shader_scene_terrain_uTexGradients(1);
772 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, scene_terrain
);
773 glActiveTexture( GL_TEXTURE0
);
774 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
776 shader_scene_terrain_uPv( cam
->mtx
.pv
);
777 shader_scene_terrain_uCamera( cam
->transform
[3] );
779 struct world_pass pass
=
781 .shader
= k_shader_terrain_blend
,
783 .fn_bind
= bindpoint_terrain
,
784 .fn_set_mdl
= shader_scene_terrain_uMdl
,
785 .fn_set_uPvmPrev
= shader_scene_terrain_uPvmPrev
,
788 world_render_both_stages( world
, &pass
);
791 static void render_sky( world_instance
*world
, vg_camera
*cam
)
794 * Modify matrix to remove clipping and view translation
801 m4x4_copy( cam
->mtx
.v
, v
);
802 m4x4_copy( cam
->mtx_prev
.v
, v_prev
);
804 for( int i
=0; i
<3; i
++ ){
806 v3_normalize(v_prev
[i
]);
809 v3_zero( v_prev
[3] );
811 m4x4_copy( cam
->mtx
.p
, pv
);
812 m4x4_copy( cam
->mtx_prev
.p
, pv_prev
);
813 m4x4_reset_clipping( pv
, 100.0f
, 0.1f
);
814 m4x4_reset_clipping( pv_prev
, 100.0f
, 0.1f
);
816 m4x4_mul( pv
, v
, pv
);
817 m4x4_mul( pv_prev
, v_prev
, pv_prev
);
819 m4x3f identity_matrix
;
820 m4x3_identity( identity_matrix
);
825 if( world
->skybox
== k_skybox_default
){
826 shader_model_sky_use();
827 shader_model_sky_uMdl( identity_matrix
);
828 shader_model_sky_uPv( pv
);
829 shader_model_sky_uPvmPrev( pv_prev
);
830 shader_model_sky_uTexGarbage(0);
831 world_link_lighting_ub( world
, _shader_model_sky
.id
);
833 glActiveTexture( GL_TEXTURE0
);
834 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
836 else if( world
->skybox
== k_skybox_space
){
837 shader_model_sky_space_use();
839 shader_model_sky_space_uMdl( identity_matrix
);
840 shader_model_sky_space_uPv( pv
);
841 shader_model_sky_space_uPvmPrev( pv_prev
);
842 shader_model_sky_space_uTexGarbage(0);
843 world_link_lighting_ub( world
, _shader_model_sky_space
.id
);
845 glActiveTexture( GL_TEXTURE0
);
846 glBindTexture( GL_TEXTURE_2D
, world_render
.tex_terrain_noise
);
849 vg_fatal_error( "Programming error\n" );
852 glDepthMask( GL_FALSE
);
853 glDisable( GL_DEPTH_TEST
);
855 mesh_bind( &world_render
.skydome
);
856 mesh_draw( &world_render
.skydome
);
858 glEnable( GL_DEPTH_TEST
);
859 glDepthMask( GL_TRUE
);
862 void render_world_gates( world_instance
*world
, vg_camera
*cam
)
864 float closest
= INFINITY
;
865 struct ent_gate
*gate
= NULL
;
867 for( u32 i
=0; i
<mdl_arrcount(&world
->ent_gate
); i
++ ){
868 ent_gate
*gi
= mdl_arritm( &world
->ent_gate
, i
);
870 if( !(gi
->flags
& k_ent_gate_nonlocal
) )
871 if( !(gi
->flags
& k_ent_gate_linked
) )
874 float dist
= v3_dist2( gi
->co
[0], cam
->transform
[3] );
876 vg_line_point( gi
->co
[0], 0.25f
, VG__BLUE
);
878 if( dist
< closest
){
884 world
->rendering_gate
= gate
;
887 if( gate
->flags
& k_ent_gate_locked
){
888 world
->rendering_gate
= NULL
;
892 if( gate
->flags
& k_ent_gate_nonlocal
){
893 if( !(gate
->flags
& k_ent_gate_linked
) ||
894 (world_static
.load_state
!= k_world_loader_none
) ){
895 world
->rendering_gate
= NULL
;
896 render_gate_unlinked( world
, gate
, cam
);
900 world_instance
*dest_world
= &world_static
.instances
[ gate
->target
];
901 render_gate( world
, dest_world
, gate
, cam
);
904 render_gate( world
, world
, gate
, cam
);
908 void world_prerender( world_instance
*world
)
910 if( mdl_arrcount( &world
->ent_light
) ){
911 f32 rate
= vg_maxf(0.1f
, fabsf(k_day_length
)) * vg_signf(k_day_length
);
912 world
->time
+= vg
.time_frame_delta
* (1.0/(rate
*60.0));
918 if( world
->info
.flags
& 0x1 ){
919 world
->time
= world
->info
.timezone
;
922 struct ub_world_lighting
*state
= &world
->ub_lighting
;
924 state
->g_time
= world
->time
;
925 state
->g_realtime
= vg
.time_real
;
926 state
->g_debug_indices
= k_debug_light_indices
;
927 state
->g_light_preview
= k_light_preview
;
928 state
->g_debug_complexity
= k_debug_light_complexity
;
929 state
->g_time_of_day
= vg_fractf( world
->time
);
931 if( vg
.quality_profile
== k_quality_profile_high
)
932 state
->g_shadow_samples
= 8;
933 else if( vg
.quality_profile
== k_quality_profile_low
)
934 state
->g_shadow_samples
= 2;
936 state
->g_shadow_samples
= 0;
938 state
->g_day_phase
= cosf( state
->g_time_of_day
* VG_PIf
* 2.0f
);
939 state
->g_sunset_phase
= cosf( state
->g_time_of_day
* VG_PIf
* 4.0f
+ VG_PIf
);
941 state
->g_day_phase
= state
->g_day_phase
* 0.5f
+ 0.5f
;
942 state
->g_sunset_phase
= powf( state
->g_sunset_phase
* 0.5f
+ 0.5f
, 6.0f
);
944 float a
= state
->g_time_of_day
* VG_PIf
* 2.0f
;
945 state
->g_sun_dir
[0] = sinf( a
);
946 state
->g_sun_dir
[1] = cosf( a
);
947 state
->g_sun_dir
[2] = 0.2f
;
948 v3_normalize( state
->g_sun_dir
);
950 world
->probabilities
[ k_probability_curve_constant
] = 1.0f
;
951 float dp
= state
->g_day_phase
;
953 world
->probabilities
[ k_probability_curve_wildlife_day
] =
954 (dp
*dp
*0.8f
+state
->g_sunset_phase
)*0.8f
;
955 world
->probabilities
[ k_probability_curve_wildlife_night
] =
956 1.0f
-powf(fabsf((state
->g_time_of_day
-0.5f
)*5.0f
),5.0f
);
958 glBindBuffer( GL_UNIFORM_BUFFER
, world
->ubo_lighting
);
959 glBufferSubData( GL_UNIFORM_BUFFER
, 0,
960 sizeof(struct ub_world_lighting
), &world
->ub_lighting
);
963 static void render_other_entities( world_instance
*world
, vg_camera
*cam
)
967 bh_iter_init_range( 0, &it
, cam
->pos
, radius
+10.0f
);
975 while( bh_next( world
->entity_bh
, &it
, &idx
) ){
976 u32 id
= world
->entity_list
[ idx
],
977 type
= mdl_entity_id_type( id
),
978 index
= mdl_entity_id_id( id
);
980 if( type
== k_ent_glider
)
982 if( glider_count
< vg_list_size(glider_list
) )
983 glider_list
[ glider_count
++ ] = index
;
985 else if( type
== k_ent_npc
)
987 if( npc_count
< vg_list_size(npc_list
) )
988 npc_list
[ npc_count
++ ] = index
;
992 shader_model_entity_use();
993 shader_model_entity_uTexMain( 0 );
994 shader_model_entity_uCamera( cam
->transform
[3] );
995 shader_model_entity_uPv( cam
->mtx
.pv
);
996 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_entity
);
998 for( u32 j
=0; j
<glider_count
; j
++ )
1000 ent_glider
*glider
= mdl_arritm( &world
->ent_glider
, glider_list
[j
] );
1002 if( !(glider
->flags
& 0x1) )
1006 mdl_transform_m4x3( &glider
->transform
, mdl
);
1008 f32 dist
= v3_dist( glider
->transform
.co
, cam
->pos
) * (1.0f
/radius
),
1009 scale
= vg_smoothstepf( vg_clampf( 5.0f
-dist
*5.0f
, 0.0f
,1.0f
) );
1010 m3x3_scalef( mdl
, scale
);
1012 render_glider_model( cam
, world
, mdl
, k_board_shader_entity
);
1015 for( u32 j
=0; j
<npc_count
; j
++ )
1017 u32 index
= npc_list
[j
];
1018 ent_npc
*npc
= mdl_arritm( &world
->ent_npc
, npc_list
[j
] );
1020 npc_render( npc
, world
, cam
);
1024 void render_world( world_instance
*world
, vg_camera
*cam
,
1025 int stenciled
, int viewing_from_gate
,
1026 int with_water
, int with_cubemaps
)
1029 glClear( GL_DEPTH_BUFFER_BIT
);
1030 glStencilFunc( GL_EQUAL
, 1, 0xFF );
1031 glStencilMask( 0x00 );
1032 glEnable( GL_CULL_FACE
);
1033 glEnable( GL_STENCIL_TEST
);
1036 glStencilMask( 0xFF );
1037 glStencilFunc( GL_ALWAYS
, 1, 0xFF );
1038 glDisable( GL_STENCIL_TEST
);
1041 render_sky( world
, cam
);
1044 m4x3_identity(identity
);
1045 render_world_routes( world
, world
, identity
, cam
, viewing_from_gate
, 0 );
1046 render_world_standard( world
, cam
);
1047 render_world_cubemapped( world
, cam
, with_cubemaps
);
1049 render_world_vb( world
, cam
);
1050 render_world_alphatest( world
, cam
);
1051 render_world_foliage( world
, cam
);
1052 render_terrain( world
, cam
);
1054 if( !viewing_from_gate
){
1055 world_entity_focus_render();
1059 float min_dist
= INFINITY
;
1061 if( mdl_arrcount( &world
->ent_route
) ){
1062 for( u32 i
=0; i
<mdl_arrcount( &world
->ent_route
); i
++ ){
1063 ent_route
*route
= mdl_arritm( &world
->ent_route
, i
);
1064 float dist
= v3_dist2( route
->board_transform
[3], cam
->pos
);
1066 if( dist
< min_dist
){
1072 ent_route
*route
= mdl_arritm( &world
->ent_route
, closest
);
1073 sfd_render( world
, cam
, route
->board_transform
);
1077 if( !viewing_from_gate
){
1079 if( mdl_entity_id_type(world_static
.focused_entity
) == k_ent_challenge
)
1080 greyout
= world_static
.focus_strength
;
1082 if( greyout
> 0.0f
){
1083 glDrawBuffers( 1, (GLenum
[]){ GL_COLOR_ATTACHMENT0
} );
1085 glDisable(GL_DEPTH_TEST
);
1086 glDepthMask(GL_FALSE
);
1087 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1088 glBlendEquation(GL_FUNC_ADD
);
1090 shader_blitcolour_use();
1091 shader_blitcolour_uColour( (v4f
){ 0.5f
, 0.5f
, 0.5f
, greyout
*0.56f
} );
1094 glDisable(GL_BLEND
);
1095 glEnable(GL_DEPTH_TEST
);
1096 glDepthMask(GL_TRUE
);
1097 glDrawBuffers( 2, (GLenum
[]){ GL_COLOR_ATTACHMENT0
,
1098 GL_COLOR_ATTACHMENT1
} );
1101 render_world_fxglow( world
, world
, cam
, NULL
, 1, 1, 0 );
1106 render_water_texture( world
, cam
);
1107 vg_framebuffer_bind( gpipeline
.fb_main
, k_render_scale
);
1112 glStencilFunc( GL_EQUAL
, 1, 0xFF );
1113 glStencilMask( 0x00 );
1114 glEnable( GL_CULL_FACE
);
1115 glEnable( GL_STENCIL_TEST
);
1120 render_water_surface( world
, cam
);
1123 render_remote_players( world
, cam
);
1124 render_other_entities( world
, cam
);
1125 ent_miniworld_render( world
, cam
);
1129 glStencilMask( 0xFF );
1130 glStencilFunc( GL_ALWAYS
, 1, 0xFF );
1131 glDisable( GL_STENCIL_TEST
);
1136 static void render_world_override_pass( world_instance
*world
,
1137 struct world_pass
*pass
,
1138 m4x3f mmdl
, m3x3f mnormal
,
1141 for( int i
=0; i
<world
->surface_count
; i
++ )
1143 struct world_surface
*mat
= &world
->surfaces
[i
];
1144 if( mat
->info
.flags
& k_material_flag_ghosts
) continue;
1147 if( pass
->geo_type
== k_world_geo_type_solid
)
1150 sm
= &mat
->sm_no_collide
;
1152 if( !sm
->indice_count
)
1155 pass
->fn_set_mdl( mmdl
);
1156 pass
->fn_set_uNormalMtx( mnormal
);
1157 pass
->fn_set_uPvmPrev( mpvm_prev
);
1158 pass
->fn_bind( world
, mat
);
1159 mdl_draw_submesh( sm
);
1163 void render_world_override( world_instance
*world
,
1164 world_instance
*lighting_source
,
1167 ent_spawn
*dest_spawn
, v4f map_info
)
1169 struct world_pass pass
=
1172 .fn_bind
= bindpoint_override
,
1173 .fn_set_mdl
= shader_scene_override_uMdl
,
1174 .fn_set_uPvmPrev
= shader_scene_override_uPvmPrev
,
1175 .fn_set_uNormalMtx
= shader_scene_override_uNormalMtx
,
1176 .shader
= k_shader_override
1179 shader_scene_override_use();
1180 shader_scene_override_uTexGarbage(0);
1181 shader_scene_override_uTexMain(1);
1182 shader_scene_override_uPv( pass
.cam
->mtx
.pv
);
1183 shader_scene_override_uMapInfo( map_info
);
1185 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( lighting_source
, scene_override
);
1186 bind_terrain_noise();
1188 shader_scene_override_uCamera( pass
.cam
->transform
[3] );
1191 m4x3_expand( mmdl
, mpvm_prev
);
1192 m4x4_mul( cam
->mtx_prev
.pv
, mpvm_prev
, mpvm_prev
);
1195 m3x3_inv( mmdl
, mnormal
);
1196 m3x3_transpose( mnormal
, mnormal
);
1197 v3_normalize( mnormal
[0] );
1198 v3_normalize( mnormal
[1] );
1199 v3_normalize( mnormal
[2] );
1201 v4f uPlayerPos
, uSpawnPos
;
1202 v4_zero( uPlayerPos
);
1203 v4_zero( uSpawnPos
);
1204 v3_copy( world
->player_co
, uPlayerPos
);
1206 if( dest_spawn
&& (v3_dist2(dest_spawn
->transform
.co
,uPlayerPos
) > 0.1f
) )
1207 v3_copy( dest_spawn
->transform
.co
, uSpawnPos
);
1209 v3_add( uPlayerPos
, (v3f
){0,-1,0}, uSpawnPos
);
1211 uPlayerPos
[3] = v3_dist(uPlayerPos
,uSpawnPos
);
1212 uSpawnPos
[3] = 1.0f
/uPlayerPos
[3];
1214 shader_scene_override_uPlayerPos( uPlayerPos
);
1215 shader_scene_override_uSpawnPos( uSpawnPos
);
1218 glDisable( GL_CULL_FACE
);
1219 mesh_bind( &world
->mesh_geo
);
1220 pass
.geo_type
= k_world_geo_type_solid
;
1221 render_world_override_pass( world
, &pass
, mmdl
, mnormal
, mpvm_prev
);
1222 mesh_bind( &world
->mesh_no_collide
);
1223 pass
.geo_type
= k_world_geo_type_nonsolid
;
1224 render_world_override_pass( world
, &pass
, mmdl
, mnormal
, mpvm_prev
);
1225 glEnable( GL_CULL_FACE
);
1227 render_world_fxglow( world
, world
, cam
, mmdl
, 0, 0, 1 );
1230 static void render_cubemap_side( world_instance
*world
, ent_cubemap
*cm
,
1233 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
1234 GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ side
, cm
->texture_id
, 0 );
1235 glClear( GL_DEPTH_BUFFER_BIT
);
1238 { -1.0f
, 0.0f
, 0.0f
},
1239 { 1.0f
, 0.0f
, 0.0f
},
1240 { 0.0f
, -1.0f
, 0.0f
},
1241 { 0.0f
, 1.0f
, 0.0f
},
1242 { 0.0f
, 0.0f
, -1.0f
},
1243 { 0.0f
, 0.0f
, 1.0f
}
1246 { 0.0f
, -1.0f
, 0.0f
},
1247 { 0.0f
, -1.0f
, 0.0f
},
1248 { 0.0f
, 0.0f
, 1.0f
},
1249 { 0.0f
, 0.0f
, -1.0f
},
1250 { 0.0f
, -1.0f
, 0.0f
},
1251 { 0.0f
, -1.0f
, 0.0f
}
1254 v3_zero( cam
.angles
);
1255 v3_copy( cm
->co
, cam
.pos
);
1257 v3_copy( forward
[side
], cam
.transform
[2] );
1258 v3_copy( up
[side
], cam
.transform
[1] );
1259 v3_cross( up
[side
], forward
[side
], cam
.transform
[0] );
1260 v3_copy( cm
->co
, cam
.transform
[3] );
1261 m4x3_invert_affine( cam
.transform
, cam
.transform_inverse
);
1263 vg_camera_update_view( &cam
);
1268 m4x4_copy( cam
.mtx
.p
, cam
.mtx_prev
.p
);
1269 m4x4_projection( cam
.mtx
.p
, cam
.fov
, 1.0f
, cam
.nearz
, cam
.farz
);
1270 vg_camera_finalize( &cam
);
1271 vg_camera_finalize( &cam
);
1273 render_world( world
, &cam
, 0, 1, 1, 0 );
1276 void render_world_cubemaps( world_instance
*world
)
1278 if( world
->cubemap_cooldown
)
1279 world
->cubemap_cooldown
--;
1281 world
->cubemap_cooldown
= 60;
1283 glViewport( 0, 0, WORLD_CUBEMAP_RES
, WORLD_CUBEMAP_RES
);
1284 for( u32 i
=0; i
<mdl_arrcount( &world
->ent_cubemap
); i
++ ){
1285 ent_cubemap
*cm
= mdl_arritm( &world
->ent_cubemap
, i
);
1286 glBindFramebuffer( GL_FRAMEBUFFER
, cm
->framebuffer_id
);
1288 world
->cubemap_side
++;
1289 if( world
->cubemap_side
>= 6 )
1290 world
->cubemap_side
= 0;
1292 render_cubemap_side( world
, cm
, world
->cubemap_side
);
1299 * ---------------------------------------------
1302 void render_world_depth( world_instance
*world
, vg_camera
*cam
)
1304 m4x3f identity_matrix
;
1305 m4x3_identity( identity_matrix
);
1307 shader_scene_depth_use();
1308 shader_scene_depth_uCamera( cam
->transform
[3] );
1309 shader_scene_depth_uPv( cam
->mtx
.pv
);
1310 shader_scene_depth_uPvmPrev( cam
->mtx_prev
.pv
);
1311 shader_scene_depth_uMdl( identity_matrix
);
1312 world_link_lighting_ub( world
, _shader_scene_depth
.id
);
1314 mesh_bind( &world
->mesh_geo
);
1315 mesh_draw( &world
->mesh_geo
);
1318 void render_world_position( world_instance
*world
, vg_camera
*cam
)
1320 m4x3f identity_matrix
;
1321 m4x3_identity( identity_matrix
);
1323 shader_scene_position_use();
1324 shader_scene_position_uCamera( cam
->transform
[3] );
1325 shader_scene_position_uPv( cam
->mtx
.pv
);
1326 shader_scene_position_uPvmPrev( cam
->mtx_prev
.pv
);
1327 shader_scene_position_uMdl( identity_matrix
);
1328 world_link_lighting_ub( world
, _shader_scene_position
.id
);
1330 mesh_bind( &world
->mesh_geo
);
1331 mesh_draw( &world
->mesh_geo
);
1334 struct ui_enum_opt skybox_setting_options
[] = {
1335 { 0, "g_daysky_colour" },
1336 { 1, "g_nightsky_colour" },
1337 { 2, "g_sunset_colour" },
1338 { 3, "g_ambient_colour" },
1339 { 4, "g_sun_colour" },
1342 static f32
*skybox_prop_location( world_instance
*world
, i32 index
){
1344 case 0: return world
->ub_lighting
.g_daysky_colour
; break;
1345 case 1: return world
->ub_lighting
.g_nightsky_colour
; break;
1346 case 2: return world
->ub_lighting
.g_sunset_colour
; break;
1347 case 3: return world
->ub_lighting
.g_ambient_colour
; break;
1348 case 4: return world
->ub_lighting
.g_sun_colour
; break;
1349 default: return NULL
;
1353 void imgui_world_light_edit( ui_context
*ctx
, world_instance
*world
)
1355 ui_rect panel
= { vg
.window_x
-400, 0, 400, vg
.window_y
};
1356 ui_fill( ctx
, panel
, ui_colour( ctx
, k_ui_bg
+1 ) );
1357 ui_outline( ctx
, panel
, 1, ui_colour( ctx
, k_ui_bg
+7 ), 0 );
1358 ui_rect_pad( panel
, (ui_px
[2]){ 8, 8 } );
1359 ui_capture_mouse(ctx
, 1);
1361 static i32 option_to_edit
= 0;
1362 ui_enum( ctx
, panel
, "option", skybox_setting_options
, 5, &option_to_edit
);
1363 ui_colourpicker( ctx
, panel
, "colour",
1364 skybox_prop_location( world
, option_to_edit
) );
1366 if( ui_button( ctx
, panel
, "save tweaker file ('/tmp/tweaker.txt')\n" ) == 1 )
1368 FILE *fp
= fopen( "/tmp/tweaker.txt", "w" );
1370 for( i32 i
=0; i
<5; i
++ ){
1371 struct ui_enum_opt
*opt
= &skybox_setting_options
[i
];
1372 f32
*val
= skybox_prop_location( world
, i
);
1373 fprintf( fp
, "%s = {%.3ff, %.3ff, %.3ff, %.3ff},\n",
1374 opt
->alias
, val
[0], val
[1], val
[2], val
[3] );