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 Cinematic()73 public Cinematic() { 74 } 75 Cinematic(Node scene)76 public Cinematic(Node scene) { 77 this.scene = scene; 78 } 79 Cinematic(Node scene, float initialDuration)80 public Cinematic(Node scene, float initialDuration) { 81 super(initialDuration); 82 this.scene = scene; 83 } 84 Cinematic(Node scene, LoopMode loopMode)85 public Cinematic(Node scene, LoopMode loopMode) { 86 super(loopMode); 87 this.scene = scene; 88 } 89 Cinematic(Node scene, float initialDuration, LoopMode loopMode)90 public Cinematic(Node scene, float initialDuration, LoopMode loopMode) { 91 super(initialDuration, loopMode); 92 this.scene = scene; 93 } 94 95 @Override onPlay()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 onStop()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 onPause()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 write(JmeExporter ex)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 read(JmeImporter im)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 setSpeed(float speed)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 initialize(AppStateManager stateManager, Application app)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 isInitialized()171 public boolean isInitialized() { 172 return initialized; 173 } 174 setEnabled(boolean enabled)175 public void setEnabled(boolean enabled) { 176 if (enabled) { 177 play(); 178 } 179 } 180 isEnabled()181 public boolean isEnabled() { 182 return playState == PlayState.Playing; 183 } 184 stateAttached(AppStateManager stateManager)185 public void stateAttached(AppStateManager stateManager) { 186 } 187 stateDetached(AppStateManager stateManager)188 public void stateDetached(AppStateManager stateManager) { 189 stop(); 190 } 191 update(float tpf)192 public void update(float tpf) { 193 if (isInitialized()) { 194 internalUpdate(tpf); 195 } 196 } 197 198 @Override onUpdate(float tpf)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 setTime(float time)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 addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent)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 render(RenderManager rm)252 public void render(RenderManager rm) { 253 } 254 postRender()255 public void postRender() { 256 } 257 cleanup()258 public void cleanup() { 259 } 260 261 /** 262 * fits the duration of the cinamatic to the duration of all its child cinematic events 263 */ fitDuration()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 bindCamera(String cameraName, Camera cam)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 getCamera(String cameraName)286 public CameraNode getCamera(String cameraName) { 287 return cameras.get(cameraName); 288 } 289 enableCurrentCam(boolean enabled)290 private void enableCurrentCam(boolean enabled) { 291 if (currentCam != null) { 292 currentCam.getControl(CameraControl.class).setEnabled(enabled); 293 } 294 } 295 setActiveCamera(String cameraName)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 activateCamera(final float timeStamp, final String cameraName)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 setScene(Node scene)338 public void setScene(Node scene) { 339 this.scene = scene; 340 } 341 getEventsData()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 putEventData(String type, String name, Object object)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 getEventData(String type, String name)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 removeEventData(String type, String name)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 getScene()378 public Node getScene() { 379 return scene; 380 } 381 } 382