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