cool font shader and entity bvh fix
[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
11 VG_STATIC void world_gen_entities_init( world_instance *world ){
12 /* lights */
13 for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
14 ent_light *light = mdl_arritm( &world->ent_light, j );
15
16 m4x3f to_world;
17 q_m3x3( light->transform.q, to_world );
18 v3_copy( light->transform.co, to_world[3] );
19 m4x3_invert_affine( to_world, light->inverse_world );
20
21 light->angle_sin_cos[0] = sinf( light->angle * 0.5f );
22 light->angle_sin_cos[1] = cosf( light->angle * 0.5f );
23 }
24
25 /* gates */
26 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
27 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
28
29 if( !(gate->flags & k_ent_gate_nonlocal) ) {
30 gate_transform_update( gate );
31 }
32 }
33 vg_async_call( world_link_nonlocal_async, world, 0 );
34
35 /* water */
36 for( u32 j=0; j<mdl_arrcount(&world->ent_water); j++ ){
37 ent_water *water = mdl_arritm( &world->ent_water, j );
38 if( world->water.enabled ){
39 vg_warn( "Multiple water surfaces in level!\n" );
40 break;
41 }
42
43 world->water.enabled = 1;
44 water_set_surface( world, water->transform.co[1] );
45 }
46
47 /* volumes */
48 for( u32 j=0; j<mdl_arrcount(&world->ent_volume); j++ ){
49 ent_volume *volume = mdl_arritm( &world->ent_volume, j );
50 mdl_transform_m4x3( &volume->transform, volume->to_world );
51 m4x3_invert_full( volume->to_world, volume->to_local );
52 }
53
54 /* audio packs */
55 for( u32 j=0; j<mdl_arrcount(&world->ent_audio); j++ ){
56 ent_audio *audio = mdl_arritm( &world->ent_audio, j );
57
58 for( u32 k=0; k<audio->clip_count; k++ ){
59 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
60 audio->clip_start+k );
61
62 if( clip->_.file.pack_size ){
63 u32 size = clip->_.file.pack_size,
64 offset = clip->_.file.pack_offset;
65
66 /* embedded files are fine to clear the scratch buffer, only
67 * external audio uses it */
68
69 vg_linear_clear( vg_mem.scratch );
70 void *data = vg_linear_alloc( vg_mem.scratch,
71 clip->_.file.pack_size );
72
73 mdl_fread_pack_file( &world->meta, &clip->_.file, data );
74
75 clip->_.clip.path = NULL;
76 clip->_.clip.flags = audio->flags;
77 clip->_.clip.data = data;
78 clip->_.clip.size = size;
79 }
80 else{
81 clip->_.clip.path = mdl_pstr(&world->meta,clip->_.file.pstr_path);
82 clip->_.clip.flags = audio->flags;
83 clip->_.clip.data = NULL;
84 clip->_.clip.size = 0;
85 }
86
87 audio_clip_load( &clip->_.clip, world->heap );
88 }
89 }
90
91 /* create generic entity hierachy for those who need it */
92 u32 indexed_count = 0;
93 struct {
94 u32 type;
95 mdl_array_ptr *array;
96 }
97 indexables[] = {
98 { k_ent_gate, &world->ent_gate },
99 { k_ent_challenge, &world->ent_challenge },
100 { k_ent_volume, &world->ent_volume },
101 { k_ent_unlock, &world->ent_unlock }
102 };
103
104 for( u32 i=0; i<vg_list_size(indexables); i++ )
105 indexed_count += mdl_arrcount( indexables[i].array );
106 vg_info( "indexing %u entities\n", indexed_count );
107
108 world->entity_list = vg_linear_alloc( world->heap,
109 vg_align8(indexed_count*sizeof(u32)));
110
111 u32 index=0;
112 for( u32 i=0; i<vg_list_size(indexables); i++ ){
113 u32 type = indexables[i].type,
114 count = mdl_arrcount( indexables[i].array );
115
116 for( u32 j=0; j<count; j ++ )
117 world->entity_list[index ++] = mdl_entity_id( type, j );
118 }
119
120 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
121 indexed_count, 2 );
122 }
123
124 VG_STATIC
125 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
126 {
127 ent_spawn *rp = NULL, *r;
128 float min_dist = INFINITY;
129
130 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
131 r = mdl_arritm( &world->ent_spawn, i );
132 float d = v3_dist2( r->transform.co, position );
133
134 if( d < min_dist ){
135 min_dist = d;
136 rp = r;
137 }
138 }
139
140 if( !rp ){
141 if( mdl_arrcount(&world->ent_spawn) ){
142 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
143 return mdl_arritm( &world->ent_spawn, 0 );
144 }
145 else{
146 vg_error( "There are no spawns in the level!\n" );
147 }
148 }
149
150 return rp;
151 }
152
153 VG_STATIC
154 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
155 {
156 ent_spawn *rp = NULL, *r;
157 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
158 r = mdl_arritm( &world->ent_spawn, i );
159 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
160 rp = r;
161 break;
162 }
163 }
164
165 if( !rp )
166 vg_warn( "No spawn named '%s'\n", name );
167
168 return rp;
169 }
170
171 VG_STATIC void ent_volume_call( world_instance *world, ent_call *call )
172 {
173 u32 index = mdl_entity_id_id( call->id );
174 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
175 if( !volume->target ) return;
176
177 if( call->function == k_ent_function_trigger ){
178 call->id = volume->target;
179
180 if( volume->flags & k_ent_volume_flag_particles ){
181 float *co = alloca( sizeof(float)*3 );
182 co[0] = vg_randf64()*2.0f-1.0f;
183 co[1] = vg_randf64()*2.0f-1.0f;
184 co[2] = vg_randf64()*2.0f-1.0f;
185 m4x3_mulv( volume->to_world, co, co );
186
187 call->function = k_ent_function_particle_spawn;
188 call->data = co;
189 entity_call( world, call );
190 }
191 else{
192 call->function = volume->trigger.event;
193 entity_call( world, call );
194 }
195 }
196 }
197
198 VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ){
199 if( world->status == k_world_status_unloading ){
200 vg_warn( "cannot modify audio while unloading world\n" );
201 return;
202 }
203
204 u8 world_id = (world - world_static.instances) + 1;
205 u32 index = mdl_entity_id_id( call->id );
206 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
207
208 v3f sound_co;
209
210 if( call->function == k_ent_function_particle_spawn ){
211 v3_copy( call->data, sound_co );
212 }
213 else if( call->function == k_ent_function_trigger ){
214 v3_copy( audio->transform.co, sound_co );
215 }
216 else
217 vg_fatal_error( "ent_audio_call (invalid function id)" );
218
219 float chance = vg_randf64()*100.0f,
220 bar = 0.0f;
221
222 for( u32 i=0; i<audio->clip_count; i++ ){
223 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
224 audio->clip_start+i );
225
226 float mod = world->probabilities[ audio->probability_curve ],
227 p = clip->probability * mod;
228
229 bar += p;
230 if( chance < bar ){
231 audio_lock();
232
233 if( audio->behaviour == k_channel_behaviour_unlimited ){
234 audio_oneshot_3d( &clip->_.clip, sound_co,
235 audio->transform.s[0],
236 audio->volume );
237 }
238 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
239 audio_channel *ch =
240 audio_get_group_idle_channel( audio->group,
241 audio->max_channels );
242
243 if( ch ){
244 audio_channel_init( ch, &clip->_.clip, audio->flags );
245 audio_channel_group( ch, audio->group );
246 audio_channel_world( ch, world_id );
247 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
248 audio_channel_edit_volume( ch, audio->volume, 1 );
249 ch = audio_relinquish_channel( ch );
250 }
251 }
252 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
253 audio_channel *ch =
254 audio_get_group_idle_channel( audio->group,
255 audio->max_channels );
256
257 /* group is full */
258 if( !ch ){
259 audio_channel *existing =
260 audio_get_group_first_active_channel( audio->group );
261
262 if( existing ){
263 if( existing->source == &clip->_.clip ){
264 audio_unlock();
265 return;
266 }
267
268 existing->group = 0;
269 existing = audio_channel_fadeout(existing, audio->crossfade);
270 }
271
272 ch = audio_get_first_idle_channel();
273 }
274
275 if( ch ){
276 audio_channel_init( ch, &clip->_.clip, audio->flags );
277 audio_channel_group( ch, audio->group );
278 audio_channel_world( ch, world_id );
279 audio_channel_fadein( ch, audio->crossfade );
280 ch = audio_relinquish_channel( ch );
281 }
282 }
283
284 audio_unlock();
285 return;
286 }
287 }
288 }
289
290
291 VG_STATIC void ent_ccmd_call( world_instance *world, ent_call *call ){
292 if( call->function == k_ent_function_trigger ){
293 u32 index = mdl_entity_id_id( call->id );
294 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
295 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command) );
296 }
297 }
298
299 /*
300 * BVH implementation
301 * ----------------------------------------------------------------------------
302 */
303
304 VG_STATIC void
305 entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
306 world_instance *world = user;
307
308 u32 id = world->entity_list[ item_index ],
309 type = mdl_entity_id_type( id ),
310 index = mdl_entity_id_id( id );
311
312 if( type == k_ent_gate ){
313 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
314 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
315 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
316
317 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
318 }
319 else if( type == k_ent_challenge ){
320 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
321
322 /* TODO: This might be more work than necessary. could maybe just get
323 * away with representing them as points */
324
325 boxf box;
326 box_init_inf( box );
327
328 for( u32 i=0; i<challenge->submesh_count; i++ ){
329 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
330 challenge->submesh_start+i );
331 box_concat( box, sm->bbx );
332 }
333
334 m4x3f transform;
335 mdl_transform_m4x3( &challenge->transform, transform );
336 m4x3_expand_aabb_aabb( transform, bound, box );
337 }
338 else if( type == k_ent_volume ){
339 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
340 m4x3_expand_aabb_aabb( volume->to_world, bound,
341 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
342 }
343 else if( type == k_ent_unlock ){
344 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index );
345
346 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
347 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
348 m4x3f transform;
349 mdl_transform_m4x3( &unlock->transform, transform );
350 m4x3_expand_aabb_aabb( transform, bound, box );
351 }
352 else{
353 vg_fatal_error( "Programming error\n" );
354 }
355 }
356
357 VG_STATIC float entity_bh_centroid( void *user, u32 item_index, int axis ){
358 world_instance *world = user;
359
360 u32 id = world->entity_list[ item_index ],
361 type = mdl_entity_id_type( id ),
362 index = mdl_entity_id_id( id );
363
364 if( type == k_ent_gate ){
365 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
366 return gate->to_world[3][axis];
367 }
368 else if( type == k_ent_challenge ){
369 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
370 return challenge->transform.co[axis];
371 }
372 else if( type == k_ent_volume ){
373 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
374 return volume->transform.co[axis];
375 }
376 else if( type == k_ent_unlock ){
377 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index );
378 return unlock->transform.co[axis];
379 }
380 else {
381 vg_fatal_error( "Programming error\n" );
382 return INFINITY;
383 }
384 }
385
386 VG_STATIC void entity_bh_swap( void *user, u32 ia, u32 ib ){
387 world_instance *world = user;
388
389 u32 a = world->entity_list[ ia ],
390 b = world->entity_list[ ib ];
391
392 world->entity_list[ ia ] = b;
393 world->entity_list[ ib ] = a;
394 }
395
396 VG_STATIC void entity_bh_debug( void *user, u32 item_index ){
397 world_instance *world = user;
398
399 u32 id = world->entity_list[ item_index ],
400 type = mdl_entity_id_type( id ),
401 index = mdl_entity_id_id( id );
402
403 if( type == k_ent_gate ){
404 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
405 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
406 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
407 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
408 }
409 else if( type == k_ent_challenge ){
410 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
411 boxf box;
412 box_init_inf( box );
413
414 for( u32 i=0; i<challenge->submesh_count; i++ ){
415 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
416 challenge->submesh_start+i );
417 box_concat( box, sm->bbx );
418 }
419
420 m4x3f transform;
421 mdl_transform_m4x3( &challenge->transform, transform );
422 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
423 }
424 else if( type == k_ent_volume ){
425 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
426 vg_line_boxf_transformed( volume->to_world,
427 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
428 0xf000ff00 );
429 }
430 else if( type == k_ent_unlock ){
431 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index );
432
433 boxf box = {{-1.2f*0.5f,-0.72f*0.5f,-0.01f*0.5f},
434 { 1.2f*0.5f, 0.72f*0.5f, 0.01f*0.5f}};
435 m4x3f transform;
436 mdl_transform_m4x3( &unlock->transform, transform );
437 vg_line_boxf_transformed( transform, box, 0xf0ff0000 );
438 }
439 else{
440 vg_fatal_error( "Programming error\n" );
441 }
442 }
443
444 VG_STATIC void entity_bh_closest( void *user, u32 item_index, v3f point,
445 v3f closest ){
446 world_instance *world = user;
447
448 u32 id = world->entity_list[ item_index ],
449 type = mdl_entity_id_type( id ),
450 index = mdl_entity_id_id( id );
451
452 if( type == k_ent_gate ){
453 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
454 v3_copy( gate->to_world[3], closest );
455 }
456 else if( type == k_ent_challenge ){
457 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
458 v3_copy( challenge->transform.co, closest );
459 }
460 else if( type == k_ent_volume ){
461 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
462 v3_copy( volume->to_world[3], closest );
463 }
464 else if( type == k_ent_unlock ){
465 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index );
466 v3_copy( unlock->transform.co, closest );
467 }
468 else{
469 vg_fatal_error( "Programming error\n" );
470 }
471 }
472
473 VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav ){
474 vg_info( "Start instance %p\n", world );
475
476 world->probabilities[ k_probability_curve_constant ] = 1.0f;
477 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
478 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
479 if( audio->flags & AUDIO_FLAG_AUTO_START ){
480 ent_call call;
481 call.data = NULL;
482 call.function = k_ent_function_trigger;
483 call.id = mdl_entity_id( k_ent_audio, i );
484 entity_call( world, &call );
485 }
486 }
487
488 /* read savedata
489 * ----------------------------------------------------------------------- */
490
491 for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
492 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, i );
493 const char *alias = mdl_pstr( &world->meta, unlock->pstr_alias );
494
495 if( vg_msg_seekkvu32( sav, alias, k_vg_msg_first ) ){
496 ent_call call;
497 call.data = NULL;
498 call.function = 0;
499 call.id = mdl_entity_id( k_ent_unlock, i );
500 entity_call( world, &call );
501 }
502 }
503 }
504
505 VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav ){
506 for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
507 ent_unlock *unlock = mdl_arritm(&world->ent_unlock,i);
508
509 const char *alias = mdl_pstr(&world->meta,unlock->pstr_alias);
510 vg_msg_wkvu32( sav, alias, unlock->status );
511 }
512 }
513
514 #endif /* WORLD_ENTITY_C */