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