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