grind sound
[carveJwlIkooP6JGAAIwe30JlM.git] / world.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #include "common.h"
6
7 VG_STATIC int ray_world( v3f pos, v3f dir, ray_hit *hit );
8
9 #ifndef WORLD_H
10 #define WORLD_H
11
12 #include "vg/vg_loader.h"
13
14 #include "network.h"
15 #include "network_msg.h"
16 #include "scene.h"
17 #include "render.h"
18 #include "rigidbody.h"
19 #include "bvh.h"
20 #include "model.h"
21
22 #include "shaders/terrain.h"
23 #include "shaders/sky.h"
24 #include "shaders/planeinf.h"
25 #include "shaders/standard.h"
26 #include "shaders/vblend.h"
27 #include "shaders/gpos.h"
28 #include "shaders/fscolour.h"
29 #include "shaders/alphatest.h"
30
31 typedef struct teleport_gate teleport_gate;
32
33 enum { k_max_ui_segments = 8 };
34
35 enum { k_max_ui_elements = k_max_ui_segments };
36 enum { k_max_element_verts = 10 };
37 enum { k_max_element_indices = 20 };
38
39 enum { k_route_ui_max_verts = k_max_ui_elements*k_max_element_verts };
40 enum { k_route_ui_max_indices = k_max_ui_elements*k_max_element_indices };
41
42 enum logic_type
43 {
44 k_logic_type_relay = 1,
45 k_logic_type_chance = 2,
46 k_logic_type_achievement = 3
47 };
48
49 enum geo_type
50 {
51 k_geo_type_solid = 0,
52 k_geo_type_nonsolid = 1,
53 k_geo_type_water = 2
54 };
55
56 VG_STATIC struct gworld
57 {
58 /*
59 * Allocated as system memory
60 * --------------------------------------------------------------------------
61 */
62
63 /* rendering */
64 glmesh skydome;
65 mdl_submesh dome_upper, dome_lower;
66
67 glmesh mesh_gate_surface;
68
69 double sky_time, sky_rate, sky_target_rate;
70
71 /* water rendering */
72 struct
73 {
74 struct framebuffer fbreflect, fbdepth;
75
76 boxf depthbounds;
77 int depth_computed;
78
79 float height;
80 int enabled;
81 v4f plane;
82 }
83 water;
84
85 /* split flap display */
86 struct
87 {
88 mdl_submesh *sm_module, *sm_card;
89 glmesh mesh_base, mesh_display;
90
91 u32 w, h;
92 float *buffer;
93 }
94 sfd;
95
96 /* timing bars, fixed maximum amount */
97 struct route_ui_bar
98 {
99 GLuint vao, vbo, ebo;
100
101 u32 indices_head;
102 u32 vertex_head;
103
104 struct route_ui_segment
105 {
106 float length;
107 u32 vertex_start, vertex_count,
108 index_start, index_count, notches;
109 }
110 segments[k_max_ui_segments];
111
112 u32 segment_start, segment_count, fade_start, fade_count;
113 double fade_timer_start;
114 float xpos;
115 }
116 ui_bars[16];
117
118 v3f render_gate_pos;
119 int active_route_board;
120 int in_trigger;
121
122 /* This is a small flag we use to changelevel.
123 * It will not be cleared until all sounds stop playing
124 */
125 int switching_to_new_world;
126 char world_name[ 64 ];
127
128 /*
129 * Dynamically allocated when world_load is called.
130 *
131 * the following arrays index somewhere into this linear
132 * allocator
133 *
134 * (world_gen.h)
135 * --------------------------------------------------------------------------
136 */
137 void *dynamic_vgl,
138 *audio_vgl; /* sub buffer of the audio buffer */
139
140 /*
141 * Main world .mdl
142 */
143 mdl_context *meta;
144
145 /*
146 * Materials / textures
147 */
148
149 GLuint *textures;
150 u32 texture_count;
151
152 struct world_material
153 {
154 mdl_material info;
155 mdl_submesh sm_geo,
156 sm_no_collide;
157 }
158 * materials;
159 u32 material_count;
160
161 /*
162 * Named safe places to respawn
163 */
164 struct respawn_point
165 {
166 v3f co;
167 v4f q;
168 const char *name;
169 }
170 * spawns;
171 u32 spawn_count;
172
173 /*
174 * Audio player entities
175 */
176 struct world_audio_thing
177 {
178 v3f pos;
179 float volume;
180 u32 flags;
181
182 audio_player player;
183 audio_clip temp_embedded_clip;
184 }
185 * audio_things;
186 u32 audio_things_count;
187
188 /*
189 * Relays
190 */
191 struct logic_relay
192 {
193 v3f pos;
194
195 struct relay_target
196 {
197 u32 sub_id;
198 enum classtype classtype;
199 }
200 targets[4];
201 u32 target_count;
202 }
203 * logic_relays;
204 u32 relay_count;
205
206 /*
207 * Box trigger entities
208 */
209 struct trigger_zone
210 {
211 m4x3f transform, inv_transform;
212
213 struct relay_target target;
214 }
215 * triggers;
216 u32 trigger_count;
217
218 /*
219 * Achievements
220 */
221 struct logic_achievement
222 {
223 v3f pos;
224 const char *achievement_id;
225 u32 achieved;
226 }
227 * logic_achievements;
228 u32 achievement_count;
229
230
231 /*
232 * Routes (world_routes.h)
233 * --------------------------------------------------------------------------
234 */
235 struct route_node
236 {
237 v3f co, right, up, h;
238 u32 next[2];
239
240 u32 special_type, special_id, current_refs, ref_count;
241 u32 route_ids[4]; /* Gates can be linked into up to four routes */
242 }
243 *nodes;
244 u32 node_count;
245
246 struct route
247 {
248 u32 track_id;
249 v4f colour;
250
251 u32 start;
252 mdl_submesh sm;
253
254 int active;
255 float factive;
256
257 double best_lap, latest_pass; /* Session */
258
259 m4x3f scoreboard_transform;
260 }
261 *routes;
262 u32 route_count;
263
264 struct route_gate
265 {
266 struct teleport_gate
267 {
268 v3f co[2];
269 v4f q[2];
270 v2f dims;
271
272 m4x3f to_world, recv_to_world, transport;
273 }
274 gate;
275
276 u32 node_id;
277
278 struct route_timing
279 {
280 u32 version; /* Incremented on every teleport */
281 double time;
282 }
283 timing;
284 }
285 *gates;
286 u32 gate_count;
287
288 struct route_collector
289 {
290 struct route_timing timing;
291 }
292 *collectors;
293 u32 collector_count;
294
295
296 /* logic
297 * ----------------------------------------------------
298 */
299
300 u32 active_gate,
301 current_run_version;
302 double time, rewind_from, rewind_to, last_use;
303
304 /* world geometry */
305 scene *scene_geo,
306 *scene_no_collide,
307 *scene_lines;
308
309 struct grind_edge
310 {
311 v3f p0, p1;
312 }
313 *grind_edges;
314 u32 grind_edge_count;
315
316 /* spacial mappings */
317 bh_tree *audio_bh,
318 *trigger_bh,
319 *geo_bh,
320 *grind_bh;
321
322 /* graphics */
323 glmesh mesh_route_lines;
324
325 glmesh mesh_geo,
326 mesh_no_collide,
327 mesh_water;
328
329 mdl_submesh sm_foliage_main;
330 rigidbody rb_geo;
331 }
332 world;
333
334
335 /*
336 * API
337 */
338
339 VG_STATIC int ray_hit_is_ramp( ray_hit *hit );
340 VG_STATIC struct world_material *ray_hit_material( ray_hit *hit );
341 VG_STATIC void ray_world_get_tri( ray_hit *hit, v3f tri[3] );
342 VG_STATIC int ray_world( v3f pos, v3f dir, ray_hit *hit );
343
344 /*
345 * Submodules
346 */
347
348 #include "world_routes.h"
349 #include "world_sfd.h"
350 #include "world_render.h"
351 #include "world_water.h"
352 #include "world_gen.h"
353 #include "world_gate.h"
354
355 /*
356 * -----------------------------------------------------------------------------
357 * Events
358 * -----------------------------------------------------------------------------
359 */
360
361 VG_STATIC int world_stop_sound( int argc, const char *argv[] )
362 {
363 /*
364 * None of our world audio runs as one shots, they always have a player.
365 * Therefore it is safe to delete clip data after the players are
366 * disconnected
367 */
368 audio_lock();
369 for( int i=0; i<world.audio_things_count; i++ )
370 {
371 struct world_audio_thing *at = &world.audio_things[i];
372
373 if( audio_player_is_playing( &at->player ) )
374 {
375 u32 cflags = audio_player_get_flags( &at->player );
376 audio_player_set_flags( &at->player, cflags | AUDIO_FLAG_KILL );
377 }
378 }
379 audio_unlock();
380
381 return 0;
382 }
383
384 VG_STATIC int world_change_world( int argc, const char *argv[] )
385 {
386 if( argc == 0 )
387 {
388 vg_info( "%s\n", world.world_name );
389 return 0;
390 }
391 else
392 {
393 vg_info( "Switching world...\n" );
394 strcpy( world.world_name, argv[0] );
395 world.switching_to_new_world = 1;
396 world_stop_sound( 0, NULL );
397 }
398
399 return 0;
400 }
401
402 VG_STATIC void world_init(void)
403 {
404 vg_convar_push( (struct vg_convar){
405 .name = "water_enable",
406 .data = &world.water.enabled,
407 .data_type = k_convar_dtype_i32,
408 .opt_i32 = { .min=0, .max=1, .clamp=1 },
409 .persistent = 0
410 });
411
412 vg_function_push( (struct vg_cmd)
413 {
414 .name = "world_stop_sound",
415 .function = world_stop_sound
416 });
417
418 vg_function_push( (struct vg_cmd)
419 {
420 .name = "world",
421 .function = world_change_world
422 });
423
424 world.sky_rate = 1.0;
425 world.sky_target_rate = 1.0;
426
427 shader_terrain_register();
428 shader_sky_register();
429 shader_planeinf_register();
430 shader_gpos_register();
431 shader_fscolour_register();
432 shader_alphatest_register();
433
434 vg_info( "Loading world resources\n" );
435
436 vg_linear_clear( vg_mem.scratch );
437 mdl_context *msky = mdl_load_full( vg_mem.scratch, "models/rs_skydome.mdl" );
438
439 mdl_node *nlower = mdl_node_from_name( msky, "dome_lower" ),
440 *nupper = mdl_node_from_name( msky, "dome_upper" );
441
442 world.dome_lower = *mdl_node_submesh( msky, nlower, 0 );
443 world.dome_upper = *mdl_node_submesh( msky, nupper, 0 );
444
445 vg_acquire_thread_sync();
446 {
447 mdl_unpack_glmesh( msky, &world.skydome );
448 }
449 vg_release_thread_sync();
450
451 /* Other systems */
452 vg_info( "Loading other world systems\n" );
453
454 vg_loader_step( world_render_init, NULL );
455 vg_loader_step( world_sfd_init, NULL );
456 vg_loader_step( world_water_init, NULL );
457 vg_loader_step( world_gates_init, NULL );
458 vg_loader_step( world_routes_init, NULL );
459
460 /* Allocate dynamic world memory arena */
461 u32 max_size = 76*1024*1024;
462 world.dynamic_vgl = vg_create_linear_allocator( vg_mem.rtmemory, max_size,
463 VG_MEMORY_SYSTEM );
464 }
465
466 VG_STATIC void world_audio_init(void)
467 {
468 u32 size = vg_linear_remaining( vg_audio.audio_pool )
469 - sizeof(vg_linear_allocator);
470
471 world.audio_vgl = vg_create_linear_allocator( vg_audio.audio_pool,
472 size, VG_MEMORY_SYSTEM );
473 }
474
475 VG_STATIC void world_trigger_achievement( u32 uid )
476 {
477 struct logic_achievement *ach = &world.logic_achievements[ uid ];
478
479 if( ach->achieved )
480 return;
481
482 steam_set_achievement( ach->achievement_id );
483 steam_store_achievements();
484
485 ach->achieved = 1;
486 }
487
488 VG_STATIC void world_run_relay( struct relay_target *rt );
489 VG_STATIC void world_trigger_relay( u32 uid )
490 {
491 struct logic_relay *relay = &world.logic_relays[ uid ];
492
493 for( int i=0; i<relay->target_count; i++ )
494 {
495 world_run_relay( &relay->targets[i] );
496 }
497 }
498
499 VG_STATIC void world_trigger_audio( u32 uid )
500 {
501 struct world_audio_thing *wat = &world.audio_things[ uid ];
502
503 audio_lock();
504 audio_player_playclip( &wat->player,
505 &wat->temp_embedded_clip );
506 audio_unlock();
507 }
508
509 VG_STATIC void world_run_relay( struct relay_target *rt )
510 {
511 struct entity_instruction
512 {
513 enum classtype classtype;
514 void (*p_trigger)( u32 uid );
515 }
516 entity_instructions[] =
517 {
518 { k_classtype_logic_achievement, world_trigger_achievement },
519 { k_classtype_logic_relay, world_trigger_relay },
520 { k_classtype_audio, world_trigger_audio }
521 };
522
523 for( int i=0; i<vg_list_size(entity_instructions); i++ )
524 {
525 struct entity_instruction *instr = &entity_instructions[i];
526
527 if( instr->classtype == rt->classtype )
528 {
529 instr->p_trigger( rt->sub_id );
530 return;
531 }
532 }
533
534 vg_error( "Don't know how to trigger classtype %d\n", rt->classtype );
535 }
536
537 VG_STATIC void world_update( v3f pos )
538 {
539 if( world.switching_to_new_world )
540 {
541 int all_stopped = 1;
542
543 audio_lock();
544 for( int i=0; i<world.audio_things_count; i++ )
545 {
546 struct world_audio_thing *at = &world.audio_things[i];
547
548 if( audio_player_is_playing( &at->player ) )
549 {
550 all_stopped = 0;
551 break;
552 }
553 }
554 audio_unlock();
555
556 if( all_stopped )
557 {
558 world.switching_to_new_world = 0;
559 world_unload();
560 vg_loader_start( world_load );
561 return;
562 }
563 }
564
565 world.sky_time += world.sky_rate * vg.time_delta;
566 world.sky_rate = vg_lerp( world.sky_rate, world.sky_target_rate,
567 vg.time_delta * 5.0 );
568
569 world_routes_update();
570 #if 0
571 world_routes_debug();
572 #endif
573
574 if( world.route_count > 0 )
575 {
576 int closest = 0;
577 float min_dist = INFINITY;
578
579 for( int i=0; i<world.route_count; i++ )
580 {
581 float d = v3_dist2( world.routes[i].scoreboard_transform[3], pos );
582
583 if( d < min_dist )
584 {
585 min_dist = d;
586 closest = i;
587 }
588 }
589
590 if( (world.active_route_board != closest) || network_scores_updated )
591 {
592 network_scores_updated = 0;
593 world.active_route_board = closest;
594
595 struct route *route = &world.routes[closest];
596
597 u32 id = route->track_id;
598
599 if( id != 0xffffffff )
600 {
601 struct netmsg_board *local_board =
602 &scoreboard_client_data.boards[id];
603
604 for( int i=0; i<13; i++ )
605 {
606 sfd_encode( i, &local_board->data[27*i] );
607 }
608 }
609 }
610 }
611
612 int in_trigger = 0;
613 for( int i=0; i<world.trigger_count; i++ )
614 {
615 struct trigger_zone *zone = &world.triggers[i];
616
617 v3f local;
618 m4x3_mulv( zone->inv_transform, pos, local );
619
620 if( (fabsf(local[0]) <= 1.0f) &&
621 (fabsf(local[1]) <= 1.0f) &&
622 (fabsf(local[2]) <= 1.0f) )
623 {
624 in_trigger = 1;
625
626 if( !world.in_trigger )
627 {
628 world_run_relay( &zone->target );
629 }
630 }
631
632 vg_line_boxf_transformed( zone->transform, (boxf){{-1.0f,-1.0f,-1.0f},
633 { 1.0f, 1.0f, 1.0f}},
634 0xff00ff00 );
635 }
636
637 world.in_trigger = in_trigger;
638 sfd_update();
639 }
640
641 /*
642 * -----------------------------------------------------------------------------
643 * API implementation
644 * -----------------------------------------------------------------------------
645 */
646
647 VG_STATIC void ray_world_get_tri( ray_hit *hit, v3f tri[3] )
648 {
649 for( int i=0; i<3; i++ )
650 v3_copy( world.scene_geo->arrvertices[ hit->tri[i] ].co, tri[i] );
651 }
652
653 VG_STATIC int ray_world( v3f pos, v3f dir, ray_hit *hit )
654 {
655 return scene_raycast( world.scene_geo, world.geo_bh, pos, dir, hit );
656 }
657
658 VG_STATIC struct world_material *world_tri_index_material( u32 index )
659 {
660 for( int i=1; i<world.material_count; i++ )
661 {
662 struct world_material *mat = &world.materials[i];
663
664 if( (index >= mat->sm_geo.vertex_start) &&
665 (index < mat->sm_geo.vertex_start+mat->sm_geo.vertex_count ) )
666 {
667 return mat;
668 }
669 }
670
671 /* error material */
672 return &world.materials[0];
673 }
674
675 VG_STATIC struct world_material *world_contact_material( rb_ct *ct )
676 {
677 return world_tri_index_material( ct->element_id );
678 }
679
680 VG_STATIC struct world_material *ray_hit_material( ray_hit *hit )
681 {
682 return world_tri_index_material( hit->tri[0] );
683 }
684
685 #endif /* WORLD_H */