world regions
[carveJwlIkooP6JGAAIwe30JlM.git] / world_entity.c
1 #ifndef WORLD_ENTITY_C
2 #define WORLD_ENTITY_C
3
4 #include "model.h"
5 #include "entity.h"
6 #include "world.h"
7 #include "world_load.h"
8 #include "save.h"
9 #include "vg/vg_msg.h"
10 #include "menu.h"
11 #include "ent_challenge.h"
12 #include "ent_skateshop.h"
13 #include "ent_route.h"
14
15 static void world_entity_focus( u32 entity_id ){
16 localplayer.immobile = 1;
17 menu.disable_open = 1;
18
19 v3_zero( localplayer.rb.v );
20 v3_zero( localplayer.rb.w );
21 player_walk.move_speed = 0.0f;
22 world_static.focused_entity = entity_id;
23 skaterift.activity = k_skaterift_ent_focus;
24 }
25
26 static void world_entity_unfocus(void){
27 localplayer.immobile = 0;
28 skaterift.activity = k_skaterift_default;
29 menu.disable_open = 0;
30 srinput.state = k_input_state_resume;
31 }
32
33 static void world_entity_focus_camera( world_instance *world, u32 uid ){
34 if( mdl_entity_id_type( uid ) == k_ent_camera ){
35 u32 index = mdl_entity_id_id( uid );
36 ent_camera *cam = mdl_arritm( &world->ent_camera, index );
37
38 v3f dir = {0.0f,-1.0f,0.0f};
39 mdl_transform_vector( &cam->transform, dir, dir );
40 v3_angles( dir, world_static.focus_cam.angles );
41 v3_copy( cam->transform.co, world_static.focus_cam.pos );
42 world_static.focus_cam.fov = cam->fov;
43 }
44 else {
45 camera_copy( &localplayer.cam, &world_static.focus_cam );
46
47 /* TODO ? */
48 world_static.focus_cam.nearz = localplayer.cam.nearz;
49 world_static.focus_cam.farz = localplayer.cam.farz;
50 }
51 }
52
53 /* logic preupdate */
54 static void world_entity_focus_preupdate(void){
55 f32 rate = vg_minf( 1.0f, vg.time_frame_delta * 2.0f );
56 int active = 0;
57 if( skaterift.activity == k_skaterift_ent_focus )
58 active = 1;
59
60 vg_slewf( &world_static.focus_strength, active,
61 vg.time_frame_delta * (1.0f/0.5f) );
62
63 u32 type = mdl_entity_id_type( world_static.focused_entity ),
64 index = mdl_entity_id_id( world_static.focused_entity );
65 world_instance *world = world_current_instance();
66
67 /* TODO: Table. */
68 if( type == k_ent_skateshop ){
69 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
70 ent_skateshop_preupdate( skateshop, active );
71 }
72 else if( type == k_ent_challenge ){
73 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
74 ent_challenge_preupdate( challenge, active );
75 }
76 else if( type == k_ent_route ){
77 ent_route *route = mdl_arritm( &world->ent_route, index );
78 ent_route_preupdate( route, active );
79 }
80 }
81
82 /* additional renderings like text etc.. */
83 static void world_entity_focus_render(void){
84 if( skaterift.activity != k_skaterift_ent_focus )
85 return;
86
87 u32 type = mdl_entity_id_type( world_static.focused_entity ),
88 index = mdl_entity_id_id( world_static.focused_entity );
89 world_instance *world = world_current_instance();
90
91 if( type == k_ent_skateshop ){
92 ent_skateshop *skateshop = mdl_arritm( &world->ent_skateshop, index );
93 skateshop_render( skateshop );
94 }
95 else if( type == k_ent_challenge ){}
96 else if( type == k_ent_route ){}
97 else if( type == k_ent_miniworld ){}
98 else {
99 vg_fatal_error( "Programming error\n" );
100 }
101 }
102
103 static void world_gen_entities_init( world_instance *world ){
104 /* lights */
105 for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
106 ent_light *light = mdl_arritm( &world->ent_light, j );
107
108 m4x3f to_world;
109 q_m3x3( light->transform.q, to_world );
110 v3_copy( light->transform.co, to_world[3] );
111 m4x3_invert_affine( to_world, light->inverse_world );
112
113 light->angle_sin_cos[0] = sinf( light->angle * 0.5f );
114 light->angle_sin_cos[1] = cosf( light->angle * 0.5f );
115 }
116
117 /* gates */
118 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
119 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
120
121 if( !(gate->flags & k_ent_gate_nonlocal) ) {
122 gate_transform_update( gate );
123 }
124 }
125
126 vg_async_call( world_link_nonlocal_async, world, 0 );
127
128 /* water */
129 for( u32 j=0; j<mdl_arrcount(&world->ent_water); j++ ){
130 ent_water *water = mdl_arritm( &world->ent_water, j );
131 if( world->water.enabled ){
132 vg_warn( "Multiple water surfaces in level!\n" );
133 break;
134 }
135
136 world->water.enabled = 1;
137 water_set_surface( world, water->transform.co[1] );
138 }
139
140 /* volumes */
141 for( u32 j=0; j<mdl_arrcount(&world->ent_volume); j++ ){
142 ent_volume *volume = mdl_arritm( &world->ent_volume, j );
143 mdl_transform_m4x3( &volume->transform, volume->to_world );
144 m4x3_invert_full( volume->to_world, volume->to_local );
145 }
146
147 /* audio packs */
148 for( u32 j=0; j<mdl_arrcount(&world->ent_audio); j++ ){
149 ent_audio *audio = mdl_arritm( &world->ent_audio, j );
150
151 for( u32 k=0; k<audio->clip_count; k++ ){
152 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
153 audio->clip_start+k );
154
155 if( clip->_.file.pack_size ){
156 u32 size = clip->_.file.pack_size,
157 offset = clip->_.file.pack_offset;
158
159 /* embedded files are fine to clear the scratch buffer, only
160 * external audio uses it */
161
162 vg_linear_clear( vg_mem.scratch );
163 void *data = vg_linear_alloc( vg_mem.scratch,
164 clip->_.file.pack_size );
165
166 mdl_fread_pack_file( &world->meta, &clip->_.file, data );
167
168 clip->_.clip.path = NULL;
169 clip->_.clip.flags = audio->flags;
170 clip->_.clip.data = data;
171 clip->_.clip.size = size;
172 }
173 else{
174 clip->_.clip.path = mdl_pstr(&world->meta,clip->_.file.pstr_path);
175 clip->_.clip.flags = audio->flags;
176 clip->_.clip.data = NULL;
177 clip->_.clip.size = 0;
178 }
179
180 audio_clip_load( &clip->_.clip, world->heap );
181 }
182 }
183
184 /* create generic entity hierachy for those who need it */
185 u32 indexed_count = 0;
186 struct {
187 u32 type;
188 mdl_array_ptr *array;
189 }
190 indexables[] = {
191 { k_ent_gate, &world->ent_gate },
192 { k_ent_objective, &world->ent_objective },
193 { k_ent_volume, &world->ent_volume },
194 { k_ent_challenge, &world->ent_challenge }
195 };
196
197 for( u32 i=0; i<vg_list_size(indexables); i++ )
198 indexed_count += mdl_arrcount( indexables[i].array );
199 vg_info( "indexing %u entities\n", indexed_count );
200
201 world->entity_list = vg_linear_alloc( world->heap,
202 vg_align8(indexed_count*sizeof(u32)));
203
204 u32 index=0;
205 for( u32 i=0; i<vg_list_size(indexables); i++ ){
206 u32 type = indexables[i].type,
207 count = mdl_arrcount( indexables[i].array );
208
209 for( u32 j=0; j<count; j ++ )
210 world->entity_list[index ++] = mdl_entity_id( type, j );
211 }
212
213 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
214 indexed_count, 2 );
215
216 world->tar_min = world->entity_bh->nodes[0].bbx[0][1];
217 world->tar_max = world->entity_bh->nodes[0].bbx[1][1] + 20.0f;
218
219 for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i++ ){
220 ent_marker *marker = mdl_arritm( &world->ent_marker, i );
221
222 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_min" ) )
223 world->tar_min = marker->transform.co[1];
224
225 if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tar_max" ) )
226 world->tar_max = marker->transform.co[1];
227 }
228 }
229
230 static
231 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
232 {
233 ent_spawn *rp = NULL, *r;
234 float min_dist = INFINITY;
235
236 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
237 r = mdl_arritm( &world->ent_spawn, i );
238 float d = v3_dist2( r->transform.co, position );
239
240 if( d < min_dist ){
241 min_dist = d;
242 rp = r;
243 }
244 }
245
246 if( !rp ){
247 if( mdl_arrcount(&world->ent_spawn) ){
248 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
249 return mdl_arritm( &world->ent_spawn, 0 );
250 }
251 else{
252 vg_error( "There are no spawns in the level!\n" );
253 }
254 }
255
256 return rp;
257 }
258
259 static
260 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
261 {
262 ent_spawn *rp = NULL, *r;
263 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
264 r = mdl_arritm( &world->ent_spawn, i );
265 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
266 rp = r;
267 break;
268 }
269 }
270
271 if( !rp )
272 vg_warn( "No spawn named '%s'\n", name );
273
274 return rp;
275 }
276
277 static void ent_volume_call( world_instance *world, ent_call *call )
278 {
279 u32 index = mdl_entity_id_id( call->id );
280 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
281 if( !volume->target ) return;
282
283 if( call->function == k_ent_function_trigger ){
284 call->id = volume->target;
285
286 if( volume->flags & k_ent_volume_flag_particles ){
287 float *co = alloca( sizeof(float)*3 );
288 co[0] = vg_randf64()*2.0f-1.0f;
289 co[1] = vg_randf64()*2.0f-1.0f;
290 co[2] = vg_randf64()*2.0f-1.0f;
291 m4x3_mulv( volume->to_world, co, co );
292
293 call->function = k_ent_function_particle_spawn;
294 call->data = co;
295 entity_call( world, call );
296 }
297 else{
298 call->function = volume->trigger.event;
299 entity_call( world, call );
300 }
301 }
302 else if( call->function == k_ent_function_trigger_leave ){
303 call->id = volume->target;
304
305 if( volume->flags & k_ent_volume_flag_particles ){
306 assert(0);
307 }
308 else{
309 call->function = volume->trigger.event_leave;
310 entity_call( world, call );
311 }
312 }
313 }
314
315 static void ent_audio_call( world_instance *world, ent_call *call ){
316 if( world->status == k_world_status_unloading ){
317 vg_warn( "cannot modify audio while unloading world\n" );
318 return;
319 }
320
321 u8 world_id = (world - world_static.instances) + 1;
322 u32 index = mdl_entity_id_id( call->id );
323 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
324
325 v3f sound_co;
326
327 if( call->function == k_ent_function_particle_spawn ){
328 v3_copy( call->data, sound_co );
329 }
330 else if( call->function == k_ent_function_trigger ){
331 v3_copy( audio->transform.co, sound_co );
332 }
333 else
334 return;
335
336 float chance = vg_randf64()*100.0f,
337 bar = 0.0f;
338
339 for( u32 i=0; i<audio->clip_count; i++ ){
340 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
341 audio->clip_start+i );
342
343 float mod = world->probabilities[ audio->probability_curve ],
344 p = clip->probability * mod;
345
346 bar += p;
347 if( chance < bar ){
348 audio_lock();
349
350 if( audio->behaviour == k_channel_behaviour_unlimited ){
351 audio_oneshot_3d( &clip->_.clip, sound_co,
352 audio->transform.s[0],
353 audio->volume );
354 }
355 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
356 audio_channel *ch =
357 audio_get_group_idle_channel( audio->group,
358 audio->max_channels );
359
360 if( ch ){
361 audio_channel_init( ch, &clip->_.clip, audio->flags );
362 audio_channel_group( ch, audio->group );
363 audio_channel_world( ch, world_id );
364 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
365 audio_channel_edit_volume( ch, audio->volume, 1 );
366 ch = audio_relinquish_channel( ch );
367 }
368 }
369 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
370 audio_channel *ch =
371 audio_get_group_idle_channel( audio->group,
372 audio->max_channels );
373
374 /* group is full */
375 if( !ch ){
376 audio_channel *existing =
377 audio_get_group_first_active_channel( audio->group );
378
379 if( existing ){
380 if( existing->source == &clip->_.clip ){
381 audio_unlock();
382 return;
383 }
384
385 existing->group = 0;
386 existing = audio_channel_fadeout(existing, audio->crossfade);
387 }
388
389 ch = audio_get_first_idle_channel();
390 }
391
392 if( ch ){
393 audio_channel_init( ch, &clip->_.clip, audio->flags );
394 audio_channel_group( ch, audio->group );
395 audio_channel_world( ch, world_id );
396 audio_channel_fadein( ch, audio->crossfade );
397 ch = audio_relinquish_channel( ch );
398 }
399 }
400
401 audio_unlock();
402 return;
403 }
404 }
405 }
406
407
408 static void ent_ccmd_call( world_instance *world, ent_call *call ){
409 if( call->function == k_ent_function_trigger ){
410 u32 index = mdl_entity_id_id( call->id );
411 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
412 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command) );
413 }
414 }
415
416 /*
417 * BVH implementation
418 * ----------------------------------------------------------------------------
419 */
420
421 static void
422 entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
423 world_instance *world = user;
424
425 u32 id = world->entity_list[ item_index ],
426 type = mdl_entity_id_type( id ),
427 index = mdl_entity_id_id( id );
428
429 if( type == k_ent_gate ){
430 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
431 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
432 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
433
434 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
435 }
436 else if( type == k_ent_objective ){
437 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
438
439 /* TODO: This might be more work than necessary. could maybe just get
440 * away with representing them as points */
441
442 boxf box;
443 box_init_inf( box );
444
445 for( u32 i=0; i<objective->submesh_count; i++ ){
446 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
447 objective->submesh_start+i );
448 box_concat( box, sm->bbx );
449 }
450
451 m4x3f transform;
452 mdl_transform_m4x3( &objective->transform, transform );
453 m4x3_expand_aabb_aabb( transform, bound, box );
454 }
455 else if( type == k_ent_volume ){
456 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
457 m4x3_expand_aabb_aabb( volume->to_world, bound,
458 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
459 }
460 else if( type == k_ent_challenge ){
461 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
462
463 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
464 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
465 m4x3f transform;
466 mdl_transform_m4x3( &challenge->transform, transform );
467 m4x3_expand_aabb_aabb( transform, bound, box );
468 }
469 else{
470 vg_fatal_error( "Programming error\n" );
471 }
472 }
473
474 static float entity_bh_centroid( void *user, u32 item_index, int axis ){
475 world_instance *world = user;
476
477 u32 id = world->entity_list[ item_index ],
478 type = mdl_entity_id_type( id ),
479 index = mdl_entity_id_id( id );
480
481 if( type == k_ent_gate ){
482 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
483 return gate->to_world[3][axis];
484 }
485 else if( type == k_ent_objective ){
486 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
487 return objective->transform.co[axis];
488 }
489 else if( type == k_ent_volume ){
490 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
491 return volume->transform.co[axis];
492 }
493 else if( type == k_ent_challenge ){
494 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
495 return challenge->transform.co[axis];
496 }
497 else {
498 vg_fatal_error( "Programming error\n" );
499 return INFINITY;
500 }
501 }
502
503 static void entity_bh_swap( void *user, u32 ia, u32 ib ){
504 world_instance *world = user;
505
506 u32 a = world->entity_list[ ia ],
507 b = world->entity_list[ ib ];
508
509 world->entity_list[ ia ] = b;
510 world->entity_list[ ib ] = a;
511 }
512
513 static void entity_bh_debug( void *user, u32 item_index ){
514 world_instance *world = user;
515
516 u32 id = world->entity_list[ item_index ],
517 type = mdl_entity_id_type( id ),
518 index = mdl_entity_id_id( id );
519
520 if( type == k_ent_gate ){
521 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
522 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
523 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
524 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
525 }
526 else if( type == k_ent_objective ){
527 ent_objective *objective = mdl_arritm( &world->ent_objective, index );
528 boxf box;
529 box_init_inf( box );
530
531 for( u32 i=0; i<objective->submesh_count; i++ ){
532 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
533 objective->submesh_start+i );
534 box_concat( box, sm->bbx );
535 }
536
537 m4x3f transform;
538 mdl_transform_m4x3( &objective->transform, transform );
539 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
540 }
541 else if( type == k_ent_volume ){
542 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
543 vg_line_boxf_transformed( volume->to_world,
544 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
545 0xf000ff00 );
546 }
547 else if( type == k_ent_challenge ){
548 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
549
550 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
551 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
552 m4x3f transform;
553 mdl_transform_m4x3( &challenge->transform, transform );
554 vg_line_boxf_transformed( transform, box, 0xf0ff0000 );
555 }
556 else{
557 vg_fatal_error( "Programming error\n" );
558 }
559 }
560
561 static void entity_bh_closest( void *user, u32 item_index, v3f point,
562 v3f closest ){
563 world_instance *world = user;
564
565 u32 id = world->entity_list[ item_index ],
566 type = mdl_entity_id_type( id ),
567 index = mdl_entity_id_id( id );
568
569 if( type == k_ent_gate ){
570 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
571 v3_copy( gate->to_world[3], closest );
572 }
573 else if( type == k_ent_objective ){
574 ent_objective *challenge = mdl_arritm( &world->ent_objective, index );
575 v3_copy( challenge->transform.co, closest );
576 }
577 else if( type == k_ent_volume ){
578 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
579 v3_copy( volume->to_world[3], closest );
580 }
581 else if( type == k_ent_challenge ){
582 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
583 v3_copy( challenge->transform.co, closest );
584 }
585 else{
586 vg_fatal_error( "Programming error\n" );
587 }
588 }
589
590 static void world_entity_start( world_instance *world, vg_msg *sav ){
591 vg_info( "Start instance %p\n", world );
592
593 world->probabilities[ k_probability_curve_constant ] = 1.0f;
594 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
595 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
596 if( audio->flags & AUDIO_FLAG_AUTO_START ){
597 ent_call call;
598 call.data = NULL;
599 call.function = k_ent_function_trigger;
600 call.id = mdl_entity_id( k_ent_audio, i );
601 entity_call( world, &call );
602 }
603 }
604
605 /* read savedata
606 * ----------------------------------------------------------------------- */
607
608 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
609 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, i );
610 const char *alias = mdl_pstr( &world->meta, challenge->pstr_alias );
611
612 if( vg_msg_getkvu32( sav, alias, 0 ) ){
613 ent_call call;
614 call.data = NULL;
615 call.function = 0;
616 call.id = mdl_entity_id( k_ent_challenge, i );
617 entity_call( world, &call );
618 }
619 }
620
621 vg_msg routes_block = *sav;
622 if( vg_msg_seekframe( &routes_block, "routes" ) ){
623 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
624 ent_route *route = mdl_arritm( &world->ent_route, i );
625
626 vg_msg route_info = routes_block;
627 if( vg_msg_seekframe( &route_info,
628 mdl_pstr(&world->meta,route->pstr_name) ) ){
629 route->flags |= vg_msg_getkvu32( &route_info, "flags", 0 );
630 route->best_laptime =
631 vg_msg_getkvf64( &route_info, "best_laptime", 0.0 );
632 }
633 }
634 }
635
636 ent_region_re_eval( world );
637 }
638
639 static void world_entity_serialize( world_instance *world, vg_msg *sav ){
640 for( u32 i=0; i<mdl_arrcount(&world->ent_challenge); i++ ){
641 ent_challenge *challenge = mdl_arritm(&world->ent_challenge,i);
642
643 const char *alias = mdl_pstr(&world->meta,challenge->pstr_alias);
644 vg_msg_wkvu32( sav, alias, challenge->status );
645 }
646
647 if( mdl_arrcount(&world->ent_route) ){
648 vg_msg_frame( sav, "routes" );
649 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
650 ent_route *route = mdl_arritm( &world->ent_route, i );
651
652 vg_msg_frame( sav, mdl_pstr( &world->meta, route->pstr_name ) );
653 {
654 vg_msg_wkvu32( sav, "flags", route->flags );
655 vg_msg_wkvf64( sav, "best_laptime", route->best_laptime );
656 }
657 vg_msg_end_frame( sav );
658 }
659 vg_msg_end_frame( sav );
660 }
661 }
662
663 #endif /* WORLD_ENTITY_C */