Physics Nodes

Like many other users of JME, I am making a Minecraft clone. However, I’ve run into a problem when loading/deleting blocks (necessary to have a “infinite” world). I’m trying to do this is a separate thread as it takes a couple seconds for it to completely finish, but threads cannot touch the spatial and physics objects, which is virtually where all CPU time is spent. As a result, I was forced to enqueue almost all the thread, which causes the problem of my game freezing every time i run the thread, as it has to wait for my deleting and loading to finish before the game can continue on. I’ve decided that the next solution would be to simply use nodes, as removing them would be quick and I can do all the heavy tasks without enqueuing. However, I’ve realized I can’t do this with physics… or can I? That’s my question.
P.S. I’m also open to other ideas that would perform the same task

TLDR; Can I put RigidBodyControl in Nodes, or something similar so I can add/remove them from physics quickly?

You can construct the mesh in a separate thread so long as its not connected to the scene graph - then once it is fully constructed attach it at that point.

@zarch said: You can construct the mesh in a separate thread so long as its not connected to the scene graph - then once it is fully constructed attach it at that point.

But if i put all the rigid bodies in a single mesh, I can’t remove part of it (a single cube, out of the huge chunk). If you meant make the meshes of each individual cube, that’s what I am currently doing, and attaching/detaching them from the physics space is what is causing the freeze.

@Kuru said: But if i put all the rigid bodies in a single mesh, I can't remove part of it (a single cube, out of the huge chunk). If you meant make the meshes of each individual cube, that's what I am currently doing, and attaching/detaching them from the physics space is what is causing the freeze.

You can’t remove part of it but you could generate a new one and replace the old one.

@pspeed said: You can't remove part of it but you could generate a new one and replace the old one.

That’s in interesting idea… my only concern with that would be that there may be a pause/freeze in the game every time i add/remove something. Any clue on the performance impact of doing this?

Swapping out an already generated object does not have a large performance consequence.

@Kuru said: That's in interesting idea... my only concern with that would be that there may be a pause/freeze in the game every time i add/remove something. Any clue on the performance impact of doing this?

That’s why you build it on another thread.

Attaching one new object shouldn’t impact frame rate much. If you have lots of objects to add then you will want to spread them out over multiple frames.

@pspeed said: That's why you build it on another thread.

Attaching one new object shouldn’t impact frame rate much. If you have lots of objects to add then you will want to spread them out over multiple frames.

@zarch said: Swapping out an already generated object does not have a large performance consequence.

So I’ve tried doing it and encountered a problem. I have a freeze every time i try to place/remove a block (which causes it to build a new mesh every time). The reason why I cannot build it in a thread is because I need the physics to be updated instantaneously, or it would cause some very serious bugs that would rend my game unplayable. Also, I could image a few problems with it if i place a block right after placing another. (Remember: I’m trying to make a Minecraft clone)

@Kuru said: So I've tried doing it and encountered a problem. I have a freeze every time i try to place/remove a block (which causes it to build a new mesh every time). The reason why I cannot build it in a thread is because I need the physics to be updated instantaneously, or it would cause some very serious bugs that would rend my game unplayable. Also, I could image a few problems with it if i place a block right after placing another. (Remember: I'm trying to make a Minecraft clone)

Yes, I don’t know anything about that at all. :wink: (http://mythruna.com)

I don’t understand why you can’t built it on another thread.

Player clicks block. This kicks off another thread to rebuild the meshes, physics or otherwise. Thread is done and sends the new meshes back to the render thread where the old ones are swapped out for new instantly.

…where is the issue? I don’t see how there would be serious bugs with physics just because you delayed the swap a little.

@pspeed said: Yes, I don't know anything about that at all. ;) (http://mythruna.com)

I don’t understand why you can’t built it on another thread.

Player clicks block. This kicks off another thread to rebuild the meshes, physics or otherwise. Thread is done and sends the new meshes back to the render thread where the old ones are swapped out for new instantly.

…where is the issue? I don’t see how there would be serious bugs with physics just because you delayed the swap a little.

Alright, it works. I’ve found work-arounds for the bugs that were fatal (ex. at one point i had over 9 seperate threads). There remain few minor bugs (such as trying to place a block right under the player in a specific position, and the physics thread causing you to allow the player to go through the soon-to-be block), but none that renders the game unplayable. Anyways, the bugs would only appear if the player tries to exploit the bug in unusual situations.

TLDR: Thanks!

Yeah, 9 threads does sound like a problem. :slight_smile:

Personally, I would use a single thread pool executor and submit tasks. I’d also do the visual and physics meshes at the same time so there is no discrepancy.

Player clicks block, sometime later world is updated in one frame, both visuals and physics. No bugs.

@pspeed said: Yes, I don't know anything about that at all. ;) (http://mythruna.com) .

You should update your front page i think, the first thing I saw was “Posted on January 25, 2012”. If i was a new user I would assume either: that this project had been abandoned, or not updated very often

@wezrule said: You should update your front page i think, the first thing I saw was "Posted on January 25, 2012". If i was a new user I would assume either: that this project had been abandoned, or not updated very often

That’s sort of a theme problem since that main page isn’t meant to change. You can see to the right that the twitter feed has been active recently.

He’s right though, the main thing you see when you land on that page is that date - it is sort of staring at you.

Just making the date less prominent would probably make a big difference.

(For example make the date the same colour as the rest of the text and put the Welcome To bit in the cyan type colour).

At the moment the different colour from the block of text makes it jump out at you.

P.S. Depending on zoom/screen res/etc the twitter feed isn’t actually visible at all until you scroll the page down.

To be honest, i must agree to him, the date jumps kinda at, you, I would modify the theme accordingly.

Yes, modify the wordpress theme. I’ll wait until I have 12-14 hours free. :wink:

So after a couple days of finding and fixing bugs due to this change, there was one bug that I couldn’t fix. I’ve traced the cause of the problems to be the following code:

private Callable createControl = new Callable() { //Callable task that will create a new physics mesh and return it
    public Object call() throws Exception {
        return CollisionShapeFactory.createMeshShape((Node) Main.app.enqueue(getNode).get());
    }
};

private Callable getNode = new Callable() { //Called to safely get the chunk's node from another thread
    public Object call() throws Exception {
        return node;
    }
};

The reason why it’s especially difficult to debug this is because it is caused by every kind of exception. Such as CastClassException, NullPointerException, IndexOutOfBoundsException, ect.

Here’s some example stack traces that i got:

java.util.concurrent.ExecutionException: java.lang.IndexOutOfBoundsException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at game.Chunk.updateCollisionShape(Chunk.java:154)
at game.Main.simpleUpdate(Main.java:181)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:241)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IndexOutOfBoundsException
at com.bulletphysics.util.ObjectArrayList.remove(ObjectArrayList.java:78)
at com.bulletphysics.util.ObjectPool.get(ObjectPool.java:62)
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.processAllTriangles(BvhTriangleMeshShape.java:160)
at com.bulletphysics.collision.shapes.TriangleMeshShape.localGetSupportingVertex(TriangleMeshShape.java:71)
at com.bulletphysics.collision.shapes.TriangleMeshShape.recalcLocalAabb(TriangleMeshShape.java:88)
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.(BvhTriangleMeshShape.java:84)
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.(BvhTriangleMeshShape.java:63)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.createShape(MeshCollisionShape.java:122)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.createCollisionMesh(MeshCollisionShape.java:77)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.(MeshCollisionShape.java:65)
at com.jme3.bullet.util.CollisionShapeFactory.createSingleMeshShape(CollisionShapeFactory.java:214)
at com.jme3.bullet.util.CollisionShapeFactory.createCompoundShape(CollisionShapeFactory.java:112)
at com.jme3.bullet.util.CollisionShapeFactory.createCompoundShape(CollisionShapeFactory.java:134)
at com.jme3.bullet.util.CollisionShapeFactory.createMeshCompoundShape(CollisionShapeFactory.java:143)
at com.jme3.bullet.util.CollisionShapeFactory.createMeshShape(CollisionShapeFactory.java:173)
at game.Chunk$2.call(Chunk.java:37)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
… 1 more

and this

java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at game.Chunk.updateCollisionShape(Chunk.java:154)
at game.Main.simpleUpdate(Main.java:181)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:241)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.NullPointerException
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.processAllTriangles(BvhTriangleMeshShape.java:161)
at com.bulletphysics.collision.shapes.TriangleMeshShape.localGetSupportingVertex(TriangleMeshShape.java:71)
at com.bulletphysics.collision.shapes.TriangleMeshShape.recalcLocalAabb(TriangleMeshShape.java:91)
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.(BvhTriangleMeshShape.java:84)
at com.bulletphysics.collision.shapes.BvhTriangleMeshShape.(BvhTriangleMeshShape.java:63)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.createShape(MeshCollisionShape.java:122)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.createCollisionMesh(MeshCollisionShape.java:77)
at com.jme3.bullet.collision.shapes.MeshCollisionShape.(MeshCollisionShape.java:65)
at com.jme3.bullet.util.CollisionShapeFactory.createSingleMeshShape(CollisionShapeFactory.java:214)
at com.jme3.bullet.util.CollisionShapeFactory.createCompoundShape(CollisionShapeFactory.java:112)
at com.jme3.bullet.util.CollisionShapeFactory.createCompoundShape(CollisionShapeFactory.java:134)
at com.jme3.bullet.util.CollisionShapeFactory.createMeshCompoundShape(CollisionShapeFactory.java:143)
at com.jme3.bullet.util.CollisionShapeFactory.createMeshShape(CollisionShapeFactory.java:173)
at game.Chunk$2.call(Chunk.java:37)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
… 1 more

and sometimes just this

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:324)
at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:311)
at com.jme3.bullet.BulletAppState.render(BulletAppState.java:185)
at com.jme3.app.state.AppStateManager.render(AppStateManager.java:268)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:250)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:722)