1 #include <bz/game/scene_internal.h>
3 #include <bz/collision/particle_collision.h>
4 //#include <bz/fx/agent_simulation.h>
5 #include <bz/fx/particle_simulation.h>
6 #include <bz/fx/particle_system.h>
7 #include <bz/game/actor_internal.h>
8 #include <bz/game/tilemap.h>
9 #include <bz/gfx/aseprite.h>
10 #include <bz/gfx/font_internal.h>
11 #include <bz/gfx/gfx_internal.h>
12 #include <bz/gfx/particle_drawing.h>
13 #include <bz/math/math.h>
14 #include <bz/memory/allocator.h>
15 #include <bz/resources/resource.h>
16 #include <bz/scripting/bindings_internal.h>
17 #include <bz/scripting/script.h>
18 #include <bz/types/identifier_internal.h>
20 #include <string.h> // strncpy
22 struct BZSceneActorDefinition {
23 //char identifier[kBZMaxIdentifierLength]; // fixme
24 BZIdentifierHash identifierHash;
30 typedef struct BZSceneActorDefinition BZSceneActorDefinition;
32 /*struct BZSceneParticleSystem {
33 char identifier[kBZMaxIdentifierLength]; // fixme
34 BZIdentifierHash identifierHash;
35 BZParticleSystemID particleSystem;
37 typedef struct BZSceneParticleSystem BZSceneParticleSystem;*/
40 char identifier[kBZMaxIdentifierLength]; // fixme
41 BZIdentifierHash identifierHash;
42 BZDrawQueueID drawQueue;
44 BZParticleSimulationID particleSimulation;
45 BZCollisionSpaceID collisionSpace;
46 BZIdentifierHash particleCollisionTagHash;
47 float particleCollisionRadiusMultiplier;
51 ////#define kMaxActorInstances 50//256
55 BZScriptBindingMetadata scriptMetadata;
59 typedef struct BZSceneActor BZSceneActor;
61 #define kBZScriptingEnvironmentPublicCount 256
62 #define kBZScriptingEnvironmentEventCount 256
64 struct BZScriptingEnvironmentPublic {
65 BZIdentifierHash identifierHash;
68 typedef struct BZScriptingEnvironmentPublic BZScriptingEnvironmentPublic;
71 BZIdentifierHash identifierHash;
72 BZSceneEventData data;
74 typedef struct BZSceneEvent BZSceneEvent;
77 // BZScriptingEnvironmentID sceneEnvironment;
78 // BZScriptID sceneScript;
79 // BZScriptBindingMetadata sceneScriptMetadata;
80 // BZScriptInstanceID sceneScriptInstance;
81 BZSceneActorDefinition *actorDefinitions;
82 size_t actorDefinitionCount;
87 int32_t minActorPriority;
88 int32_t maxActorPriority;
90 size_t lastActorIndex;
91 size_t actorInstanceCount;
92 BZSceneActor *actorInstances;
94 BZScriptingEnvironmentPublic publics[kBZScriptingEnvironmentPublicCount];
96 BZAudioPlaybackEngineID audioEngine;
98 // BZAgentSimulationID agentSimulation;
101 BZSceneEvent *currentEvents;
102 size_t nextEnqueuedEvent;
103 BZSceneEvent *enqueuedEvents;
105 // char targetSceneIdentifier[kBZMaxIdentifierLength]; // fixme
108 uint8_t gSystemTicksPerSecond;
110 static void bzGameSceneActorLoadDefinition(BZSceneActorDefinition *definitionOut, BZSceneID scene, BZMemoryArenaID arena, const char *identifierFmt, ...) {
111 bzMakeIdentifier(identifier, identifierFmt);
112 definitionOut->identifierHash = bzIdentifierHashFromIdentifier(identifier);
114 BZResourceID handle = bzResourcesOpenResource("scene", "assets/actors/%s.actor.json", identifier);
115 bzAssertMessage(handle != NULL, "Invalid actor definition '%s'", identifier);
117 size_t length = bzResourcesFileLength(handle);
118 char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack
119 bzResourcesReadBytes(handle, data, length);
120 bzResourcesCloseResource(handle);
122 //json_set_allocation_functions
123 JSON_Value *actorDefinitionJson = json_parse_string(data);
124 JSON_Object *actorDefinitionJsonObject = json_object(actorDefinitionJson);
126 const char *scriptName = json_object_get_string(actorDefinitionJsonObject, "script");
127 if (scriptName != NULL) {
128 definitionOut->module = bzScriptingLoadScriptModule(arena, "%s", scriptName);
129 definitionOut->instanceSize = svmGetModuleInstanceSize(definitionOut->module);
132 definitionOut->priority = json_object_get_number(actorDefinitionJsonObject, "priority");
133 scene->minActorPriority = bzMin(scene->minActorPriority, definitionOut->priority);
134 scene->maxActorPriority = bzMax(scene->maxActorPriority, definitionOut->priority);
136 json_value_free(actorDefinitionJson);
138 bzLog("Opened actor %s", identifier);
141 static BZActorID bzGameSceneSetupActor(BZSceneActor *sceneActor, BZSceneID scene, const BZSceneActorDefinition *definition, const char *identifierFmt, ...) {
142 BZActorID actor = &sceneActor->actor;
143 BZScriptBindingMetadata *metadata = &sceneActor->scriptMetadata;
145 bzInsertIdentifier(actor->identifier, identifierFmt);
146 actor->identifierHash = bzIdentifierHashFromIdentifier(actor->identifier);
148 bzAssertMessage(definition != NULL, "Could not find definition for '%s'", actor->identifier);
150 sceneActor->priority = definition->priority;
152 actor->instance = svnResetModuleInstanceMemory(definition->module, actor->instance, metadata, bzScriptingBindingsLookupNativeFunction);
153 bzScriptingInitialiseMetadata((BZScriptBindingMetadata *)&sceneActor->scriptMetadata, actor, scene, scene->lastActorIndex);
155 sceneActor->running = true;
160 BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audioEngine, const char *identifierFmt, ...) {
161 bzMakeIdentifier(identifier, identifierFmt);
163 BZSceneID scene = bzMemoryAlloc(arena, sizeof(BZScene));
165 BZResourceID handle = bzResourcesOpenResource("scene", "assets/scenes/%s.scene.json", identifier);
166 size_t length = bzResourcesFileLength(handle);
167 char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack
168 bzResourcesReadBytes(handle, data, length);
169 bzResourcesCloseResource(handle);
171 //json_set_allocation_functions
172 JSON_Value *sceneJson = json_parse_string(data);
173 JSON_Object *sceneJsonObject = json_object(sceneJson);
175 const char *soundbankName = json_object_get_string(sceneJsonObject, "soundbank");
176 if (soundbankName != NULL && audioEngine != NULL) {
177 scene->audioEngine = audioEngine;
178 bzAudioPlaybackUseSoundbank(scene->audioEngine, arena, soundbankName);
181 const char *spritesheetName = json_object_get_string(sceneJsonObject, "spritesheet");
182 if (spritesheetName != NULL) {
183 size_t imageFrames, imageWidth, imageHeight;
184 void *imageData = bzGfxLoadAsepriteImage(arena, &imageFrames, &imageWidth, &imageHeight, spritesheetName); // FIXME, temp arena
185 bzGfxPrepareSpritesheet(arena, imageFrames, imageWidth, imageHeight, imageData);
189 const char *fontName = json_object_get_string(sceneJsonObject, "font");
190 if (fontName != NULL) {
191 BZFont *font = bzGfxLoadFont(arena, fontName); // FIXME, temp arena??
192 bzGfxPrepareFont(arena, font);
195 JSON_Array *actorDefinitionsArray = json_object_get_array(sceneJsonObject, "actors");
196 size_t actorDefinitionsArrayCount = json_array_get_count(actorDefinitionsArray);
197 scene->actorDefinitionCount = actorDefinitionsArrayCount;
199 size_t startDefinition = 0;
200 const char *sceneActorName = json_object_get_string(sceneJsonObject, "scene_actor");
201 if (sceneActorName != NULL) {
202 scene->actorDefinitionCount += 1;
206 scene->actorDefinitions = (BZSceneActorDefinition *)bzMemoryAlloc(arena, scene->actorDefinitionCount * sizeof(BZSceneActorDefinition));
208 size_t maxInstanceSize = 0;
210 if (sceneActorName != NULL) {
211 bzGameSceneActorLoadDefinition(&scene->actorDefinitions[0], scene, arena, "%s", sceneActorName); // fixme
212 maxInstanceSize = scene->actorDefinitions[0].instanceSize;
215 for (size_t i = 0; i < actorDefinitionsArrayCount; ++i) {
216 const char *actorName = json_array_get_string(actorDefinitionsArray, i);
217 size_t outIdx = i + startDefinition;
218 bzGameSceneActorLoadDefinition(&scene->actorDefinitions[outIdx], scene, arena, "%s", actorName); // fixme
219 maxInstanceSize = bzMax(maxInstanceSize, scene->actorDefinitions[outIdx].instanceSize);
222 // FIXME, alignment for instance size
224 scene->lastActorIndex = 0;
225 scene->actorInstanceCount = 1024;
227 scene->actorInstances = (BZSceneActor *)bzMemoryAlloc(arena, scene->actorInstanceCount * sizeof(BZSceneActor));
228 for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
229 scene->actorInstances[i].actor.instance = bzMemoryAlloc(arena, maxInstanceSize);
232 if (sceneActorName != NULL) {
233 bzGameSceneSetupActor(&scene->actorInstances[0], scene, &scene->actorDefinitions[0], "scene");
236 // int hasAgents = json_object_get_boolean(sceneJsonObject, "agents");
237 // if (hasAgents > 0) {
238 // scene->agentSimulation = bzFXCreateAgentSimulation(arena, scene->actorInstanceCount, "%s.agents", identifier);
241 JSON_Array *layerDefinitionsArray = json_object_get_array(sceneJsonObject, "layers");
242 scene->layerCount = json_array_get_count(layerDefinitionsArray);
243 scene->layers = bzMemoryAlloc(arena, scene->layerCount * sizeof(BZSceneLayer));
244 for (size_t i = 0; i < scene->layerCount; ++i) {
245 JSON_Object *layerDefinition = json_array_get_object(layerDefinitionsArray, i);
246 stbsp_snprintf(scene->layers[i].identifier, kBZMaxIdentifierLength, "%s", json_object_get_string(layerDefinition, "identifier"));
247 scene->layers[i].identifierHash = bzIdentifierHashFromIdentifier(scene->layers[i].identifier);
249 const char *particleSystemName = json_object_get_string(layerDefinition, "particles");
250 if (particleSystemName != NULL) {
251 BZParticleSystemID particleSystem = bzFXLoadParticleSystem(arena, "%s", particleSystemName); // FIXME, pool these...
252 int hasCollision2 = json_object_get_boolean(layerDefinition, "collision"); // FIXME
253 scene->layers[i].particleSimulation = bzFXCreateParticleSimulation(arena, (hasCollision2 > 0) ? 1024 : (5 * 1024), particleSystem, "%s.particles", scene->layers[i].identifier); // FIXME, memory
256 if (json_object_has_value_of_type(layerDefinition, "output", JSONNumber)) {
257 scene->layers[i].outputBuffer = (int)json_object_get_number(layerDefinition, "output");
258 if (particleSystemName == NULL || json_object_get_boolean(layerDefinition, "custom-particle") > 0) {
259 scene->layers[i].drawQueue = bzGfxDrawQueueCreate(arena, "scene.%s.drawQueue", scene->layers[i].identifier);
263 const char *tilemapName = json_object_get_string(layerDefinition, "tilemap");
264 if (tilemapName != NULL) {
265 scene->layers[i].tilemap = bzGameLoadTilemap(arena, "%s", tilemapName);
268 int hasCollision = json_object_get_boolean(layerDefinition, "collision");
269 if (hasCollision > 0) {
270 if (scene->layers[i].particleSimulation == NULL) {
271 scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, scene->actorInstanceCount, "%s.collision", scene->layers[i].identifier);
273 scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, 1024, "%s.particle.collision", scene->layers[i].identifier);
275 if (json_object_has_value_of_type(layerDefinition, "collision-radius", JSONNumber)) {
276 scene->layers[i].particleCollisionRadiusMultiplier = json_object_get_number(layerDefinition, "collision-radius");
278 scene->layers[i].particleCollisionRadiusMultiplier = 1.0f;
280 scene->layers[i].particleCollisionTagHash = bzIdentifierHashFromIdentifier(particleSystemName);
285 scene->currentEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent));
286 scene->enqueuedEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent));
288 json_value_free(sceneJson);
290 bzLog("Opened scene %s", identifier);
295 bool bzGameSceneUpdate(BZSceneID scene, uint8_t pauseFlags, uint8_t systemTicks) {
296 float deltaTime = (float)systemTicks / (float)gSystemTicksPerSecond;
298 for (size_t i = 0; i < scene->layerCount; ++i) {
299 BZSceneLayer *layer = &scene->layers[i];
300 if (layer->particleSimulation == NULL) {
301 bzGfxDrawQueueClear(layer->drawQueue);
303 if (layer->collisionSpace != NULL) {
304 bzCollisionResetSpace(layer->collisionSpace);
308 // if (scene->agentSimulation != NULL) {
309 // SVMOperand playerX;
310 // bzScriptingGetEnvironmentPublic(&playerX, scene, bzIdentifierHashFromIdentifier("shipX"));
312 // SVMOperand playerY;
313 // bzScriptingGetEnvironmentPublic(&playerY, scene, bzIdentifierHashFromIdentifier("shipY"));
315 // BZVector playerPos = bzVectorMake(FIXED_TO_FLOAT(playerX.floatLiteral), FIXED_TO_FLOAT(playerY.floatLiteral));
317 // bzFXUpdateAgentSimulation(scene->agentSimulation, deltaTime, NULL, &playerPos);
320 for (size_t i = 0; i < scene->layerCount; ++i) {
321 BZSceneLayer *layer = &scene->layers[i];
322 if (layer->particleSimulation != NULL) {
323 bzFXUpdateParticleSystem(layer->particleSimulation, deltaTime);
324 if (layer->collisionSpace != NULL) {
325 bzCollisionPopulateParticles(layer->collisionSpace, layer->particleSimulation, layer->particleCollisionTagHash, layer->particleCollisionRadiusMultiplier);
330 for (int32_t priority = scene->minActorPriority; priority <= scene->maxActorPriority; ++priority) {
331 for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
332 BZSceneActor *sceneActor = &scene->actorInstances[i];
333 if (sceneActor->running && sceneActor->priority == priority) {
334 SVMRunOutcome outcome = svmRunModuleInstance(sceneActor->actor.instance);
335 sceneActor->running = (outcome == SVMRunOutcomeSuspended);
340 scene->ticks += systemTicks;
342 scene->eventCount = scene->nextEnqueuedEvent;
343 scene->nextEnqueuedEvent = 0;
345 BZSceneEvent *tmp = scene->currentEvents;
346 scene->currentEvents = scene->enqueuedEvents;
347 scene->enqueuedEvents = tmp;
349 return scene->actorInstances[0].running;
352 void bzGameSceneDraw(BZSceneID scene) {
353 for (size_t i = 0; i < scene->layerCount; ++i) {
354 bzSetOutputBuffer(scene->layers[i].outputBuffer);
355 if (scene->layers[i].particleSimulation != NULL) {
356 bzGfxDrawParticles(scene->layers[i].particleSimulation, scene->layers[i].drawQueue);
358 bzGfxDrawQueueRun(scene->layers[i].drawQueue);
361 //if (scene->layers[i].collisionSpace != NULL) {
362 // bzCollisionDrawDebug(scene->layers[i].collisionSpace);
365 // if (i == 3 && scene->agentSimulation != NULL) { // FIXME
366 // bzFXAgentSimulationDrawDebug(scene->agentSimulation);
373 BZActorID bzGameSceneAddActor(BZSceneID scene, const char *identifierFmt, ...) {
374 bzMakeIdentifier(identifier, identifierFmt);
376 BZIdentifierHash definitionIdentifierHash = bzIdentifierHashFromIdentifier(identifier);
377 BZSceneActorDefinition *definition = NULL;
378 for (size_t i = 0; i < scene->actorDefinitionCount; ++i) { // fixme
379 if (definitionIdentifierHash == scene->actorDefinitions[i].identifierHash) {
380 definition = &scene->actorDefinitions[i];
384 bzAssertMessage(definition != NULL, "Undefined actor '%s'", identifier);
386 BZSceneActor *sceneActor = NULL;
387 for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
388 ++scene->lastActorIndex;
390 size_t idx = (scene->lastActorIndex % (scene->actorInstanceCount - 1)) + 1; // 0 is only for the scene script
391 if (scene->actorInstances[idx].running == false) {
392 sceneActor = &scene->actorInstances[idx];
396 bzAssertMessage(sceneActor != NULL, "Out of actors");
398 definition->spawnCount += 1;
400 return bzGameSceneSetupActor(sceneActor, scene, definition, "%s-%u", identifier, definition->spawnCount);
403 BZActorID bzGameSceneFindActor(BZSceneID scene, const char *identifierFmt, ...) {
404 bzMakeIdentifier(identifier, identifierFmt);
405 BZIdentifierHash identifierHash = bzIdentifierHashFromIdentifier(identifier);
406 for (size_t i = 0; i < scene->actorInstanceCount; ++i) { // fixme
407 if (scene->actorInstances[i].actor.identifierHash == identifierHash) {
408 return &scene->actorInstances[i].actor;
414 float bzGameSceneGetTime(BZSceneID scene) {
415 float time = (float)scene->ticks / (float)gSystemTicksPerSecond;
419 bool bzGameSceneQueryEvent(BZSceneEventData *dataOut, BZSceneID scene, BZIdentifierHash eventIdentifier) {
420 for (size_t i = 0; i < scene->eventCount; ++i) {
421 BZSceneEvent *event = &scene->currentEvents[i];
422 if (event->identifierHash == eventIdentifier) {
423 if (dataOut != NULL) {
424 *dataOut = event->data;
432 void bzGameScenePostEvent(BZSceneID scene, BZIdentifierHash eventIdentifier, const BZSceneEventData *data) {
433 bzAssert(scene->nextEnqueuedEvent < kBZScriptingEnvironmentEventCount);
434 BZSceneEvent *event = &scene->enqueuedEvents[scene->nextEnqueuedEvent++];
435 event->identifierHash = eventIdentifier;
441 BZSceneLayer *getLayer(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
442 for (size_t i = 0; i < scene->layerCount; ++i) {
443 if (scene->layers[i].identifierHash == layerIdentifierHash) {
444 return &scene->layers[i];
450 BZTilemapID bzGameGetSceneLayerTilemap(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
451 BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
453 return layer->tilemap;
459 BZDrawQueueID bzGameGetSceneLayerDrawQueue(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
460 BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
462 return layer->drawQueue;
468 BZParticleSimulationID bzGameGetSceneLayerParticleSimulation(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
469 BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
471 return layer->particleSimulation;
477 BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
478 BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
480 return layer->collisionSpace;
488 extern void bzGameSceneSetTimeTicksPerSecond(uint8_t tps);
489 extern void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags); // Pause timer on these flags
490 extern float bzGameSceneTime(uint8_t timeIdx);
492 void bzGameSceneSetTimeTicksPerSecond(uint8_t tps) {
496 void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags) {
497 times[timeIdx].pauseFlags = pauseFlags;
500 float bzGameSceneTime(uint8_t timeIdx) {
501 float time = (float)times[timeIdx].ticks / (float)sceneTPS;
506 //BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene) {
507 // return scene->agentSimulation;
510 BZAudioPlaybackEngineID bzGameGetAudioPlaybackEngine(BZSceneID scene) {
511 return scene->audioEngine;
514 bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZSceneID scene, BZIdentifierHash identifierHash) {
515 for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
516 if (scene->publics[i].identifierHash == identifierHash) {
518 out->__raw = scene->publics[i].value.__raw;
526 void bzScriptingSetEnvironmentPublic(BZSceneID scene, BZIdentifierHash identifierHash, SVMOperand value) {
527 for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
528 if (scene->publics[i].identifierHash == 0) {
529 scene->publics[i].identifierHash = identifierHash;
531 if (scene->publics[i].identifierHash == identifierHash) {
532 scene->publics[i].value.__raw = value.__raw;
536 bzError("Out of globals!!");
539 size_t bzGameGetSceneLayerCount(BZSceneID scene) {
540 return scene->layerCount;
543 BZSceneLayerID bzGameGetSceneLayerAtIndex(BZSceneID scene, size_t idx) {
544 bzAssert(idx < scene->layerCount);
545 return &scene->layers[idx];
549 BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer) {
550 return layer->collisionSpace;
553 BZParticleSimulationID bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer) {
554 return layer->particleSimulation;