wowwww
[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 #ifndef WORLD_H
8 #define WORLD_H
9
10 typedef struct world_instance world_instance;
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 #include "entity.h"
22
23 #include "shaders/scene_standard.h"
24 #include "shaders/scene_standard_alphatest.h"
25 #include "shaders/scene_vertex_blend.h"
26 #include "shaders/scene_terrain.h"
27 #include "shaders/scene_depth.h"
28 #include "shaders/scene_position.h"
29
30 #include "shaders/model_sky.h"
31
32 enum { k_max_ui_segments = 8 };
33
34 enum { k_max_ui_elements = k_max_ui_segments };
35 enum { k_max_element_verts = 10 };
36 enum { k_max_element_indices = 20 };
37
38 enum { k_route_ui_max_verts = k_max_ui_elements*k_max_element_verts };
39 enum { k_route_ui_max_indices = k_max_ui_elements*k_max_element_indices };
40
41 enum logic_type
42 {
43 k_logic_type_relay = 1,
44 k_logic_type_chance = 2,
45 k_logic_type_achievement = 3
46 };
47
48 enum geo_type
49 {
50 k_geo_type_solid = 0,
51 k_geo_type_nonsolid = 1,
52 k_geo_type_water = 2
53 };
54
55 static const float k_light_cube_size = 8.0f;
56
57 struct world_instance
58 {
59 /* This is a small flag we use to changelevel.
60 * It will not be cleared until all sounds stop playing
61 */
62
63 /* Fixed items
64 * -------------------------------------------------------
65 */
66
67 char world_name[ 64 ];
68
69 struct
70 {
71 boxf depthbounds;
72 int depth_computed;
73
74 float height;
75 int enabled;
76 v4f plane;
77 }
78 water;
79
80 /* STD140 */
81 struct ub_world_lighting
82 {
83 v4f g_cube_min,
84 g_cube_inv_range;
85
86 v4f g_water_plane,
87 g_depth_bounds;
88
89 v4f g_daysky_colour;
90 v4f g_nightsky_colour;
91 v4f g_sunset_colour;
92 v4f g_ambient_colour;
93 v4f g_sunset_ambient;
94 v4f g_sun_colour;
95 v4f g_sun_dir;
96
97 float g_water_fog;
98 float g_time;
99 float g_realtime;
100 float g_shadow_length;
101 float g_shadow_spread;
102
103 float g_time_of_day;
104 float g_day_phase;
105 float g_sunset_phase;
106
107 int g_light_preview;
108 int g_shadow_samples;
109
110 int g_debug_indices;
111 int g_debug_complexity;
112 }
113 ub_lighting;
114 GLuint ubo_lighting;
115 int ubo_bind_point;
116
117 GLuint tbo_light_entities,
118 tex_light_entities,
119 tex_light_cubes;
120
121 float probabilities[3];
122
123 v3i light_cubes;
124
125 struct framebuffer heightmap;
126
127 /*
128 * Dynamically allocated when world_load is called.
129 *
130 * the following arrays index somewhere into this linear
131 * allocator
132 *
133 * (world_gen.h)
134 * --------------------------------------------------------------------------
135 */
136 /*
137 * Main world .mdl
138 */
139 mdl_context meta;
140
141 GLuint *textures;
142 u32 texture_count;
143
144 struct world_surface
145 {
146 mdl_material info;
147 mdl_submesh sm_geo,
148 sm_no_collide;
149 }
150 * surfaces;
151 u32 surface_count;
152
153 mdl_array_ptr ent_spawn,
154 ent_gate,
155 ent_light,
156 ent_route_node,
157 ent_path_index,
158 ent_checkpoint,
159 ent_route,
160 ent_water,
161
162 ent_audio_clip,
163 ent_audio,
164 ent_volume;
165
166 ent_gate *rendering_gate;
167
168 #if 0
169 /*
170 * Named safe places to respawn
171 */
172 struct respawn_point
173 {
174 v3f co;
175 v4f q;
176 const char *name;
177 }
178 * spawns;
179 u32 spawn_count;
180
181 /*
182 * Audio player entities
183 */
184 struct world_audio_thing
185 {
186 v3f pos;
187 float volume, range;
188 u32 flags;
189 audio_clip temp_embedded_clip;
190 }
191 * audio_things;
192 u32 audio_things_count;
193
194 struct soundscape
195 {
196 /* locking */
197 audio_channel *channels[4];
198
199 /* accessable without locking */
200 v3f spawn_position;
201
202 u32 usage_count;
203 u32 max_instances;
204 u32 allow_transitions;
205 float transition_duration;
206 const char *label;
207 }
208 * soundscapes;
209 u32 soundscape_count;
210
211 /*
212 * Box volume entities
213 */
214 struct world_volume
215 {
216 m4x3f transform, inv_transform;
217 mdl_node *node;
218 }
219 * volumes;
220 u32 volume_count;
221
222 /*
223 * Lights
224 */
225 struct world_light
226 {
227 mdl_node *node;
228 struct classtype_world_light *inf;
229 m4x3f inverse_world;
230 v2f angle_sin_cos;
231 }
232 * lights;
233 u32 light_count;
234
235 /*
236 * Routes (world_routes.h)
237 * --------------------------------------------------------------------------
238 */
239
240 struct route_node
241 {
242 v3f co, right, up, h;
243 u32 next[2];
244
245 u32 special_type, special_id, current_refs, ref_count;
246 u32 route_ids[4]; /* Gates can be linked into up to four routes */
247 }
248 *nodes;
249 u32 node_count;
250
251 struct route
252 {
253 u32 track_id;
254 v4f colour;
255
256 u32 start;
257 mdl_submesh sm;
258
259 int active;
260 float factive;
261
262 double best_lap, latest_pass; /* Session */
263
264 m4x3f scoreboard_transform;
265 }
266 *routes;
267 u32 route_count;
268
269 struct route_gate
270 {
271 struct teleport_gate
272 {
273 v3f co[2];
274 v4f q[2];
275 v2f dims;
276
277 m4x3f to_world, transport;
278 }
279 gate;
280
281 u32 node_id;
282
283 struct route_timing
284 {
285 u32 version; /* Incremented on every teleport */
286 double time;
287 }
288 timing;
289 }
290 *gates;
291 u32 gate_count;
292
293 struct nonlocal_gate
294 {
295 struct teleport_gate gate;
296 mdl_node *node;
297
298 u32 target_map_index, working;
299 }
300 *nonlocal_gates;
301 u32 nonlocalgate_count;
302
303 struct route_collector
304 {
305 struct route_timing timing;
306 }
307 *collectors;
308 u32 collector_count;
309 #endif
310
311
312 /* logic
313 * ----------------------------------------------------
314 */
315
316 /* world geometry */
317 scene *scene_geo,
318 *scene_no_collide,
319 *scene_lines;
320
321 /* spacial mappings */
322 bh_tree *audio_bh,
323 *volume_bh,
324 *geo_bh;
325
326 /* graphics */
327 glmesh mesh_route_lines;
328 glmesh mesh_geo,
329 mesh_no_collide,
330 mesh_water;
331
332 rigidbody rb_geo; /* todo.. ... */
333 };
334
335 VG_STATIC struct world_global
336 {
337 /*
338 * Allocated as system memory
339 * --------------------------------------------------------------------------
340 */
341 void *generic_heap;
342
343 /* rendering */
344 glmesh skydome;
345 glmesh mesh_gate;
346 mdl_submesh sm_gate_surface,
347 sm_gate_marker[4];
348
349 double sky_time, sky_rate, sky_target_rate;
350
351 u32 current_run_version;
352 double time, rewind_from, rewind_to, last_use;
353
354 /* water rendering */
355 struct
356 {
357 struct framebuffer fbreflect, fbdepth;
358 }
359 water;
360
361 /* split flap display */
362 struct
363 {
364 glmesh mesh_base, mesh_display;
365 mdl_submesh sm_base;
366 u32 active_route_board;
367
368 u32 w, h;
369 float *buffer;
370 }
371 sfd;
372
373 v3f render_gate_pos;
374 int in_volume;
375
376 int switching_to_new_world;
377
378 world_instance worlds[4];
379 u32 world_count;
380 u32 active_world;
381 }
382 world_global;
383
384 VG_STATIC world_instance *get_active_world( void )
385 {
386 return &world_global.worlds[ world_global.active_world ];
387 }
388
389 /*
390 * API
391 */
392
393 VG_STATIC
394 int ray_hit_is_ramp( world_instance *world, ray_hit *hit );
395
396 VG_STATIC
397 struct world_surface *ray_hit_surface( world_instance *world, ray_hit *hit );
398
399 VG_STATIC
400 void ray_world_get_tri( world_instance *world, ray_hit *hit, v3f tri[3] );
401
402 VG_STATIC
403 int ray_world( world_instance *world, v3f pos, v3f dir, ray_hit *hit );
404
405 /*
406 * Submodules
407 */
408
409 #include "world_routes.h"
410 #include "world_sfd.h"
411 #include "world_render.h"
412 #include "world_water.h"
413 #include "world_volumes.h"
414 #include "world_gen.h"
415 #include "world_gate.h"
416
417 /*
418 * -----------------------------------------------------------------------------
419 * Events
420 * -----------------------------------------------------------------------------
421 */
422
423 VG_STATIC int world_stop_sound( int argc, const char *argv[] )
424 {
425 world_instance *world = get_active_world();
426 return 0;
427 }
428
429 VG_STATIC void world_init(void)
430 {
431 world_global.sky_rate = 1.0;
432 world_global.sky_target_rate = 1.0;
433
434 shader_scene_standard_register();
435 shader_scene_standard_alphatest_register();
436 shader_scene_vertex_blend_register();
437 shader_scene_terrain_register();
438 shader_scene_depth_register();
439 shader_scene_position_register();
440
441 shader_model_sky_register();
442
443 vg_info( "Loading world resources\n" );
444
445 vg_linear_clear( vg_mem.scratch );
446
447 mdl_context msky;
448 mdl_open( &msky, "models/rs_skydome.mdl", vg_mem.scratch );
449 mdl_load_metadata_block( &msky, vg_mem.scratch );
450 mdl_load_mesh_block( &msky, vg_mem.scratch );
451 mdl_close( &msky );
452
453 vg_acquire_thread_sync();
454 {
455 mdl_unpack_glmesh( &msky, &world_global.skydome );
456 }
457 vg_release_thread_sync();
458
459 /* Other systems */
460 vg_info( "Loading other world systems\n" );
461
462 vg_loader_step( world_render_init, NULL );
463 vg_loader_step( world_sfd_init, NULL );
464 vg_loader_step( world_water_init, NULL );
465 vg_loader_step( world_gates_init, NULL );
466 vg_loader_step( world_routes_init, NULL );
467
468 /* Allocate dynamic world memory arena */
469 u32 max_size = 76*1024*1024;
470 world_global.generic_heap = vg_create_linear_allocator( vg_mem.rtmemory,
471 max_size,
472 VG_MEMORY_SYSTEM );
473 }
474
475 typedef struct ent_call ent_call;
476 struct ent_call{
477 ent_index ent;
478 u32 function;
479 void *data;
480 };
481
482 VG_STATIC void entity_call( world_instance *world, ent_call *call );
483
484 VG_STATIC void ent_volume_call( world_instance *world, ent_call *call )
485 {
486 ent_volume *volume = mdl_arritm( &world->ent_volume, call->ent.index );
487 if( !volume->target.type ) return;
488
489 if( call->function == k_ent_function_trigger ){
490 call->ent = volume->target;
491
492 if( volume->type == k_volume_subtype_particle ){
493 v3f co;
494 co[0] = vg_randf()*2.0f-1.0f;
495 co[1] = vg_randf()*2.0f-1.0f;
496 co[2] = vg_randf()*2.0f-1.0f;
497 m4x3_mulv( volume->to_world, co, co );
498
499 call->function = k_ent_function_particle_spawn;
500 call->data = co;
501 entity_call( world, call );
502 }
503 else
504 entity_call( world, call );
505 }
506 }
507
508 VG_STATIC void ent_audio_call( world_instance *world, ent_call *call )
509 {
510 ent_audio *audio = mdl_arritm( &world->ent_audio, call->ent.index );
511
512 v3f sound_co;
513
514 if( call->function == k_ent_function_particle_spawn ){
515 v3_copy( call->data, sound_co );
516 }
517 else if( call->function == k_ent_function_trigger ){
518 v3_copy( audio->transform.co, sound_co );
519 }
520 else
521 vg_fatal_exit_loop( "ent_audio_call (invalid function id)" );
522
523 float chance = vg_randf()*100.0f,
524 bar = 0.0f;
525
526 for( u32 i=0; i<audio->clip_count; i++ ){
527 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
528 audio->clip_start+i );
529
530 float mod = world->probabilities[ audio->probability_curve ],
531 p = clip->probability * mod;
532
533 bar += p;
534
535 if( chance < bar ){
536
537 audio_lock();
538
539 if( audio->behaviour == k_channel_behaviour_unlimited ){
540 audio_oneshot_3d( &clip->clip, sound_co,
541 audio->transform.s[0],
542 audio->volume );
543 }
544 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
545 audio_channel *ch =
546 audio_get_group_idle_channel( audio->group,
547 audio->max_channels );
548
549 if( ch ){
550 audio_channel_init( ch, &clip->clip, audio->flags );
551 audio_channel_group( ch, audio->group );
552 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
553 audio_channel_edit_volume( ch, audio->volume, 1 );
554 ch = audio_relinquish_channel( ch );
555 }
556 }
557 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
558 audio_channel *ch =
559 audio_get_group_idle_channel( audio->group,
560 audio->max_channels );
561
562 /* group is full */
563 if( !ch ){
564 audio_channel *existing =
565 audio_get_group_first_active_channel( audio->group );
566
567 if( existing ){
568 if( existing->source == &clip->clip ){
569 audio_unlock();
570 return;
571 }
572
573 existing->group = 0;
574 existing = audio_channel_fadeout(existing, audio->crossfade);
575 }
576
577 ch = audio_get_first_idle_channel();
578 }
579
580 if( ch ){
581 audio_channel_init( ch, &clip->clip, audio->flags );
582 audio_channel_group( ch, audio->group );
583 audio_channel_fadein( ch, audio->crossfade );
584 ch = audio_relinquish_channel( ch );
585 }
586 }
587
588 audio_unlock();
589 return;
590 }
591 }
592 }
593
594 VG_STATIC void entity_call( world_instance *world, ent_call *call )
595 {
596 if( call->ent.type == k_ent_volume ){
597 ent_volume_call( world, call );
598 } else if( call->ent.type == k_ent_audio ){
599 ent_audio_call( world, call );
600 }
601 }
602
603 VG_STATIC void world_update( world_instance *world, v3f pos )
604 {
605 /* TEMP!!!!!! */
606 static double g_time = 0.0;
607 g_time += vg.time_delta * (1.0/(k_day_length*60.0));
608
609
610 struct ub_world_lighting *state = &world->ub_lighting;
611
612 state->g_time = g_time;
613 state->g_realtime = vg.time;
614 state->g_debug_indices = k_debug_light_indices;
615 state->g_light_preview = k_light_preview;
616 state->g_debug_complexity = k_debug_light_complexity;
617
618 state->g_time_of_day = vg_fractf( g_time );
619 state->g_day_phase = cosf( state->g_time_of_day * VG_PIf * 2.0f );
620 state->g_sunset_phase= cosf( state->g_time_of_day * VG_PIf * 4.0f + VG_PIf );
621
622 state->g_day_phase = state->g_day_phase * 0.5f + 0.5f;
623 state->g_sunset_phase = powf( state->g_sunset_phase * 0.5f + 0.5f, 6.0f );
624
625 float a = state->g_time_of_day * VG_PIf * 2.0f;
626 state->g_sun_dir[0] = sinf( a );
627 state->g_sun_dir[1] = cosf( a );
628 state->g_sun_dir[2] = 0.2f;
629 v3_normalize( state->g_sun_dir );
630
631
632 world->probabilities[ k_probability_curve_constant ] = 1.0f;
633
634 float dp = state->g_day_phase;
635
636 world->probabilities[ k_probability_curve_wildlife_day ] =
637 (dp*dp*0.8f+state->g_sunset_phase)*0.8f;
638 world->probabilities[ k_probability_curve_wildlife_night ] =
639 1.0f-powf(fabsf((state->g_time_of_day-0.5f)*5.0f),5.0f);
640
641
642 glBindBuffer( GL_UNIFORM_BUFFER, world->ubo_lighting );
643 glBufferSubData( GL_UNIFORM_BUFFER, 0,
644 sizeof(struct ub_world_lighting), &world->ub_lighting );
645 /* TEMP!!!!!! */
646
647 world_global.sky_time += world_global.sky_rate * vg.time_delta;
648 world_global.sky_rate = vg_lerp( world_global.sky_rate,
649 world_global.sky_target_rate,
650 vg.time_delta * 5.0 );
651
652 world_routes_update( world );
653 world_routes_debug( world );
654
655 /* ---- SFD ------------ */
656
657 if( mdl_arrcount( &world->ent_route ) ){
658 u32 closest = 0;
659 float min_dist = INFINITY;
660
661 for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i++ ){
662 ent_route *route = mdl_arritm( &world->ent_route, i );
663 float dist = v3_dist2( route->board_transform[3], pos );
664
665 if( dist < min_dist ){
666 min_dist = dist;
667 closest = i;
668 }
669 }
670
671 if( (world_global.sfd.active_route_board != closest)
672 || network_scores_updated )
673 {
674 network_scores_updated = 0;
675 world_global.sfd.active_route_board = closest;
676
677 ent_route *route = mdl_arritm( &world->ent_route, closest );
678 u32 id = route->official_track_id;
679
680 if( id != 0xffffffff ){
681 struct netmsg_board *local_board =
682 &scoreboard_client_data.boards[id];
683
684 for( int i=0; i<13; i++ ){
685 sfd_encode( i, &local_board->data[27*i] );
686 }
687 }else{
688 sfd_encode( 0, mdl_pstr( &world->meta, route->pstr_name ) );
689 sfd_encode( 1, "No data" );
690 }
691 }
692 }
693 sfd_update();
694
695
696
697 static float random_accum = 0.0f;
698 random_accum += vg.time_delta;
699
700 u32 random_ticks = 0;
701
702 while( random_accum > 0.1f ){
703 random_accum -= 0.1f;
704 random_ticks ++;
705 }
706
707 float radius = 25.0f;
708 boxf volume_proximity;
709 v3_add( pos, (v3f){ radius, radius, radius }, volume_proximity[1] );
710 v3_sub( pos, (v3f){ radius, radius, radius }, volume_proximity[0] );
711
712 bh_iter it;
713 bh_iter_init( 0, &it );
714 int idx;
715
716 int in_volume = 0;
717
718 while( bh_next( world->volume_bh, &it, volume_proximity, &idx ) ){
719 ent_volume *volume = mdl_arritm( &world->ent_volume, idx );
720
721 boxf cube = {{-1.0f,-1.0f,-1.0f},{1.0f,1.0f,1.0f}};
722
723 if( volume->type == k_volume_subtype_trigger ){
724 v3f local;
725 m4x3_mulv( volume->to_local, pos, local );
726
727 if( (fabsf(local[0]) <= 1.0f) &&
728 (fabsf(local[1]) <= 1.0f) &&
729 (fabsf(local[2]) <= 1.0f) )
730 {
731 in_volume = 1;
732 vg_line_boxf_transformed( volume->to_world, cube, 0xff00ff00 );
733
734 if( !world_global.in_volume ){
735 ent_call basecall;
736 basecall.ent.index = idx;
737 basecall.ent.type = k_ent_volume;
738 basecall.function = k_ent_function_trigger;
739 basecall.data = NULL;
740
741 entity_call( world, &basecall );
742 }
743 }
744 else
745 vg_line_boxf_transformed( volume->to_world, cube, 0xff0000ff );
746 }
747 else if( volume->type == k_volume_subtype_particle ){
748 vg_line_boxf_transformed( volume->to_world, cube, 0xff00c0ff );
749
750 for( int j=0; j<random_ticks; j++ ){
751 ent_call basecall;
752 basecall.ent.index = idx;
753 basecall.ent.type = k_ent_volume;
754 basecall.function = k_ent_function_trigger;
755 basecall.data = NULL;
756
757 entity_call( world, &basecall );
758 }
759 }
760 }
761 world_global.in_volume = in_volume;
762
763 #if 0
764 if( k_debug_light_indices )
765 {
766 for( int i=0; i<world->light_count; i++ ){
767 struct world_light *light = &world->lights[i];
768 struct classtype_world_light *inf = light->inf;
769
770 u32 colour = 0xff000000;
771 u8 r = inf->colour[0] * 255.0f,
772 g = inf->colour[1] * 255.0f,
773 b = inf->colour[2] * 255.0f;
774
775 colour |= r;
776 colour |= g << 8;
777 colour |= b << 16;
778
779 vg_line_pt3( light->node->co, 0.25f, colour );
780 }
781 }
782
783 #endif
784
785 #if 0
786
787 /* process soundscape transactions */
788 audio_lock();
789 for( int i=0; i<world->soundscape_count; i++ )
790 {
791 struct soundscape *s = &world->soundscapes[i];
792 s->usage_count = 0;
793
794 for( int j=0; j<s->max_instances; j++ )
795 {
796 if( s->channels[j] )
797 {
798 if( audio_channel_finished(s->channels[j]) )
799 s->channels[j] = audio_relinquish_channel( s->channels[j] );
800 else
801 s->usage_count ++;
802 }
803 }
804 }
805 audio_unlock();
806 #endif
807 }
808
809 /*
810 * -----------------------------------------------------------------------------
811 * API implementation
812 * -----------------------------------------------------------------------------
813 */
814
815 VG_STATIC void ray_world_get_tri( world_instance *world,
816 ray_hit *hit, v3f tri[3] )
817 {
818 for( int i=0; i<3; i++ )
819 v3_copy( world->scene_geo->arrvertices[ hit->tri[i] ].co, tri[i] );
820 }
821
822 VG_STATIC int ray_world( world_instance *world,
823 v3f pos, v3f dir, ray_hit *hit )
824 {
825 return scene_raycast( world->scene_geo, world->geo_bh, pos, dir, hit );
826 }
827
828 /*
829 * Cast a sphere from a to b and see what time it hits
830 */
831 VG_STATIC int spherecast_world( world_instance *world,
832 v3f pa, v3f pb, float r, float *t, v3f n )
833 {
834 bh_iter it;
835 bh_iter_init( 0, &it );
836
837 boxf region;
838 box_init_inf( region );
839 box_addpt( region, pa );
840 box_addpt( region, pb );
841
842 v3_add( (v3f){ r, r, r}, region[1], region[1] );
843 v3_add( (v3f){-r,-r,-r}, region[0], region[0] );
844
845 v3f dir;
846 v3_sub( pb, pa, dir );
847
848 v3f dir_inv;
849 dir_inv[0] = 1.0f/dir[0];
850 dir_inv[1] = 1.0f/dir[1];
851 dir_inv[2] = 1.0f/dir[2];
852
853 int hit = -1;
854 float min_t = 1.0f;
855
856 int idx;
857 while( bh_next( world->geo_bh, &it, region, &idx ) ){
858 u32 *ptri = &world->scene_geo->arrindices[ idx*3 ];
859 v3f tri[3];
860
861 boxf box;
862 box_init_inf( box );
863
864 for( int j=0; j<3; j++ ){
865 v3_copy( world->scene_geo->arrvertices[ptri[j]].co, tri[j] );
866 box_addpt( box, tri[j] );
867 }
868
869 v3_add( (v3f){ r, r, r}, box[1], box[1] );
870 v3_add( (v3f){-r,-r,-r}, box[0], box[0] );
871
872 if( !ray_aabb1( box, pa, dir_inv, 1.0f ) )
873 continue;
874
875 float t;
876 v3f n1;
877 if( spherecast_triangle( tri, pa, dir, r, &t, n1 ) ){
878 if( t < min_t ){
879 min_t = t;
880 hit = idx;
881 v3_copy( n1, n );
882 }
883 }
884 }
885
886 *t = min_t;
887 return hit;
888 }
889
890 VG_STATIC
891 struct world_surface *world_tri_index_surface( world_instance *world,
892 u32 index )
893 {
894 for( int i=1; i<world->surface_count; i++ ){
895 struct world_surface *surf = &world->surfaces[i];
896
897 if( (index >= surf->sm_geo.vertex_start) &&
898 (index < surf->sm_geo.vertex_start+surf->sm_geo.vertex_count ) )
899 {
900 return surf;
901 }
902 }
903
904 return &world->surfaces[0];
905 }
906
907 VG_STATIC struct world_surface *world_contact_surface( world_instance *world,
908 rb_ct *ct )
909 {
910 return world_tri_index_surface( world, ct->element_id );
911 }
912
913 VG_STATIC struct world_surface *ray_hit_surface( world_instance *world,
914 ray_hit *hit )
915 {
916 return world_tri_index_surface( world, hit->tri[0] );
917 }
918
919 #endif /* WORLD_H */