1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the editor. 4 */ 5 package com.jme3.bullet; 6 7 import com.jme3.app.Application; 8 import com.jme3.app.state.AppState; 9 import com.jme3.app.state.AppStateManager; 10 import com.jme3.bullet.PhysicsSpace.BroadphaseType; 11 import com.jme3.math.Vector3f; 12 import com.jme3.renderer.RenderManager; 13 import java.util.concurrent.*; 14 import java.util.logging.Level; 15 import java.util.logging.Logger; 16 17 /** 18 * <code>BulletAppState</code> allows using bullet physics in an Application. 19 * @author normenhansen 20 */ 21 public class BulletAppState implements AppState, PhysicsTickListener { 22 23 protected boolean initialized = false; 24 protected Application app; 25 protected AppStateManager stateManager; 26 protected ScheduledThreadPoolExecutor executor; 27 protected PhysicsSpace pSpace; 28 protected ThreadingType threadingType = ThreadingType.SEQUENTIAL; 29 protected BroadphaseType broadphaseType = BroadphaseType.DBVT; 30 protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); 31 protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); 32 private float speed = 1; 33 protected boolean active = true; 34 protected float tpf; 35 protected Future physicsFuture; 36 37 /** 38 * Creates a new BulletAppState running a PhysicsSpace for physics simulation, 39 * use getStateManager().addState(bulletAppState) to enable physics for an Application. 40 */ 41 public BulletAppState() { 42 } 43 44 /** 45 * Creates a new BulletAppState running a PhysicsSpace for physics simulation, 46 * use getStateManager().addState(bulletAppState) to enable physics for an Application. 47 * @param broadphaseType The type of broadphase collision detection, BroadphaseType.DVBT is the default 48 */ 49 public BulletAppState(BroadphaseType broadphaseType) { 50 this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); 51 } 52 53 /** 54 * Creates a new BulletAppState running a PhysicsSpace for physics simulation, 55 * use getStateManager().addState(bulletAppState) to enable physics for an Application. 56 * An AxisSweep broadphase is used. 57 * @param worldMin The minimum world extent 58 * @param worldMax The maximum world extent 59 */ 60 public BulletAppState(Vector3f worldMin, Vector3f worldMax) { 61 this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); 62 } 63 64 public BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { 65 this.worldMin.set(worldMin); 66 this.worldMax.set(worldMax); 67 this.broadphaseType = broadphaseType; 68 } 69 70 private boolean startPhysicsOnExecutor() { 71 if (executor != null) { 72 executor.shutdown(); 73 } 74 executor = new ScheduledThreadPoolExecutor(1); 75 final BulletAppState app = this; 76 Callable<Boolean> call = new Callable<Boolean>() { 77 78 public Boolean call() throws Exception { 79 detachedPhysicsLastUpdate = System.currentTimeMillis(); 80 pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType); 81 pSpace.addTickListener(app); 82 return true; 83 } 84 }; 85 try { 86 return executor.submit(call).get(); 87 } catch (InterruptedException ex) { 88 Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); 89 return false; 90 } catch (ExecutionException ex) { 91 Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); 92 return false; 93 } 94 } 95 private Callable<Boolean> parallelPhysicsUpdate = new Callable<Boolean>() { 96 97 public Boolean call() throws Exception { 98 pSpace.update(tpf * getSpeed()); 99 return true; 100 } 101 }; 102 long detachedPhysicsLastUpdate = 0; 103 private Callable<Boolean> detachedPhysicsUpdate = new Callable<Boolean>() { 104 105 public Boolean call() throws Exception { 106 pSpace.update(getPhysicsSpace().getAccuracy() * getSpeed()); 107 pSpace.distributeEvents(); 108 long update = System.currentTimeMillis() - detachedPhysicsLastUpdate; 109 detachedPhysicsLastUpdate = System.currentTimeMillis(); 110 executor.schedule(detachedPhysicsUpdate, Math.round(getPhysicsSpace().getAccuracy() * 1000000.0f) - (update * 1000), TimeUnit.MICROSECONDS); 111 return true; 112 } 113 }; 114 115 public PhysicsSpace getPhysicsSpace() { 116 return pSpace; 117 } 118 119 /** 120 * The physics system is started automatically on attaching, if you want to start it 121 * before for some reason, you can use this method. 122 */ 123 public void startPhysics() { 124 //start physics thread(pool) 125 if (threadingType == ThreadingType.PARALLEL) { 126 startPhysicsOnExecutor(); 127 // } else if (threadingType == ThreadingType.DETACHED) { 128 // startPhysicsOnExecutor(); 129 // executor.submit(detachedPhysicsUpdate); 130 } else { 131 pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType); 132 } 133 pSpace.addTickListener(this); 134 initialized = true; 135 } 136 137 public void initialize(AppStateManager stateManager, Application app) { 138 if (!initialized) { 139 startPhysics(); 140 } 141 initialized = true; 142 } 143 144 public boolean isInitialized() { 145 return initialized; 146 } 147 148 public void setEnabled(boolean enabled) { 149 this.active = enabled; 150 } 151 152 public boolean isEnabled() { 153 return active; 154 } 155 156 public void stateAttached(AppStateManager stateManager) { 157 if (!initialized) { 158 startPhysics(); 159 } 160 if (threadingType == ThreadingType.PARALLEL) { 161 PhysicsSpace.setLocalThreadPhysicsSpace(pSpace); 162 } 163 } 164 165 public void stateDetached(AppStateManager stateManager) { 166 } 167 168 public void update(float tpf) { 169 if (!active) { 170 return; 171 } 172 // if (threadingType != ThreadingType.DETACHED) { 173 pSpace.distributeEvents(); 174 // } 175 this.tpf = tpf; 176 } 177 178 public void render(RenderManager rm) { 179 if (!active) { 180 return; 181 } 182 if (threadingType == ThreadingType.PARALLEL) { 183 physicsFuture = executor.submit(parallelPhysicsUpdate); 184 } else if (threadingType == ThreadingType.SEQUENTIAL) { 185 pSpace.update(active ? tpf * speed : 0); 186 } else { 187 } 188 } 189 190 public void postRender() { 191 if (physicsFuture != null) { 192 try { 193 physicsFuture.get(); 194 physicsFuture = null; 195 } catch (InterruptedException ex) { 196 Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); 197 } catch (ExecutionException ex) { 198 Logger.getLogger(BulletAppState.class.getName()).log(Level.SEVERE, null, ex); 199 } 200 } 201 } 202 203 public void cleanup() { 204 if (executor != null) { 205 executor.shutdown(); 206 executor = null; 207 } 208 pSpace.removeTickListener(this); 209 pSpace.destroy(); 210 } 211 212 /** 213 * @return the threadingType 214 */ 215 public ThreadingType getThreadingType() { 216 return threadingType; 217 } 218 219 /** 220 * Use before attaching state 221 * @param threadingType the threadingType to set 222 */ 223 public void setThreadingType(ThreadingType threadingType) { 224 this.threadingType = threadingType; 225 } 226 227 /** 228 * Use before attaching state 229 */ 230 public void setBroadphaseType(BroadphaseType broadphaseType) { 231 this.broadphaseType = broadphaseType; 232 } 233 234 /** 235 * Use before attaching state 236 */ 237 public void setWorldMin(Vector3f worldMin) { 238 this.worldMin = worldMin; 239 } 240 241 /** 242 * Use before attaching state 243 */ 244 public void setWorldMax(Vector3f worldMax) { 245 this.worldMax = worldMax; 246 } 247 248 public float getSpeed() { 249 return speed; 250 } 251 252 public void setSpeed(float speed) { 253 this.speed = speed; 254 } 255 256 public void prePhysicsTick(PhysicsSpace space, float f) { 257 } 258 259 public void physicsTick(PhysicsSpace space, float f) { 260 } 261 262 public enum ThreadingType { 263 264 /** 265 * Default mode; user update, physics update and rendering happen sequentially (single threaded) 266 */ 267 SEQUENTIAL, 268 /** 269 * Parallel threaded mode; physics update and rendering are executed in parallel, update order is kept.<br/> 270 * Multiple BulletAppStates will execute in parallel in this mode. 271 */ 272 PARALLEL, 273 } 274 } 275