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