Home | History | Annotate | Download | only in bullet
      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