[Solved] Random exceptions while running multiple physics spaces in parallel

I’ve recently added an algorithm on my server to be able to use multiple physic spaces in parallel to one another in order to reduce load time. Everything seems to be going fine at first, but after I load up the server with multiple physics entities, I get a random exception followed by a repeating class cast exception.

The repeating class cast exception in question:

java.lang.ClassCastException: com.bulletphysics.collision.shapes.CapsuleShape cannot be cast to com.bulletphysics.collision.shapes.CompoundShape
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.jme3.bullet.BulletAppState.postRender(BulletAppState.java:255)
	at com.jme3.app.state.AppStateManager.postRender(AppStateManager.java:312)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:262)
	at com.fadorico.rwby.server.RWBY.update(RWBY.java:215)
	at com.jme3.system.NullContext.run(NullContext.java:132)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: com.bulletphysics.collision.shapes.CapsuleShape cannot be cast to com.bulletphysics.collision.shapes.CompoundShape
	at com.bulletphysics.collision.dispatch.CompoundCollisionAlgorithm.processCollision(CompoundCollisionAlgorithm.java:87)
	at com.bulletphysics.collision.dispatch.DefaultNearCallback.handleCollision(DefaultNearCallback.java:55)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher$CollisionPairCallback.processOverlap(CollisionDispatcher.java:236)
	at com.bulletphysics.collision.broadphase.HashedOverlappingPairCache.processAllOverlappingPairs(HashedOverlappingPairCache.java:190)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher.dispatchAllCollisionPairs(CollisionDispatcher.java:247)
	at com.bulletphysics.collision.dispatch.CollisionWorld.performDiscreteCollisionDetection(CollisionWorld.java:150)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.internalSingleStepSimulation(DiscreteDynamicsWorld.java:378)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.stepSimulation(DiscreteDynamicsWorld.java:339)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:349)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:336)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:132)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:130)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	... 1 more

Right before this exception floods my logs, I get one random exception. Here’s a few of them that I got:

java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.jme3.bullet.BulletAppState.postRender(BulletAppState.java:255)
	at com.jme3.app.state.AppStateManager.postRender(AppStateManager.java:312)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:262)
	at com.fadorico.rwby.server.RWBY.update(RWBY.java:215)
	at com.jme3.system.NullContext.run(NullContext.java:132)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
	at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.processAllTriangles(BvhTriangleMeshShape.java:161)
	at com.bulletphysics.collision.dispatch.ConvexConcaveCollisionAlgorithm.processCollision(ConvexConcaveCollisionAlgorithm.java:86)
	at com.bulletphysics.collision.dispatch.CompoundCollisionAlgorithm.processCollision(CompoundCollisionAlgorithm.java:120)
	at com.bulletphysics.collision.dispatch.DefaultNearCallback.handleCollision(DefaultNearCallback.java:55)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher$CollisionPairCallback.processOverlap(CollisionDispatcher.java:236)
	at com.bulletphysics.collision.broadphase.HashedOverlappingPairCache.processAllOverlappingPairs(HashedOverlappingPairCache.java:190)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher.dispatchAllCollisionPairs(CollisionDispatcher.java:247)
	at com.bulletphysics.collision.dispatch.CollisionWorld.performDiscreteCollisionDetection(CollisionWorld.java:150)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.internalSingleStepSimulation(DiscreteDynamicsWorld.java:378)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.stepSimulation(DiscreteDynamicsWorld.java:339)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:349)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:336)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:132)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:130)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	... 1 more

java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.jme3.bullet.BulletAppState.postRender(BulletAppState.java:255)
	at com.jme3.app.state.AppStateManager.postRender(AppStateManager.java:312)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:262)
	at com.fadorico.rwby.server.RWBY.update(RWBY.java:215)
	at com.jme3.system.NullContext.run(NullContext.java:132)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
	at com.bulletphysics.collision.narrowphase.GjkPairDetector.getClosestPoints(GjkPairDetector.java:140)
	at com.bulletphysics.collision.narrowphase.DiscreteCollisionDetectorInterface.getClosestPoints(DiscreteCollisionDetectorInterface.java:69)
	at com.bulletphysics.collision.dispatch.ConvexConvexAlgorithm.processCollision(ConvexConvexAlgorithm.java:127)
	at com.bulletphysics.collision.dispatch.ConvexTriangleCallback.processTriangle(ConvexTriangleCallback.java:163)
	at com.bulletphysics.collision.shapes.BvhTriangleMeshShape$MyNodeOverlapCallback.processNode(BvhTriangleMeshShape.java:268)
	at com.bulletphysics.collision.shapes.OptimizedBvh.walkStacklessQuantizedTree(OptimizedBvh.java:955)
	at com.bulletphysics.collision.shapes.OptimizedBvh.reportAabbOverlappingNodex(OptimizedBvh.java:703)
	at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.processAllTriangles(BvhTriangleMeshShape.java:163)
	at com.bulletphysics.collision.dispatch.ConvexConcaveCollisionAlgorithm.processCollision(ConvexConcaveCollisionAlgorithm.java:86)
	at com.bulletphysics.collision.dispatch.CompoundCollisionAlgorithm.processCollision(CompoundCollisionAlgorithm.java:120)
	at com.bulletphysics.collision.dispatch.DefaultNearCallback.handleCollision(DefaultNearCallback.java:55)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher$CollisionPairCallback.processOverlap(CollisionDispatcher.java:236)
	at com.bulletphysics.collision.broadphase.HashedOverlappingPairCache.processAllOverlappingPairs(HashedOverlappingPairCache.java:190)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher.dispatchAllCollisionPairs(CollisionDispatcher.java:247)
	at com.bulletphysics.collision.dispatch.CollisionWorld.performDiscreteCollisionDetection(CollisionWorld.java:150)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.internalSingleStepSimulation(DiscreteDynamicsWorld.java:378)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.stepSimulation(DiscreteDynamicsWorld.java:339)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:349)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:336)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:132)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:130)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	... 1 more

java.lang.ClassCastException: com.bulletphysics.collision.shapes.BvhTriangleMeshShape cannot be cast to com.bulletphysics.collision.shapes.ConvexShape
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.jme3.bullet.BulletAppState.postRender(BulletAppState.java:255)
	at com.jme3.app.state.AppStateManager.postRender(AppStateManager.java:312)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:262)
	at com.fadorico.rwby.server.RWBY.update(RWBY.java:215)
	at com.jme3.system.NullContext.run(NullContext.java:132)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: com.bulletphysics.collision.shapes.BvhTriangleMeshShape cannot be cast to com.bulletphysics.collision.shapes.ConvexShape
	at com.bulletphysics.collision.dispatch.ConvexConvexAlgorithm.processCollision(ConvexConvexAlgorithm.java:110)
	at com.bulletphysics.collision.dispatch.ConvexTriangleCallback.processTriangle(ConvexTriangleCallback.java:163)
	at com.bulletphysics.collision.shapes.BvhTriangleMeshShape$MyNodeOverlapCallback.processNode(BvhTriangleMeshShape.java:268)
	at com.bulletphysics.collision.shapes.OptimizedBvh.walkStacklessQuantizedTree(OptimizedBvh.java:955)
	at com.bulletphysics.collision.shapes.OptimizedBvh.reportAabbOverlappingNodex(OptimizedBvh.java:703)
	at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.processAllTriangles(BvhTriangleMeshShape.java:163)
	at com.bulletphysics.collision.dispatch.ConvexConcaveCollisionAlgorithm.processCollision(ConvexConcaveCollisionAlgorithm.java:86)
	at com.bulletphysics.collision.dispatch.CompoundCollisionAlgorithm.processCollision(CompoundCollisionAlgorithm.java:120)
	at com.bulletphysics.collision.dispatch.DefaultNearCallback.handleCollision(DefaultNearCallback.java:55)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher$CollisionPairCallback.processOverlap(CollisionDispatcher.java:236)
	at com.bulletphysics.collision.broadphase.HashedOverlappingPairCache.processAllOverlappingPairs(HashedOverlappingPairCache.java:190)
	at com.bulletphysics.collision.dispatch.CollisionDispatcher.dispatchAllCollisionPairs(CollisionDispatcher.java:247)
	at com.bulletphysics.collision.dispatch.CollisionWorld.performDiscreteCollisionDetection(CollisionWorld.java:150)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.internalSingleStepSimulation(DiscreteDynamicsWorld.java:378)
	at com.bulletphysics.dynamics.DiscreteDynamicsWorld.stepSimulation(DiscreteDynamicsWorld.java:339)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:349)
	at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:336)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:132)
	at com.jme3.bullet.BulletAppState$2.call(BulletAppState.java:130)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	... 1 more

I’ve noticed that these errors seem to pop up quicker the more physics spaces I have running in parallel.

Also this is how I set up my physics spaces:

BulletAppState bulletAppState = new BulletAppState(PhysicsSpace.BroadphaseType.AXIS_SWEEP_3);
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
bulletAppState.getPhysicsSpace().addCollisionGroupListener(CollisionSystem.get(), 2);
bulletAppState.getPhysicsSpace().setMaxSubSteps(16);

So can anyone explain to me what could be happening or even guide me to the potential problem? Cause I don’t have the slightest idea.

Any chance you’re adding some physics objects to two spaces?

I don’t believe so. There’s only a few places in my code that call the add/addAll methods of the physics spaces and looking at them there’s not really any way this could happen. Also just to clarify, I load up my entities on the server and this issue can happen after its done loading things in. For instance, I load up some dummy players, terrain, trees and some enemies here and there. Then I let the server sit there for a few minutes to check how its doing and that’s when it happens. While I let the server sit there, enemies are moving around and I also have an algorithm that enables/disables physics controls depending on an entities’ distance from it. Those are pretty much the only things physics related that are happening during that time.

I’ve noticed that if I have my world divided into lets say 4 physics spaces, the error will eventually pop up. But if I divide my world into more physics spaces, like 16, the error can pop up as early as when I’m loading all the entities in. So the more physics spaces I have, the faster the error will pop up.

EDIT: Using JME 3.1 Alpha release FYI

Are you reusing collision shapes for multiple physics spaces? Its good practice to do that to save memory but I don’t know how multiple physics spaces work with that. Also, what happens if you switch to native bullet? Just to check if similar issues occur.

Oh its nice to know that you can reuse collision shapes :stuck_out_tongue: I didn’t know that. But back to trying it out with native bullet, I assume that replacing jBullet with native bullet would simply be removing the jme3-jbullet from my project and adding jme3-bullet and jme3-bullet-native? If thats the case then doing this gives me a whole bunch of UnsatisfiedLinkError exceptions, such as this one

java.lang.UnsatisfiedLinkError: com.jme3.bullet.collision.shapes.CompoundCollisionShape.createShape()J
	at com.jme3.bullet.collision.shapes.CompoundCollisionShape.createShape(Native Method)
	at com.jme3.bullet.collision.shapes.CompoundCollisionShape.<init>(CompoundCollisionShape.java:58)
	...

I tried clearing the SDK’s cache to see if that would help but it didn’t.

Yeah thats how its done, these errors come from a missing native binary though… Does the dll get extracted to your project folder? You need jme3-bullet.jar and jme3-bullet-natives.jar

I don’t see the DLLs in my project folder, only the usual ones (OpenAL64 and lwjgl64). I tried adding the jars from the JME installation folder manually instead of the libraries but no luck.

EDIT: Ok I have the DLL now (bulletjme.dll), still the same error however. (UnsatisfiedLinkError)

Well adding the libraries should work, just wanted to say which jars that should be. You have the dll? Does that mean it was extracted? I don’t actually know if 3.1 has proper DLLs for bullet but afaik they should be up to date for the java side.

I’m pretty sure it was extracted, cause I don’t remember seeing such a DLL in my folder before, plus the file says it was last modified August 2015, which corresponds to the 3.1 alpha release. Just to be safe I went into the jme3-bullet-native jar and replaced the DLL that was extracted with the one in there, but I still get that same error. So for some reason it still can’t see the DLL? That or like you said it may be because the 3.1 DLL isn’t properly set. I’m not sure.

Do you get the same UnsatisfiedLinkError? Or a different one?

I’m getting mostly 2 from what I can see:

java.lang.UnsatisfiedLinkError: com.jme3.bullet.objects.infos.RigidBodyMotionState.createMotionState()J
	at com.jme3.bullet.objects.infos.RigidBodyMotionState.createMotionState(Native Method)
	at com.jme3.bullet.objects.infos.RigidBodyMotionState.<init>(RigidBodyMotionState.java:58)
	at com.jme3.bullet.objects.PhysicsRigidBody.<init>(PhysicsRigidBody.java:59)
	at com.jme3.bullet.control.RigidBodyControl.<init>(RigidBodyControl.java:68)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
	at java.lang.Class.newInstance(Class.java:442)
	at com.jme3.export.SavableClassUtil.fromName(SavableClassUtil.java:171)
	at com.jme3.export.SavableClassUtil.fromName(SavableClassUtil.java:203)
	at com.jme3.export.binary.BinaryImporter.readObject(BinaryImporter.java:331)
	at com.jme3.export.binary.BinaryInputCapsule.resolveIDs(BinaryInputCapsule.java:483)
	at com.jme3.export.binary.BinaryInputCapsule.readSavableArray(BinaryInputCapsule.java:471)
	at com.jme3.export.binary.BinaryInputCapsule.readSavableArrayList(BinaryInputCapsule.java:587)
	at com.jme3.scene.Spatial.read(Spatial.java:1450)
	at com.jme3.scene.Node.read(Node.java:697)
	at com.jme3.terrain.geomipmap.TerrainQuad.read(TerrainQuad.java:1743)
	at com.jme3.export.binary.BinaryImporter.readObject(BinaryImporter.java:342)
	at com.jme3.export.binary.BinaryInputCapsule.resolveIDs(BinaryInputCapsule.java:483)
	at com.jme3.export.binary.BinaryInputCapsule.readSavableArray(BinaryInputCapsule.java:471)
	at com.jme3.export.binary.BinaryInputCapsule.readSavableArrayList(BinaryInputCapsule.java:587)
	at com.jme3.scene.Node.read(Node.java:688)
	at com.jme3.export.binary.BinaryImporter.readObject(BinaryImporter.java:342)
	at com.jme3.export.binary.BinaryImporter.load(BinaryImporter.java:242)
	at com.jme3.export.binary.BinaryImporter.load(BinaryImporter.java:125)
	at com.jme3.export.binary.BinaryImporter.load(BinaryImporter.java:109)
	at com.jme3.asset.DesktopAssetManager.loadLocatedAsset(DesktopAssetManager.java:262)
	at com.jme3.asset.DesktopAssetManager.loadAsset(DesktopAssetManager.java:376)
	at com.jme3.asset.DesktopAssetManager.loadModel(DesktopAssetManager.java:419)
        ...

java.lang.UnsatisfiedLinkError: com.jme3.bullet.collision.shapes.BoxCollisionShape.createShape(Lcom/jme3/math/Vector3f;)J
	at com.jme3.bullet.collision.shapes.BoxCollisionShape.createShape(Native Method)
	at com.jme3.bullet.collision.shapes.BoxCollisionShape.createShape(BoxCollisionShape.java:82)
	at com.jme3.bullet.collision.shapes.BoxCollisionShape.<init>(BoxCollisionShape.java:60)
        ...

The first happens when it tries to load a chunk of my environment stored in a j3o file, the second happens when I try to create a box collision shape to attach to some of the entities.

Hm, no idea, maybe the wrong version is extracted for some reason? Maybe try extracting the 64bit version manually or something?

I tried both DLL versions but sadly both do the same thing. I know we we’re doing this to test to see if the parallel errors we’re also happening with native bullet, but I’m wondering if we’re deviating from the original error to much? (no offence intended, I appreciate the help)

Could be, still leaves us with a funny error. Maybe make a test case for the issue. If it doesn’t happen there the issue is somewhere in your code.

Bullet uses some acceleration structures that are not threadsafe.

→ Do not share same objects between physicspaces, eg do not recycle meshes and rigidbodies, I found this to be a problem in my case.

Another way would be to write a multi bullet appstate, that executes all physicupdates in sequence instead of parralel. Depending on the circumstances this works quite great.

(I do it this way, as I have one main physic space, and a few smaller ones for vehicle insides, with only the player being dynamic, so they do not need much processing power)

http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=12&t=6775

Also 10 years of waiting for bullet 3.x XD

I tried making a test case but weirdly couldn’t reproduce the issue. So I decided to play around with my code for a bit to see what would happen and I got rid of the error somehow. I found that if I replaced all the tree’s mesh collision shapes (or whatever the default shape is when you create the control from the SDK) with cylinder collision shapes, which removes all mesh collision shapes in my world, the error doesn’t show up anymore. I was able to run my server for a whole hour without any exceptions popping up. So the issue is fixed, but I still have no clue what the root cause was, which sorta worries me cause it could crawl up on me again.

I tried going back to the test case and add the exact assets I was using but that still didn’t reproduce the error.

That’s what I originally thought, but according to Normen its ok to reuse collision shapes.

The reason why I decided to have multiple physics spaces in parallel is because the physics calculations we’re starting to slow down my server pretty severely. My world was originally all in one physics space, so having a few hundred physics objects in the world quickly brought the server to its limits. That being said, I think that running one bullet appstate that updates all physics spaces sequentially would just bring back this problem in my case.

Well I suppose you’re using the same tree collision shape in multiple physics spaces. As said you can reuse them in one physics space but not in multiple physics spaces - or rather I suspected that and Empire basically confirmed it. Afaik especially for mesh shapes bullet generates additional collision info during runtime which might be exactly what is not thread safe. Maybe your partitioning is causing this if you have e.g. overlapping areas with trees in it?

Unless a DesktopAssetManager’s cache makes the trees reuse collision shapes I don’t see how I would be reusing collision shapes in multiple physics spaces. My environment is split up in tiles, which are all stored in seperate j3o files. Currently I don’t have any overlapping areas, so one tile is specific to one physics space. I must be overlooking something, so i’ll be looking over my code again more to see if I might have done something silly without noticing.

Thing with bugs is you don’t see them because you didn’t intend to write them so when you read through your code you see what you intended to do, not what you actually did :chimpanzee_smile:

I don’t know how exactly you created your map, unless you’re using AssetLinkNodes (or linked objects as they’re called in the SDK) the trees should be separate spatials with separate PhysicsControls (which btw you should probably batch to one mesh). But your issues strongly point at some kind of re-use here. To debug you could maybe make some kind of map to check for re-use of objects.

Haha ya you’re right :stuck_out_tongue:

I created my map using a custom plugin in the SDK that lets me edit an infinite world. In the SDK itself it uses AssetLinkNodes for the environment (so that if for example the tree asset was to change, it would change everywhere easily). In my game, I use an “exported” version of that world which as actual Nodes instead of AssetLinkNodes.

When you say I should batch my PhysicsControls, you’re talking about batching the collision shapes and then creating a new PhysicsControl and assigning that shape to it? Cause if thats the case, I thought having one big collision shape was worse then having multiple physics shapes.