unlock rendering
[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 }
344
345 VG_STATIC float entity_bh_centroid( void *user, u32 item_index, int axis ){
346 world_instance *world = user;
347
348 u32 id = world->entity_list[ item_index ],
349 type = mdl_entity_id_type( id ),
350 index = mdl_entity_id_id( id );
351
352 if( type == k_ent_gate ){
353 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
354 return gate->to_world[3][axis];
355 }
356 else if( type == k_ent_challenge ){
357 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
358 return challenge->transform.co[axis];
359 }
360 else if( type == k_ent_volume ){
361 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
362 return volume->transform.co[axis];
363 }
364 else if( type == k_ent_unlock ){
365 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, index );
366 return unlock->transform.co[axis];
367 }
368 else {
369 vg_fatal_error( "Programming error\n" );
370 return INFINITY;
371 }
372 }
373
374 VG_STATIC void entity_bh_swap( void *user, u32 ia, u32 ib ){
375 world_instance *world = user;
376
377 u32 a = world->entity_list[ ia ],
378 b = world->entity_list[ ib ];
379
380 world->entity_list[ ia ] = b;
381 world->entity_list[ ib ] = a;
382 }
383
384 VG_STATIC void entity_bh_debug( void *user, u32 item_index ){
385 world_instance *world = user;
386
387 u32 id = world->entity_list[ item_index ],
388 type = mdl_entity_id_type( id ),
389 index = mdl_entity_id_id( id );
390
391 if( type == k_ent_gate ){
392 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
393 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
394 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
395 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
396 }
397 else if( type == k_ent_challenge ){
398 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
399 boxf box;
400 box_init_inf( box );
401
402 for( u32 i=0; i<challenge->submesh_count; i++ ){
403 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
404 challenge->submesh_start+i );
405 box_concat( box, sm->bbx );
406 }
407
408 m4x3f transform;
409 mdl_transform_m4x3( &challenge->transform, transform );
410 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
411 }
412 else if( type == k_ent_volume ){
413 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
414 vg_line_boxf_transformed( volume->to_world,
415 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
416 0xf000ff00 );
417 }
418 else{
419 vg_fatal_error( "Programming error\n" );
420 }
421 }
422
423 VG_STATIC void entity_bh_closest( void *user, u32 item_index, v3f point,
424 v3f closest ){
425 world_instance *world = user;
426
427 u32 id = world->entity_list[ item_index ],
428 type = mdl_entity_id_type( id ),
429 index = mdl_entity_id_id( id );
430
431 if( type == k_ent_gate ){
432 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
433 v3_copy( gate->to_world[3], closest );
434 }
435 else if( type == k_ent_challenge ){
436 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
437 v3_copy( challenge->transform.co, closest );
438 }
439 else if( type == k_ent_volume ){
440 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
441 v3_copy( volume->to_world[3], closest );
442 }
443 else{
444 vg_fatal_error( "Programming error\n" );
445 }
446 }
447
448 VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav ){
449 vg_info( "Start instance %p\n", world );
450
451 world->probabilities[ k_probability_curve_constant ] = 1.0f;
452 for( u32 i=0; i<mdl_arrcount(&world->ent_audio); i++ ){
453 ent_audio *audio = mdl_arritm(&world->ent_audio,i);
454 if( audio->flags & AUDIO_FLAG_AUTO_START ){
455 ent_call call;
456 call.data = NULL;
457 call.function = k_ent_function_trigger;
458 call.id = mdl_entity_id( k_ent_audio, i );
459 entity_call( world, &call );
460 }
461 }
462
463 /* read savedata
464 * ----------------------------------------------------------------------- */
465
466 for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
467 ent_unlock *unlock = mdl_arritm( &world->ent_unlock, i );
468 const char *alias = mdl_pstr( &world->meta, unlock->pstr_alias );
469
470 if( vg_msg_seekkvu32( sav, alias, k_vg_msg_first ) ){
471 ent_call call;
472 call.data = NULL;
473 call.function = 0;
474 call.id = mdl_entity_id( k_ent_unlock, i );
475 entity_call( world, &call );
476 }
477 }
478 }
479
480 VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav ){
481 for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
482 ent_unlock *unlock = mdl_arritm(&world->ent_unlock,i);
483
484 const char *alias = mdl_pstr(&world->meta,unlock->pstr_alias);
485 vg_msg_wkvu32( sav, alias, unlock->status );
486 }
487 }
488
489 #endif /* WORLD_ENTITY_C */