| /* |
| * To change this template, choose Tools | Templates |
| * and open the template in the editor. |
| */ |
| package com.jme3.bullet; |
| |
| import com.jme3.app.Application; |
| import com.jme3.app.state.AppState; |
| import com.jme3.app.state.AppStateManager; |
| import com.jme3.bullet.PhysicsSpace.BroadphaseType; |
| import com.jme3.math.Vector3f; |
| import com.jme3.renderer.RenderManager; |
| import java.util.concurrent.*; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * <code>BulletAppState</code> allows using bullet physics in an Application. |
| * @author normenhansen |
| */ |
| public class BulletAppState implements AppState, PhysicsTickListener { |
| |
| protected boolean initialized = false; |
| protected Application app; |
| protected AppStateManager stateManager; |
| protected ScheduledThreadPoolExecutor executor; |
| protected PhysicsSpace pSpace; |
| protected ThreadingType threadingType = ThreadingType.SEQUENTIAL; |
| protected BroadphaseType broadphaseType = BroadphaseType.DBVT; |
| protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); |
| protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); |
| private float speed = 1; |
| protected boolean active = true; |
| protected float tpf; |
| protected Future physicsFuture; |
| |
| /** |
| * Creates a new BulletAppState running a PhysicsSpace for physics simulation, |
| * use getStateManager().addState(bulletAppState) to enable physics for an Application. |
| */ |
| public BulletAppState() { |
| } |
| |
| /** |
| * Creates a new BulletAppState running a PhysicsSpace for physics simulation, |
| * use getStateManager().addState(bulletAppState) to enable physics for an Application. |
| * @param broadphaseType The type of broadphase collision detection, BroadphaseType.DVBT is the default |
| */ |
| public BulletAppState(BroadphaseType broadphaseType) { |
| this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); |
| } |
| |
| /** |
| * Creates a new BulletAppState running a PhysicsSpace for physics simulation, |
| * use getStateManager().addState(bulletAppState) to enable physics for an Application. |
| * An AxisSweep broadphase is used. |
| * @param worldMin The minimum world extent |
| * @param worldMax The maximum world extent |
| */ |
| public BulletAppState(Vector3f worldMin, Vector3f worldMax) { |
| this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); |
| } |
| |
| public BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { |
| this.worldMin.set(worldMin); |
| this.worldMax.set(worldMax); |
| this.broadphaseType = broadphaseType; |
| } |
| |
| private boolean startPhysicsOnExecutor() { |
| if (executor != null) { |
| executor.shutdown(); |
| } |
| executor = new ScheduledThreadPoolExecutor(1); |
| final BulletAppState app = this; |
| Callable<Boolean> call = new Callable<Boolean>() { |
| |
| public Boolean call() throws Exception { |
| detachedPhysicsLastUpdate = System.currentTimeMillis(); |
| pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType); |
| pSpace.addTickListener(app); |
| return true; |
| } |
| }; |
| try { |
| return executor.submit(call).get(); |
| } catch (InterruptedException ex) { |
| Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); |
| return false; |
| } catch (ExecutionException ex) { |
| Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); |
| return false; |
| } |
| } |
| private Callable<Boolean> parallelPhysicsUpdate = new Callable<Boolean>() { |
| |
| public Boolean call() throws Exception { |
| pSpace.update(tpf * getSpeed()); |
| return true; |
| } |
| }; |
| long detachedPhysicsLastUpdate = 0; |
| private Callable<Boolean> detachedPhysicsUpdate = new Callable<Boolean>() { |
| |
| public Boolean call() throws Exception { |
| pSpace.update(getPhysicsSpace().getAccuracy() * getSpeed()); |
| pSpace.distributeEvents(); |
| long update = System.currentTimeMillis() - detachedPhysicsLastUpdate; |
| detachedPhysicsLastUpdate = System.currentTimeMillis(); |
| executor.schedule(detachedPhysicsUpdate, Math.round(getPhysicsSpace().getAccuracy() * 1000000.0f) - (update * 1000), TimeUnit.MICROSECONDS); |
| return true; |
| } |
| }; |
| |
| public PhysicsSpace getPhysicsSpace() { |
| return pSpace; |
| } |
| |
| /** |
| * The physics system is started automatically on attaching, if you want to start it |
| * before for some reason, you can use this method. |
| */ |
| public void startPhysics() { |
| //start physics thread(pool) |
| if (threadingType == ThreadingType.PARALLEL) { |
| startPhysicsOnExecutor(); |
| // } else if (threadingType == ThreadingType.DETACHED) { |
| // startPhysicsOnExecutor(); |
| // executor.submit(detachedPhysicsUpdate); |
| } else { |
| pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType); |
| } |
| pSpace.addTickListener(this); |
| initialized = true; |
| } |
| |
| public void initialize(AppStateManager stateManager, Application app) { |
| if (!initialized) { |
| startPhysics(); |
| } |
| initialized = true; |
| } |
| |
| public boolean isInitialized() { |
| return initialized; |
| } |
| |
| public void setEnabled(boolean enabled) { |
| this.active = enabled; |
| } |
| |
| public boolean isEnabled() { |
| return active; |
| } |
| |
| public void stateAttached(AppStateManager stateManager) { |
| if (!initialized) { |
| startPhysics(); |
| } |
| if (threadingType == ThreadingType.PARALLEL) { |
| PhysicsSpace.setLocalThreadPhysicsSpace(pSpace); |
| } |
| } |
| |
| public void stateDetached(AppStateManager stateManager) { |
| } |
| |
| public void update(float tpf) { |
| if (!active) { |
| return; |
| } |
| // if (threadingType != ThreadingType.DETACHED) { |
| pSpace.distributeEvents(); |
| // } |
| this.tpf = tpf; |
| } |
| |
| public void render(RenderManager rm) { |
| if (!active) { |
| return; |
| } |
| if (threadingType == ThreadingType.PARALLEL) { |
| physicsFuture = executor.submit(parallelPhysicsUpdate); |
| } else if (threadingType == ThreadingType.SEQUENTIAL) { |
| pSpace.update(active ? tpf * speed : 0); |
| } else { |
| } |
| } |
| |
| public void postRender() { |
| if (physicsFuture != null) { |
| try { |
| physicsFuture.get(); |
| physicsFuture = null; |
| } catch (InterruptedException ex) { |
| Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); |
| } catch (ExecutionException ex) { |
| Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); |
| } |
| } |
| } |
| |
| public void cleanup() { |
| if (executor != null) { |
| executor.shutdown(); |
| executor = null; |
| } |
| pSpace.removeTickListener(this); |
| pSpace.destroy(); |
| } |
| |
| /** |
| * @return the threadingType |
| */ |
| public ThreadingType getThreadingType() { |
| return threadingType; |
| } |
| |
| /** |
| * Use before attaching state |
| * @param threadingType the threadingType to set |
| */ |
| public void setThreadingType(ThreadingType threadingType) { |
| this.threadingType = threadingType; |
| } |
| |
| /** |
| * Use before attaching state |
| */ |
| public void setBroadphaseType(BroadphaseType broadphaseType) { |
| this.broadphaseType = broadphaseType; |
| } |
| |
| /** |
| * Use before attaching state |
| */ |
| public void setWorldMin(Vector3f worldMin) { |
| this.worldMin = worldMin; |
| } |
| |
| /** |
| * Use before attaching state |
| */ |
| public void setWorldMax(Vector3f worldMax) { |
| this.worldMax = worldMax; |
| } |
| |
| public float getSpeed() { |
| return speed; |
| } |
| |
| public void setSpeed(float speed) { |
| this.speed = speed; |
| } |
| |
| public void prePhysicsTick(PhysicsSpace space, float f) { |
| } |
| |
| public void physicsTick(PhysicsSpace space, float f) { |
| } |
| |
| public enum ThreadingType { |
| |
| /** |
| * Default mode; user update, physics update and rendering happen sequentially (single threaded) |
| */ |
| SEQUENTIAL, |
| /** |
| * Parallel threaded mode; physics update and rendering are executed in parallel, update order is kept.<br/> |
| * Multiple BulletAppStates will execute in parallel in this mode. |
| */ |
| PARALLEL, |
| } |
| } |