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