some gate improvements
[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(void){
10 world_instance *world = world_loading_instance();
11
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 };
102
103 for( u32 i=0; i<vg_list_size(indexables); i++ )
104 indexed_count += mdl_arrcount( indexables[i].array );
105 vg_info( "indexing %u entities\n", indexed_count );
106
107 world->entity_list = vg_linear_alloc( world->heap,
108 vg_align8(indexed_count*sizeof(u32)));
109
110 u32 index=0;
111 for( u32 i=0; i<vg_list_size(indexables); i++ ){
112 u32 type = indexables[i].type,
113 count = mdl_arrcount( indexables[i].array );
114
115 for( u32 j=0; j<count; j ++ )
116 world->entity_list[index ++] = mdl_entity_id( type, j );
117 }
118
119 world->entity_bh = bh_create( world->heap, &bh_system_entity_list, world,
120 indexed_count, 2 );
121 }
122
123 VG_STATIC
124 ent_spawn *world_find_closest_spawn( world_instance *world, v3f position )
125 {
126 ent_spawn *rp = NULL, *r;
127 float min_dist = INFINITY;
128
129 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
130 r = mdl_arritm( &world->ent_spawn, i );
131 float d = v3_dist2( r->transform.co, position );
132
133 if( d < min_dist ){
134 min_dist = d;
135 rp = r;
136 }
137 }
138
139 if( !rp ){
140 if( mdl_arrcount(&world->ent_spawn) ){
141 vg_warn( "Invalid distances to spawns.. defaulting to first one.\n" );
142 return mdl_arritm( &world->ent_spawn, 0 );
143 }
144 else{
145 vg_error( "There are no spawns in the level!\n" );
146 }
147 }
148
149 return rp;
150 }
151
152 VG_STATIC
153 ent_spawn *world_find_spawn_by_name( world_instance *world, const char *name )
154 {
155 ent_spawn *rp = NULL, *r;
156 for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
157 r = mdl_arritm( &world->ent_spawn, i );
158 if( !strcmp( mdl_pstr(&world->meta, r->pstr_name), name ) ){
159 rp = r;
160 break;
161 }
162 }
163
164 if( !rp )
165 vg_warn( "No spawn named '%s'\n", name );
166
167 return rp;
168 }
169
170 VG_STATIC void ent_volume_call( world_instance *world, ent_call *call )
171 {
172 u32 index = mdl_entity_id_id( call->id );
173 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
174 if( !volume->target ) return;
175
176 if( call->function == k_ent_function_trigger ){
177 call->id = volume->target;
178
179 if( volume->flags & k_ent_volume_flag_particles ){
180 float *co = alloca( sizeof(float)*3 );
181 co[0] = vg_randf64()*2.0f-1.0f;
182 co[1] = vg_randf64()*2.0f-1.0f;
183 co[2] = vg_randf64()*2.0f-1.0f;
184 m4x3_mulv( volume->to_world, co, co );
185
186 call->function = k_ent_function_particle_spawn;
187 call->data = co;
188 entity_call( world, call );
189 }
190 else{
191 call->function = volume->trigger.event;
192 entity_call( world, call );
193 }
194 }
195 }
196
197 VG_STATIC void ent_audio_call( world_instance *world, ent_call *call ){
198 if( world->status == k_world_status_unloading ){
199 vg_warn( "cannot modify audio while unloading world\n" );
200 return;
201 }
202
203 u8 world_id = (world - world_static.worlds) + 1;
204 u32 index = mdl_entity_id_id( call->id );
205 ent_audio *audio = mdl_arritm( &world->ent_audio, index );
206
207 v3f sound_co;
208
209 if( call->function == k_ent_function_particle_spawn ){
210 v3_copy( call->data, sound_co );
211 }
212 else if( call->function == k_ent_function_trigger ){
213 v3_copy( audio->transform.co, sound_co );
214 }
215 else
216 vg_fatal_error( "ent_audio_call (invalid function id)" );
217
218 float chance = vg_randf64()*100.0f,
219 bar = 0.0f;
220
221 for( u32 i=0; i<audio->clip_count; i++ ){
222 ent_audio_clip *clip = mdl_arritm( &world->ent_audio_clip,
223 audio->clip_start+i );
224
225 float mod = world->probabilities[ audio->probability_curve ],
226 p = clip->probability * mod;
227
228 bar += p;
229 if( chance < bar ){
230 audio_lock();
231
232 if( audio->behaviour == k_channel_behaviour_unlimited ){
233 audio_oneshot_3d( &clip->_.clip, sound_co,
234 audio->transform.s[0],
235 audio->volume );
236 }
237 else if( audio->behaviour == k_channel_behaviour_discard_if_full ){
238 audio_channel *ch =
239 audio_get_group_idle_channel( audio->group,
240 audio->max_channels );
241
242 if( ch ){
243 audio_channel_init( ch, &clip->_.clip, audio->flags );
244 audio_channel_group( ch, audio->group );
245 audio_channel_world( ch, world_id );
246 audio_channel_set_spacial( ch, sound_co, audio->transform.s[0] );
247 audio_channel_edit_volume( ch, audio->volume, 1 );
248 ch = audio_relinquish_channel( ch );
249 }
250 }
251 else if( audio->behaviour == k_channel_behaviour_crossfade_if_full){
252 audio_channel *ch =
253 audio_get_group_idle_channel( audio->group,
254 audio->max_channels );
255
256 /* group is full */
257 if( !ch ){
258 audio_channel *existing =
259 audio_get_group_first_active_channel( audio->group );
260
261 if( existing ){
262 if( existing->source == &clip->_.clip ){
263 audio_unlock();
264 return;
265 }
266
267 existing->group = 0;
268 existing = audio_channel_fadeout(existing, audio->crossfade);
269 }
270
271 ch = audio_get_first_idle_channel();
272 }
273
274 if( ch ){
275 audio_channel_init( ch, &clip->_.clip, audio->flags );
276 audio_channel_group( ch, audio->group );
277 audio_channel_world( ch, world_id );
278 audio_channel_fadein( ch, audio->crossfade );
279 ch = audio_relinquish_channel( ch );
280 }
281 }
282
283 audio_unlock();
284 return;
285 }
286 }
287 }
288
289
290 VG_STATIC void ent_ccmd_call( world_instance *world, ent_call *call ){
291 if( call->function == k_ent_function_trigger ){
292 u32 index = mdl_entity_id_id( call->id );
293 ent_ccmd *ccmd = mdl_arritm( &world->ent_ccmd, index );
294 vg_execute_console_input( mdl_pstr(&world->meta, ccmd->pstr_command) );
295 }
296 }
297
298 /*
299 * BVH implementation
300 * ----------------------------------------------------------------------------
301 */
302
303 VG_STATIC void
304 entity_bh_expand_bound( void *user, boxf bound, u32 item_index ){
305 world_instance *world = user;
306
307 u32 id = world->entity_list[ item_index ],
308 type = mdl_entity_id_type( id ),
309 index = mdl_entity_id_id( id );
310
311 if( type == k_ent_gate ){
312 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
313 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
314 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
315
316 m4x3_expand_aabb_aabb( gate->to_world, bound, box );
317 }
318 else if( type == k_ent_challenge ){
319 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
320
321 /* TODO: This might be more work than necessary. could maybe just get
322 * away with representing them as points */
323
324 boxf box;
325 box_init_inf( box );
326
327 for( u32 i=0; i<challenge->submesh_count; i++ ){
328 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
329 challenge->submesh_start+i );
330 box_concat( box, sm->bbx );
331 }
332
333 m4x3f transform;
334 mdl_transform_m4x3( &challenge->transform, transform );
335 m4x3_expand_aabb_aabb( transform, bound, box );
336 }
337 else if( type == k_ent_volume ){
338 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
339 m4x3_expand_aabb_aabb( volume->to_world, bound,
340 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}} );
341 }
342 }
343
344 VG_STATIC float entity_bh_centroid( void *user, u32 item_index, int axis ){
345 world_instance *world = user;
346
347 u32 id = world->entity_list[ item_index ],
348 type = mdl_entity_id_type( id ),
349 index = mdl_entity_id_id( id );
350
351 if( type == k_ent_gate ){
352 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
353 return gate->to_world[3][axis];
354 }
355 else if( type == k_ent_challenge ){
356 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
357 return challenge->transform.co[axis];
358 }
359 else if( type == k_ent_volume ){
360 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
361 return volume->transform.co[axis];
362 }
363 else {
364 vg_fatal_error( "Programming error\n" );
365 return INFINITY;
366 }
367 }
368
369 VG_STATIC void entity_bh_swap( void *user, u32 ia, u32 ib ){
370 world_instance *world = user;
371
372 u32 a = world->entity_list[ ia ],
373 b = world->entity_list[ ib ];
374
375 world->entity_list[ ia ] = b;
376 world->entity_list[ ib ] = a;
377 }
378
379 VG_STATIC void entity_bh_debug( void *user, u32 item_index ){
380 world_instance *world = user;
381
382 u32 id = world->entity_list[ item_index ],
383 type = mdl_entity_id_type( id ),
384 index = mdl_entity_id_id( id );
385
386 if( type == k_ent_gate ){
387 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
388 boxf box = {{ -gate->dimensions[0], -gate->dimensions[1], -0.1f },
389 { gate->dimensions[0], gate->dimensions[1], 0.1f }};
390 vg_line_boxf_transformed( gate->to_world, box, 0xf000ff00 );
391 }
392 else if( type == k_ent_challenge ){
393 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
394 boxf box;
395 box_init_inf( box );
396
397 for( u32 i=0; i<challenge->submesh_count; i++ ){
398 mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
399 challenge->submesh_start+i );
400 box_concat( box, sm->bbx );
401 }
402
403 m4x3f transform;
404 mdl_transform_m4x3( &challenge->transform, transform );
405 vg_line_boxf_transformed( transform, box, 0xf000ff00 );
406 }
407 else if( type == k_ent_volume ){
408 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
409 vg_line_boxf_transformed( volume->to_world,
410 (boxf){{-1.0f,-1.0f,-1.0f},{ 1.0f, 1.0f, 1.0f}},
411 0xf000ff00 );
412 }
413 else{
414 vg_fatal_error( "Programming error\n" );
415 }
416 }
417
418 VG_STATIC void entity_bh_closest( void *user, u32 item_index, v3f point,
419 v3f closest ){
420 world_instance *world = user;
421
422 u32 id = world->entity_list[ item_index ],
423 type = mdl_entity_id_type( id ),
424 index = mdl_entity_id_id( id );
425
426 if( type == k_ent_gate ){
427 ent_gate *gate = mdl_arritm( &world->ent_gate, index );
428 v3_copy( gate->to_world[3], closest );
429 }
430 else if( type == k_ent_challenge ){
431 ent_challenge *challenge = mdl_arritm( &world->ent_challenge, index );
432 v3_copy( challenge->transform.co, closest );
433 }
434 else if( type == k_ent_volume ){
435 ent_volume *volume = mdl_arritm( &world->ent_volume, index );
436 v3_copy( volume->to_world[3], closest );
437 }
438 else{
439 vg_fatal_error( "Programming error\n" );
440 }
441 }
442
443 #endif /* WORLD_ENTITY_C */