• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
BulletAppState()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      */
BulletAppState(BroadphaseType broadphaseType)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      */
BulletAppState(Vector3f worldMin, Vector3f worldMax)60     public BulletAppState(Vector3f worldMin, Vector3f worldMax) {
61         this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
62     }
63 
BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType)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 
startPhysicsOnExecutor()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 
getPhysicsSpace()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      */
startPhysics()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 
initialize(AppStateManager stateManager, Application app)137     public void initialize(AppStateManager stateManager, Application app) {
138         if (!initialized) {
139             startPhysics();
140         }
141         initialized = true;
142     }
143 
isInitialized()144     public boolean isInitialized() {
145         return initialized;
146     }
147 
setEnabled(boolean enabled)148     public void setEnabled(boolean enabled) {
149         this.active = enabled;
150     }
151 
isEnabled()152     public boolean isEnabled() {
153         return active;
154     }
155 
stateAttached(AppStateManager stateManager)156     public void stateAttached(AppStateManager stateManager) {
157         if (!initialized) {
158             startPhysics();
159         }
160         if (threadingType == ThreadingType.PARALLEL) {
161             PhysicsSpace.setLocalThreadPhysicsSpace(pSpace);
162         }
163     }
164 
stateDetached(AppStateManager stateManager)165     public void stateDetached(AppStateManager stateManager) {
166     }
167 
update(float tpf)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 
render(RenderManager rm)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 
postRender()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 
cleanup()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      */
getThreadingType()215     public ThreadingType getThreadingType() {
216         return threadingType;
217     }
218 
219     /**
220      * Use before attaching state
221      * @param threadingType the threadingType to set
222      */
setThreadingType(ThreadingType threadingType)223     public void setThreadingType(ThreadingType threadingType) {
224         this.threadingType = threadingType;
225     }
226 
227     /**
228      * Use before attaching state
229      */
setBroadphaseType(BroadphaseType broadphaseType)230     public void setBroadphaseType(BroadphaseType broadphaseType) {
231         this.broadphaseType = broadphaseType;
232     }
233 
234     /**
235      * Use before attaching state
236      */
setWorldMin(Vector3f worldMin)237     public void setWorldMin(Vector3f worldMin) {
238         this.worldMin = worldMin;
239     }
240 
241     /**
242      * Use before attaching state
243      */
setWorldMax(Vector3f worldMax)244     public void setWorldMax(Vector3f worldMax) {
245         this.worldMax = worldMax;
246     }
247 
getSpeed()248     public float getSpeed() {
249         return speed;
250     }
251 
setSpeed(float speed)252     public void setSpeed(float speed) {
253         this.speed = speed;
254     }
255 
prePhysicsTick(PhysicsSpace space, float f)256     public void prePhysicsTick(PhysicsSpace space, float f) {
257     }
258 
physicsTick(PhysicsSpace space, float f)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