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 imageWidth
, imageHeight
;
184 void *imageData
= bzGfxLoadAsepriteImage(arena
, &imageWidth
, &imageHeight
, spritesheetName
); // FIXME, temp arena
185 bzGfxPrepareSpritesheet(arena
, imageWidth
, imageHeight
, imageData
);
189 const char *fontName
= json_object_get_string(sceneJsonObject
, "font");
190 BZFont
*font
= bzGfxLoadFont(arena
, fontName
); // FIXME, temp arena??
191 bzGfxPrepareFont(arena
, font
);
193 JSON_Array
*actorDefinitionsArray
= json_object_get_array(sceneJsonObject
, "actors");
194 size_t actorDefinitionsArrayCount
= json_array_get_count(actorDefinitionsArray
);
195 scene
->actorDefinitionCount
= actorDefinitionsArrayCount
;
197 size_t startDefinition
= 0;
198 const char *sceneActorName
= json_object_get_string(sceneJsonObject
, "scene_actor");
199 if (sceneActorName
!= NULL
) {
200 scene
->actorDefinitionCount
+= 1;
204 scene
->actorDefinitions
= (BZSceneActorDefinition
*)bzMemoryAlloc(arena
, scene
->actorDefinitionCount
* sizeof(BZSceneActorDefinition
));
206 size_t maxInstanceSize
= 0;
208 if (sceneActorName
!= NULL
) {
209 bzGameSceneActorLoadDefinition(&scene
->actorDefinitions
[0], scene
, arena
, "%s", sceneActorName
); // fixme
210 maxInstanceSize
= scene
->actorDefinitions
[0].instanceSize
;
213 for (size_t i
= 0; i
< actorDefinitionsArrayCount
; ++i
) {
214 const char *actorName
= json_array_get_string(actorDefinitionsArray
, i
);
215 size_t outIdx
= i
+ startDefinition
;
216 bzGameSceneActorLoadDefinition(&scene
->actorDefinitions
[outIdx
], scene
, arena
, "%s", actorName
); // fixme
217 maxInstanceSize
= bzMax(maxInstanceSize
, scene
->actorDefinitions
[outIdx
].instanceSize
);
220 // FIXME, alignment for instance size
222 scene
->lastActorIndex
= 0;
223 scene
->actorInstanceCount
= 1024;
225 scene
->actorInstances
= (BZSceneActor
*)bzMemoryAlloc(arena
, scene
->actorInstanceCount
* sizeof(BZSceneActor
));
226 for (size_t i
= 0; i
< scene
->actorInstanceCount
; ++i
) {
227 scene
->actorInstances
[i
].actor
.instance
= bzMemoryAlloc(arena
, maxInstanceSize
);
230 if (sceneActorName
!= NULL
) {
231 bzGameSceneSetupActor(&scene
->actorInstances
[0], scene
, &scene
->actorDefinitions
[0], "scene");
234 // int hasAgents = json_object_get_boolean(sceneJsonObject, "agents");
235 // if (hasAgents > 0) {
236 // scene->agentSimulation = bzFXCreateAgentSimulation(arena, scene->actorInstanceCount, "%s.agents", identifier);
239 JSON_Array
*layerDefinitionsArray
= json_object_get_array(sceneJsonObject
, "layers");
240 scene
->layerCount
= json_array_get_count(layerDefinitionsArray
);
241 scene
->layers
= bzMemoryAlloc(arena
, scene
->layerCount
* sizeof(BZSceneLayer
));
242 for (size_t i
= 0; i
< scene
->layerCount
; ++i
) {
243 JSON_Object
*layerDefinition
= json_array_get_object(layerDefinitionsArray
, i
);
244 stbsp_snprintf(scene
->layers
[i
].identifier
, kBZMaxIdentifierLength
, "%s", json_object_get_string(layerDefinition
, "identifier"));
245 scene
->layers
[i
].identifierHash
= bzIdentifierHashFromIdentifier(scene
->layers
[i
].identifier
);
247 const char *particleSystemName
= json_object_get_string(layerDefinition
, "particles");
248 if (particleSystemName
!= NULL
) {
249 BZParticleSystemID particleSystem
= bzFXLoadParticleSystem(arena
, "%s", particleSystemName
); // FIXME, pool these...
250 int hasCollision2
= json_object_get_boolean(layerDefinition
, "collision"); // FIXME
251 scene
->layers
[i
].particleSimulation
= bzFXCreateParticleSimulation(arena
, (hasCollision2
> 0) ? 1024 : (5 * 1024), particleSystem
, "%s.particles", scene
->layers
[i
].identifier
); // FIXME, memory
254 if (json_object_has_value_of_type(layerDefinition
, "output", JSONNumber
)) {
255 scene
->layers
[i
].outputBuffer
= (int)json_object_get_number(layerDefinition
, "output");
256 if (particleSystemName
== NULL
|| json_object_get_boolean(layerDefinition
, "custom-particle") > 0) {
257 scene
->layers
[i
].drawQueue
= bzGfxDrawQueueCreate(arena
, "scene.%s.drawQueue", scene
->layers
[i
].identifier
);
261 const char *tilemapName
= json_object_get_string(layerDefinition
, "tilemap");
262 if (tilemapName
!= NULL
) {
263 scene
->layers
[i
].tilemap
= bzGameLoadTilemap(arena
, "%s", tilemapName
);
266 int hasCollision
= json_object_get_boolean(layerDefinition
, "collision");
267 if (hasCollision
> 0) {
268 if (scene
->layers
[i
].particleSimulation
== NULL
) {
269 scene
->layers
[i
].collisionSpace
= bzCollisionMakeSpace(arena
, scene
->actorInstanceCount
, "%s.collision", scene
->layers
[i
].identifier
);
271 scene
->layers
[i
].collisionSpace
= bzCollisionMakeSpace(arena
, 1024, "%s.particle.collision", scene
->layers
[i
].identifier
);
273 if (json_object_has_value_of_type(layerDefinition
, "collision-radius", JSONNumber
)) {
274 scene
->layers
[i
].particleCollisionRadiusMultiplier
= json_object_get_number(layerDefinition
, "collision-radius");
276 scene
->layers
[i
].particleCollisionRadiusMultiplier
= 1.0f
;
278 scene
->layers
[i
].particleCollisionTagHash
= bzIdentifierHashFromIdentifier(particleSystemName
);
283 scene
->currentEvents
= bzMemoryAlloc(arena
, kBZScriptingEnvironmentEventCount
* sizeof(BZSceneEvent
));
284 scene
->enqueuedEvents
= bzMemoryAlloc(arena
, kBZScriptingEnvironmentEventCount
* sizeof(BZSceneEvent
));
286 json_value_free(sceneJson
);
288 bzLog("Opened scene %s", identifier
);
293 bool bzGameSceneUpdate(BZSceneID scene
, uint8_t pauseFlags
, uint8_t systemTicks
) {
294 float deltaTime
= (float)systemTicks
/ (float)gSystemTicksPerSecond
;
296 for (size_t i
= 0; i
< scene
->layerCount
; ++i
) {
297 BZSceneLayer
*layer
= &scene
->layers
[i
];
298 if (layer
->particleSimulation
== NULL
) {
299 bzGfxDrawQueueClear(layer
->drawQueue
);
301 if (layer
->collisionSpace
!= NULL
) {
302 bzCollisionResetSpace(layer
->collisionSpace
);
306 // if (scene->agentSimulation != NULL) {
307 // SVMOperand playerX;
308 // bzScriptingGetEnvironmentPublic(&playerX, scene, bzIdentifierHashFromIdentifier("shipX"));
310 // SVMOperand playerY;
311 // bzScriptingGetEnvironmentPublic(&playerY, scene, bzIdentifierHashFromIdentifier("shipY"));
313 // BZVector playerPos = bzVectorMake(FIXED_TO_FLOAT(playerX.floatLiteral), FIXED_TO_FLOAT(playerY.floatLiteral));
315 // bzFXUpdateAgentSimulation(scene->agentSimulation, deltaTime, NULL, &playerPos);
318 for (size_t i
= 0; i
< scene
->layerCount
; ++i
) {
319 BZSceneLayer
*layer
= &scene
->layers
[i
];
320 if (layer
->particleSimulation
!= NULL
) {
321 bzFXUpdateParticleSystem(layer
->particleSimulation
, deltaTime
);
322 if (layer
->collisionSpace
!= NULL
) {
323 bzCollisionPopulateParticles(layer
->collisionSpace
, layer
->particleSimulation
, layer
->particleCollisionTagHash
, layer
->particleCollisionRadiusMultiplier
);
328 for (int32_t priority
= scene
->minActorPriority
; priority
<= scene
->maxActorPriority
; ++priority
) {
329 for (size_t i
= 0; i
< scene
->actorInstanceCount
; ++i
) {
330 BZSceneActor
*sceneActor
= &scene
->actorInstances
[i
];
331 if (sceneActor
->running
&& sceneActor
->priority
== priority
) {
332 SVMRunOutcome outcome
= svmRunModuleInstance(sceneActor
->actor
.instance
);
333 sceneActor
->running
= (outcome
== SVMRunOutcomeSuspended
);
338 scene
->ticks
+= systemTicks
;
340 scene
->eventCount
= scene
->nextEnqueuedEvent
;
341 scene
->nextEnqueuedEvent
= 0;
343 BZSceneEvent
*tmp
= scene
->currentEvents
;
344 scene
->currentEvents
= scene
->enqueuedEvents
;
345 scene
->enqueuedEvents
= tmp
;
347 return scene
->actorInstances
[0].running
;
350 void bzGameSceneDraw(BZSceneID scene
) {
351 for (size_t i
= 0; i
< scene
->layerCount
; ++i
) {
352 bzSetOutputBuffer(scene
->layers
[i
].outputBuffer
);
353 if (scene
->layers
[i
].particleSimulation
!= NULL
) {
354 bzGfxDrawParticles(scene
->layers
[i
].particleSimulation
, scene
->layers
[i
].drawQueue
);
356 bzGfxDrawQueueRun(scene
->layers
[i
].drawQueue
);
359 //if (scene->layers[i].collisionSpace != NULL) {
360 // bzCollisionDrawDebug(scene->layers[i].collisionSpace);
363 // if (i == 3 && scene->agentSimulation != NULL) { // FIXME
364 // bzFXAgentSimulationDrawDebug(scene->agentSimulation);
371 BZActorID
bzGameSceneAddActor(BZSceneID scene
, const char *identifierFmt
, ...) {
372 bzMakeIdentifier(identifier
, identifierFmt
);
374 BZIdentifierHash definitionIdentifierHash
= bzIdentifierHashFromIdentifier(identifier
);
375 BZSceneActorDefinition
*definition
= NULL
;
376 for (size_t i
= 0; i
< scene
->actorDefinitionCount
; ++i
) { // fixme
377 if (definitionIdentifierHash
== scene
->actorDefinitions
[i
].identifierHash
) {
378 definition
= &scene
->actorDefinitions
[i
];
382 bzAssertMessage(definition
!= NULL
, "Undefined actor '%s'", identifier
);
384 BZSceneActor
*sceneActor
= NULL
;
385 for (size_t i
= 0; i
< scene
->actorInstanceCount
; ++i
) {
386 ++scene
->lastActorIndex
;
388 size_t idx
= (scene
->lastActorIndex
% (scene
->actorInstanceCount
- 1)) + 1; // 0 is only for the scene script
389 if (scene
->actorInstances
[idx
].running
== false) {
390 sceneActor
= &scene
->actorInstances
[idx
];
394 bzAssertMessage(sceneActor
!= NULL
, "Out of actors");
396 definition
->spawnCount
+= 1;
398 return bzGameSceneSetupActor(sceneActor
, scene
, definition
, "%s-%u", identifier
, definition
->spawnCount
);
401 BZActorID
bzGameSceneFindActor(BZSceneID scene
, const char *identifierFmt
, ...) {
402 bzMakeIdentifier(identifier
, identifierFmt
);
403 BZIdentifierHash identifierHash
= bzIdentifierHashFromIdentifier(identifier
);
404 for (size_t i
= 0; i
< scene
->actorInstanceCount
; ++i
) { // fixme
405 if (scene
->actorInstances
[i
].actor
.identifierHash
== identifierHash
) {
406 return &scene
->actorInstances
[i
].actor
;
412 float bzGameSceneGetTime(BZSceneID scene
) {
413 float time
= (float)scene
->ticks
/ (float)gSystemTicksPerSecond
;
417 bool bzGameSceneQueryEvent(BZSceneEventData
*dataOut
, BZSceneID scene
, BZIdentifierHash eventIdentifier
) {
418 for (size_t i
= 0; i
< scene
->eventCount
; ++i
) {
419 BZSceneEvent
*event
= &scene
->currentEvents
[i
];
420 if (event
->identifierHash
== eventIdentifier
) {
421 if (dataOut
!= NULL
) {
422 *dataOut
= event
->data
;
430 void bzGameScenePostEvent(BZSceneID scene
, BZIdentifierHash eventIdentifier
, const BZSceneEventData
*data
) {
431 bzAssert(scene
->nextEnqueuedEvent
< kBZScriptingEnvironmentEventCount
);
432 BZSceneEvent
*event
= &scene
->enqueuedEvents
[scene
->nextEnqueuedEvent
++];
433 event
->identifierHash
= eventIdentifier
;
439 BZSceneLayer
*getLayer(BZSceneID scene
, BZIdentifierHash layerIdentifierHash
) {
440 for (size_t i
= 0; i
< scene
->layerCount
; ++i
) {
441 if (scene
->layers
[i
].identifierHash
== layerIdentifierHash
) {
442 return &scene
->layers
[i
];
448 BZTilemapID
bzGameGetSceneLayerTilemap(BZSceneID scene
, BZIdentifierHash layerIdentifierHash
) {
449 BZSceneLayer
*layer
= getLayer(scene
, layerIdentifierHash
);
451 return layer
->tilemap
;
457 BZDrawQueueID
bzGameGetSceneLayerDrawQueue(BZSceneID scene
, BZIdentifierHash layerIdentifierHash
) {
458 BZSceneLayer
*layer
= getLayer(scene
, layerIdentifierHash
);
460 return layer
->drawQueue
;
466 BZParticleSimulationID
bzGameGetSceneLayerParticleSimulation(BZSceneID scene
, BZIdentifierHash layerIdentifierHash
) {
467 BZSceneLayer
*layer
= getLayer(scene
, layerIdentifierHash
);
469 return layer
->particleSimulation
;
475 BZCollisionSpaceID
bzGameGetSceneLayerCollisionSpace(BZSceneID scene
, BZIdentifierHash layerIdentifierHash
) {
476 BZSceneLayer
*layer
= getLayer(scene
, layerIdentifierHash
);
478 return layer
->collisionSpace
;
486 extern void bzGameSceneSetTimeTicksPerSecond(uint8_t tps);
487 extern void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags); // Pause timer on these flags
488 extern float bzGameSceneTime(uint8_t timeIdx);
490 void bzGameSceneSetTimeTicksPerSecond(uint8_t tps) {
494 void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags) {
495 times[timeIdx].pauseFlags = pauseFlags;
498 float bzGameSceneTime(uint8_t timeIdx) {
499 float time = (float)times[timeIdx].ticks / (float)sceneTPS;
504 //BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene) {
505 // return scene->agentSimulation;
508 BZAudioPlaybackEngineID
bzGameGetAudioPlaybackEngine(BZSceneID scene
) {
509 return scene
->audioEngine
;
512 bool bzScriptingGetEnvironmentPublic(SVMOperand
*out
, BZSceneID scene
, BZIdentifierHash identifierHash
) {
513 for (size_t i
= 0; i
< kBZScriptingEnvironmentPublicCount
; ++i
) {
514 if (scene
->publics
[i
].identifierHash
== identifierHash
) {
516 out
->__raw
= scene
->publics
[i
].value
.__raw
;
524 void bzScriptingSetEnvironmentPublic(BZSceneID scene
, BZIdentifierHash identifierHash
, SVMOperand value
) {
525 for (size_t i
= 0; i
< kBZScriptingEnvironmentPublicCount
; ++i
) {
526 if (scene
->publics
[i
].identifierHash
== 0) {
527 scene
->publics
[i
].identifierHash
= identifierHash
;
529 if (scene
->publics
[i
].identifierHash
== identifierHash
) {
530 scene
->publics
[i
].value
.__raw
= value
.__raw
;
534 bzError("Out of globals!!");
537 size_t bzGameGetSceneLayerCount(BZSceneID scene
) {
538 return scene
->layerCount
;
541 BZSceneLayerID
bzGameGetSceneLayerAtIndex(BZSceneID scene
, size_t idx
) {
542 bzAssert(idx
< scene
->layerCount
);
543 return &scene
->layers
[idx
];
547 BZCollisionSpaceID
bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer
) {
548 return layer
->collisionSpace
;
551 BZParticleSimulationID
bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer
) {
552 return layer
->particleSimulation
;