Home | History | Annotate | Download | only in cinematic
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.cinematic;
     33 
     34 import com.jme3.animation.LoopMode;
     35 import com.jme3.app.Application;
     36 import com.jme3.app.state.AppState;
     37 import com.jme3.app.state.AppStateManager;
     38 import com.jme3.asset.TextureKey;
     39 import com.jme3.cinematic.events.AbstractCinematicEvent;
     40 import com.jme3.cinematic.events.CinematicEvent;
     41 import com.jme3.cinematic.events.CinematicEventListener;
     42 import com.jme3.export.*;
     43 import com.jme3.renderer.Camera;
     44 import com.jme3.renderer.RenderManager;
     45 import com.jme3.scene.CameraNode;
     46 import com.jme3.scene.Node;
     47 import com.jme3.scene.control.CameraControl;
     48 import com.jme3.scene.control.CameraControl.ControlDirection;
     49 import java.io.IOException;
     50 import java.util.ArrayList;
     51 import java.util.HashMap;
     52 import java.util.List;
     53 import java.util.Map;
     54 import java.util.logging.Level;
     55 import java.util.logging.Logger;
     56 
     57 /**
     58  *
     59  * @author Nehon
     60  */
     61 public class Cinematic extends AbstractCinematicEvent implements AppState {
     62 
     63     private static final Logger logger = Logger.getLogger(Application.class.getName());
     64     private Node scene;
     65     protected TimeLine timeLine = new TimeLine();
     66     private int lastFetchedKeyFrame = -1;
     67     private List<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>();
     68     private Map<String, CameraNode> cameras = new HashMap<String, CameraNode>();
     69     private CameraNode currentCam;
     70     private boolean initialized = false;
     71     private Map<String, Map<String, Object>> eventsData;
     72 
     73     public Cinematic() {
     74     }
     75 
     76     public Cinematic(Node scene) {
     77         this.scene = scene;
     78     }
     79 
     80     public Cinematic(Node scene, float initialDuration) {
     81         super(initialDuration);
     82         this.scene = scene;
     83     }
     84 
     85     public Cinematic(Node scene, LoopMode loopMode) {
     86         super(loopMode);
     87         this.scene = scene;
     88     }
     89 
     90     public Cinematic(Node scene, float initialDuration, LoopMode loopMode) {
     91         super(initialDuration, loopMode);
     92         this.scene = scene;
     93     }
     94 
     95     @Override
     96     public void onPlay() {
     97         if (isInitialized()) {
     98             if (playState == PlayState.Paused) {
     99                 for (int i = 0; i < cinematicEvents.size(); i++) {
    100                     CinematicEvent ce = cinematicEvents.get(i);
    101                     if (ce.getPlayState() == PlayState.Paused) {
    102                         ce.play();
    103                     }
    104                 }
    105             }
    106         }
    107     }
    108 
    109     @Override
    110     public void onStop() {
    111         time = 0;
    112         lastFetchedKeyFrame = -1;
    113         for (int i = 0; i < cinematicEvents.size(); i++) {
    114             CinematicEvent ce = cinematicEvents.get(i);
    115             ce.stop();
    116         }
    117         enableCurrentCam(false);
    118     }
    119 
    120     @Override
    121     public void onPause() {
    122         for (int i = 0; i < cinematicEvents.size(); i++) {
    123             CinematicEvent ce = cinematicEvents.get(i);
    124             if (ce.getPlayState() == PlayState.Playing) {
    125                 ce.pause();
    126             }
    127         }
    128     }
    129 
    130     @Override
    131     public void write(JmeExporter ex) throws IOException {
    132         super.write(ex);
    133         OutputCapsule oc = ex.getCapsule(this);
    134 
    135         oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
    136         oc.writeStringSavableMap(cameras, "cameras", null);
    137         oc.write(timeLine, "timeLine", null);
    138 
    139     }
    140 
    141     @Override
    142     public void read(JmeImporter im) throws IOException {
    143         super.read(im);
    144         InputCapsule ic = im.getCapsule(this);
    145 
    146         cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
    147         cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
    148         timeLine = (TimeLine) ic.readSavable("timeLine", null);
    149     }
    150 
    151     @Override
    152     public void setSpeed(float speed) {
    153         super.setSpeed(speed);
    154         for (int i = 0; i < cinematicEvents.size(); i++) {
    155             CinematicEvent ce = cinematicEvents.get(i);
    156             ce.setSpeed(speed);
    157         }
    158 
    159 
    160     }
    161 
    162     public void initialize(AppStateManager stateManager, Application app) {
    163         initEvent(app, this);
    164         for (CinematicEvent cinematicEvent : cinematicEvents) {
    165             cinematicEvent.initEvent(app, this);
    166         }
    167 
    168         initialized = true;
    169     }
    170 
    171     public boolean isInitialized() {
    172         return initialized;
    173     }
    174 
    175     public void setEnabled(boolean enabled) {
    176         if (enabled) {
    177             play();
    178         }
    179     }
    180 
    181     public boolean isEnabled() {
    182         return playState == PlayState.Playing;
    183     }
    184 
    185     public void stateAttached(AppStateManager stateManager) {
    186     }
    187 
    188     public void stateDetached(AppStateManager stateManager) {
    189         stop();
    190     }
    191 
    192     public void update(float tpf) {
    193         if (isInitialized()) {
    194             internalUpdate(tpf);
    195         }
    196     }
    197 
    198     @Override
    199     public void onUpdate(float tpf) {
    200         for (int i = 0; i < cinematicEvents.size(); i++) {
    201             CinematicEvent ce = cinematicEvents.get(i);
    202             ce.internalUpdate(tpf);
    203         }
    204 
    205         int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
    206 
    207         //iterate to make sure every key frame is triggered
    208         for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) {
    209             KeyFrame keyFrame = timeLine.get(i);
    210             if (keyFrame != null) {
    211                 keyFrame.trigger();
    212             }
    213         }
    214 
    215         lastFetchedKeyFrame = keyFrameIndex;
    216     }
    217 
    218     @Override
    219     public void setTime(float time) {
    220         super.setTime(time);
    221         int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
    222 
    223         //triggering all the event from start to "time"
    224         //then computing timeOffset for each event
    225         for (int i = 0; i <= keyFrameIndex; i++) {
    226             KeyFrame keyFrame = timeLine.get(i);
    227             if (keyFrame != null) {
    228                 for (CinematicEvent ce : keyFrame.getCinematicEvents()) {
    229                     ce.play();
    230                     ce.setTime(time - timeLine.getKeyFrameTime(keyFrame));
    231                 }
    232             }
    233         }
    234         if (playState != PlayState.Playing) {
    235             pause();
    236         }
    237 
    238         //  step();
    239     }
    240 
    241     public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
    242         KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
    243         if (keyFrame == null) {
    244             keyFrame = new KeyFrame();
    245             timeLine.addKeyFrameAtTime(timeStamp, keyFrame);
    246         }
    247         keyFrame.cinematicEvents.add(cinematicEvent);
    248         cinematicEvents.add(cinematicEvent);
    249         return keyFrame;
    250     }
    251 
    252     public void render(RenderManager rm) {
    253     }
    254 
    255     public void postRender() {
    256     }
    257 
    258     public void cleanup() {
    259     }
    260 
    261     /**
    262      * fits the duration of the cinamatic to the duration of all its child cinematic events
    263      */
    264     public void fitDuration() {
    265         KeyFrame kf = timeLine.getKeyFrameAtTime(timeLine.getLastKeyFrameIndex());
    266         float d = 0;
    267         for (int i = 0; i < kf.getCinematicEvents().size(); i++) {
    268             CinematicEvent ce = kf.getCinematicEvents().get(i);
    269             if (d < (ce.getDuration() * ce.getSpeed())) {
    270                 d = (ce.getDuration() * ce.getSpeed());
    271             }
    272         }
    273 
    274         initialDuration = d;
    275     }
    276 
    277     public CameraNode bindCamera(String cameraName, Camera cam) {
    278         CameraNode node = new CameraNode(cameraName, cam);
    279         node.setControlDir(ControlDirection.SpatialToCamera);
    280         node.getControl(CameraControl.class).setEnabled(false);
    281         cameras.put(cameraName, node);
    282         scene.attachChild(node);
    283         return node;
    284     }
    285 
    286     public CameraNode getCamera(String cameraName) {
    287         return cameras.get(cameraName);
    288     }
    289 
    290     private void enableCurrentCam(boolean enabled) {
    291         if (currentCam != null) {
    292             currentCam.getControl(CameraControl.class).setEnabled(enabled);
    293         }
    294     }
    295 
    296     public void setActiveCamera(String cameraName) {
    297         enableCurrentCam(false);
    298         currentCam = cameras.get(cameraName);
    299         if (currentCam == null) {
    300             logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName);
    301         }
    302         enableCurrentCam(true);
    303     }
    304 
    305     public void activateCamera(final float timeStamp, final String cameraName) {
    306         addCinematicEvent(timeStamp, new AbstractCinematicEvent() {
    307 
    308             @Override
    309             public void play() {
    310                 super.play();
    311                 stop();
    312             }
    313 
    314             @Override
    315             public void onPlay() {
    316                 setActiveCamera(cameraName);
    317             }
    318 
    319             @Override
    320             public void onUpdate(float tpf) {
    321             }
    322 
    323             @Override
    324             public void onStop() {
    325             }
    326 
    327             @Override
    328             public void onPause() {
    329             }
    330 
    331             @Override
    332             public void setTime(float time) {
    333                 play();
    334             }
    335         });
    336     }
    337 
    338     public void setScene(Node scene) {
    339         this.scene = scene;
    340     }
    341 
    342     private Map<String, Map<String, Object>> getEventsData() {
    343         if (eventsData == null) {
    344             eventsData = new HashMap<String, Map<String, Object>>();
    345         }
    346         return eventsData;
    347     }
    348 
    349     public void putEventData(String type, String name, Object object) {
    350         Map<String, Map<String, Object>> data = getEventsData();
    351         Map<String, Object> row = data.get(type);
    352         if (row == null) {
    353             row = new HashMap<String, Object>();
    354         }
    355         row.put(name, object);
    356     }
    357 
    358     public Object getEventData(String type, String name) {
    359         if (eventsData != null) {
    360             Map<String, Object> row = eventsData.get(type);
    361             if (row != null) {
    362                 return row.get(name);
    363             }
    364         }
    365         return null;
    366     }
    367 
    368     public Savable removeEventData(String type, String name) {
    369         if (eventsData != null) {
    370             Map<String, Object> row = eventsData.get(type);
    371             if (row != null) {
    372                 row.remove(name);
    373             }
    374         }
    375         return null;
    376     }
    377 
    378     public Node getScene() {
    379         return scene;
    380     }
    381 }
    382