Browse Source

Cinematics :
- One can now shift time forward an backward in a cinematic with the setTime(float) method, all sub cinematic events support it except soundtrack (has to be implemented)
- Enhanced MotionPath calculation to allow time shifting, and made speed more constant
- changed test cases accordingly
- some more javadoc
- fixed some problems in GUITrack


git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8913 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om 14 years ago
parent
commit
08af19ec5b

+ 58 - 31
engine/src/core/com/jme3/cinematic/Cinematic.java

@@ -37,11 +37,13 @@ import com.jme3.app.state.AppState;
 import com.jme3.app.state.AppStateManager;
 import com.jme3.app.state.AppStateManager;
 import com.jme3.cinematic.events.AbstractCinematicEvent;
 import com.jme3.cinematic.events.AbstractCinematicEvent;
 import com.jme3.cinematic.events.CinematicEvent;
 import com.jme3.cinematic.events.CinematicEvent;
+import com.jme3.cinematic.events.CinematicEventListener;
 import com.jme3.export.*;
 import com.jme3.export.*;
 import com.jme3.renderer.Camera;
 import com.jme3.renderer.Camera;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.RenderManager;
 import com.jme3.scene.CameraNode;
 import com.jme3.scene.CameraNode;
 import com.jme3.scene.Node;
 import com.jme3.scene.Node;
+import com.jme3.scene.control.CameraControl;
 import com.jme3.scene.control.CameraControl.ControlDirection;
 import com.jme3.scene.control.CameraControl.ControlDirection;
 import java.io.IOException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -58,7 +60,6 @@ import java.util.logging.Logger;
 public class Cinematic extends AbstractCinematicEvent implements AppState {
 public class Cinematic extends AbstractCinematicEvent implements AppState {
 
 
     private static final Logger logger = Logger.getLogger(Application.class.getName());
     private static final Logger logger = Logger.getLogger(Application.class.getName());
-    private String niftyXmlPath = null;
     private Node scene;
     private Node scene;
     protected TimeLine timeLine = new TimeLine();
     protected TimeLine timeLine = new TimeLine();
     private int lastFetchedKeyFrame = -1;
     private int lastFetchedKeyFrame = -1;
@@ -66,8 +67,8 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     private Map<String, CameraNode> cameras = new HashMap<String, CameraNode>();
     private Map<String, CameraNode> cameras = new HashMap<String, CameraNode>();
     private CameraNode currentCam;
     private CameraNode currentCam;
     private boolean initialized = false;
     private boolean initialized = false;
-//    private Nifty nifty = null;
     private Map<String, Map<String, Object>> eventsData;
     private Map<String, Map<String, Object>> eventsData;
+    private int scheduledPause = -1;
 
 
     public Cinematic() {
     public Cinematic() {
     }
     }
@@ -94,7 +95,8 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     @Override
     @Override
     public void onPlay() {
     public void onPlay() {
         if (isInitialized()) {
         if (isInitialized()) {
-            enableCurrentCam(true);
+            scheduledPause = -1;
+            //enableCurrentCam(true);
             if (playState == PlayState.Paused) {
             if (playState == PlayState.Paused) {
                 for (int i = 0; i < cinematicEvents.size(); i++) {
                 for (int i = 0; i < cinematicEvents.size(); i++) {
                     CinematicEvent ce = cinematicEvents.get(i);
                     CinematicEvent ce = cinematicEvents.get(i);
@@ -125,7 +127,6 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
                 ce.pause();
                 ce.pause();
             }
             }
         }
         }
-        enableCurrentCam(false);
     }
     }
 
 
     @Override
     @Override
@@ -136,8 +137,6 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
         oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
         oc.writeStringSavableMap(cameras, "cameras", null);
         oc.writeStringSavableMap(cameras, "cameras", null);
         oc.write(timeLine, "timeLine", null);
         oc.write(timeLine, "timeLine", null);
-        oc.write(niftyXmlPath, "niftyXmlPath", null);
-
 
 
     }
     }
 
 
@@ -149,12 +148,6 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
         cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
         cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
         cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
         timeLine = (TimeLine) ic.readSavable("timeLine", null);
         timeLine = (TimeLine) ic.readSavable("timeLine", null);
-        niftyXmlPath = ic.readString("niftyXmlPath", null);
-
-    }
-
-    public void bindUi(String xmlPath) {
-        niftyXmlPath = xmlPath;
     }
     }
 
 
     @Override
     @Override
@@ -169,22 +162,11 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     }
     }
 
 
     public void initialize(AppStateManager stateManager, Application app) {
     public void initialize(AppStateManager stateManager, Application app) {
-        if (niftyXmlPath != null) {
-//            NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(app.getAssetManager(),
-//                    app.getInputManager(),
-//                    app.getAudioRenderer(),
-//                    app.getGuiViewPort());
-//            nifty = niftyDisplay.getNifty();
-//            nifty.fromXmlWithoutStartScreen(niftyXmlPath);
-//            app.getGuiViewPort().addProcessor(niftyDisplay);
-        }
         initEvent(app, this);
         initEvent(app, this);
         for (CinematicEvent cinematicEvent : cinematicEvents) {
         for (CinematicEvent cinematicEvent : cinematicEvents) {
             cinematicEvent.initEvent(app, this);
             cinematicEvent.initEvent(app, this);
         }
         }
 
 
-
-
         initialized = true;
         initialized = true;
     }
     }
 
 
@@ -215,8 +197,22 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         }
         }
     }
     }
 
 
+    private void step() {
+        if (playState != PlayState.Playing) {
+            play();
+            scheduledPause = 2;
+        }
+    }
+
     @Override
     @Override
     public void onUpdate(float tpf) {
     public void onUpdate(float tpf) {
+        if (scheduledPause >= 0) {
+            if (scheduledPause == 0) {
+                pause();
+            }
+            scheduledPause--;
+        }
+
         for (int i = 0; i < cinematicEvents.size(); i++) {
         for (int i = 0; i < cinematicEvents.size(); i++) {
             CinematicEvent ce = cinematicEvents.get(i);
             CinematicEvent ce = cinematicEvents.get(i);
             ce.internalUpdate(tpf);
             ce.internalUpdate(tpf);
@@ -235,6 +231,28 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         lastFetchedKeyFrame = keyFrameIndex;
         lastFetchedKeyFrame = keyFrameIndex;
     }
     }
 
 
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);     
+        int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
+
+        //triggering all the event from start to "time" 
+        //then computing timeOffset for each event
+        for (int i = 0; i <= keyFrameIndex; i++) {
+            KeyFrame keyFrame = timeLine.get(i);
+            if (keyFrame != null) {
+                for (CinematicEvent ce : keyFrame.getCinematicEvents()) {
+                    if (playState == PlayState.Playing) {
+                        ce.play();
+                    }
+                    ce.setTime(time - timeLine.getKeyFrameTime(keyFrame));
+                }
+            }
+        }
+
+        step();
+    }
+
     public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
     public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
         KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
         KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
         if (keyFrame == null) {
         if (keyFrame == null) {
@@ -255,6 +273,9 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     public void cleanup() {
     public void cleanup() {
     }
     }
 
 
+    /**
+     * fits the duration of the cinamatic to the duration of all its child cinematic events
+     */
     public void fitDuration() {
     public void fitDuration() {
         KeyFrame kf = timeLine.getKeyFrameAtTime(timeLine.getLastKeyFrameIndex());
         KeyFrame kf = timeLine.getKeyFrameAtTime(timeLine.getLastKeyFrameIndex());
         float d = 0;
         float d = 0;
@@ -271,7 +292,7 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
     public CameraNode bindCamera(String cameraName, Camera cam) {
     public CameraNode bindCamera(String cameraName, Camera cam) {
         CameraNode node = new CameraNode(cameraName, cam);
         CameraNode node = new CameraNode(cameraName, cam);
         node.setControlDir(ControlDirection.SpatialToCamera);
         node.setControlDir(ControlDirection.SpatialToCamera);
-        node.getControl(0).setEnabled(false);
+        node.getControl(CameraControl.class).setEnabled(false);
         cameras.put(cameraName, node);
         cameras.put(cameraName, node);
         scene.attachChild(node);
         scene.attachChild(node);
         return node;
         return node;
@@ -283,7 +304,7 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
 
 
     private void enableCurrentCam(boolean enabled) {
     private void enableCurrentCam(boolean enabled) {
         if (currentCam != null) {
         if (currentCam != null) {
-            currentCam.getControl(0).setEnabled(enabled);
+            currentCam.getControl(CameraControl.class).setEnabled(enabled);
         }
         }
     }
     }
 
 
@@ -296,13 +317,18 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         enableCurrentCam(true);
         enableCurrentCam(true);
     }
     }
 
 
-    public void activateCamera(float timeStamp, final String cameraName) {
+    public void activateCamera(final float timeStamp, final String cameraName) {
         addCinematicEvent(timeStamp, new AbstractCinematicEvent() {
         addCinematicEvent(timeStamp, new AbstractCinematicEvent() {
 
 
+            @Override
+            public void play() {
+                super.play();
+                stop();
+            }
+
             @Override
             @Override
             public void onPlay() {
             public void onPlay() {
                 setActiveCamera(cameraName);
                 setActiveCamera(cameraName);
-                stop();
             }
             }
 
 
             @Override
             @Override
@@ -316,6 +342,11 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
             @Override
             @Override
             public void onPause() {
             public void onPause() {
             }
             }
+
+            @Override
+            public void setTime(float time) {
+                play();
+            }
         });
         });
     }
     }
 
 
@@ -323,10 +354,6 @@ public class Cinematic extends AbstractCinematicEvent implements AppState {
         this.scene = scene;
         this.scene = scene;
     }
     }
 
 
-//    public Nifty getNifty() {
-//        return nifty;
-//    }
-
     private Map<String, Map<String, Object>> getEventsData() {
     private Map<String, Map<String, Object>> getEventsData() {
         if (eventsData == null) {
         if (eventsData == null) {
             eventsData = new HashMap<String, Map<String, Object>>();
             eventsData = new HashMap<String, Map<String, Object>>();

+ 68 - 14
engine/src/core/com/jme3/cinematic/MotionPath.java

@@ -38,6 +38,7 @@ import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Spline;
 import com.jme3.math.Spline;
 import com.jme3.math.Spline.SplineType;
 import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
 import com.jme3.scene.Node;
@@ -68,39 +69,60 @@ public class MotionPath implements Savable {
     }
     }
 
 
     /**
     /**
-     * interpolate the path giving the tpf and the motionControl     
+     * interpolate the path giving the time since the beginnin and the motionControl     
      * this methods sets the new localTranslation to the spatial of the motionTrack control.
      * this methods sets the new localTranslation to the spatial of the motionTrack control.
-     * @param tpf
-     * @param control   
+     * @param time the time since the animation started
+     * @param control the ocntrol over the moving spatial
      */
      */
-    public void interpolatePath(float tpf, MotionTrack control) {
+    public float interpolatePath(float time, MotionTrack control) {
 
 
         float val;
         float val;
+        float traveledDistance = 0;
         TempVars vars = TempVars.get();
         TempVars vars = TempVars.get();
         Vector3f temp = vars.vect1;
         Vector3f temp = vars.vect1;
         Vector3f tmpVector = vars.vect2;
         Vector3f tmpVector = vars.vect2;
         switch (spline.getType()) {
         switch (spline.getType()) {
             case CatmullRom:
             case CatmullRom:
 
 
-                val = tpf * (spline.getTotalLength() / control.getDuration());
+                //this iterative process is done to keep the spatial travel at a constant speed on the path even if 
+                //waypoints are not equally spread over the path
+
+                // we compute the theorical distance that the spatial should travel on this frame
+                val = (time * (spline.getTotalLength() / control.getDuration())) - control.getTraveledDistance();
+                //adding and epsilon value to the control currents value
                 control.setCurrentValue(control.getCurrentValue() + eps);
                 control.setCurrentValue(control.getCurrentValue() + eps);
+                //computing the new position at current value
                 spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
                 spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
-                float dist = getDist(control,temp, tmpVector);
+                //computing traveled distance at current value
+                float dist = getDist(control, temp, tmpVector);
 
 
+                //While the distance traveled this frame is below the theorical distance we iterate the obove computation
                 while (dist < val) {
                 while (dist < val) {
+                    //storing the distance traveled this frame
+                    traveledDistance = dist;
                     control.setCurrentValue(control.getCurrentValue() + eps);
                     control.setCurrentValue(control.getCurrentValue() + eps);
                     spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
                     spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
-                    dist = getDist(control,temp, tmpVector);
+                    dist = getDist(control, temp, tmpVector);
                 }
                 }
+                //compute the direction of the spline
                 if (control.needsDirection()) {
                 if (control.needsDirection()) {
                     tmpVector.set(temp);
                     tmpVector.set(temp);
                     control.setDirection(tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).normalizeLocal());
                     control.setDirection(tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).normalizeLocal());
                 }
                 }
+                //updating traveled distance to the total distance traveled by the spatial since the start
+                traveledDistance += control.getTraveledDistance();
                 break;
                 break;
             case Linear:
             case Linear:
-                val = control.getDuration() * spline.getSegmentsLength().get(control.getCurrentWayPoint()) / spline.getTotalLength();
-                control.setCurrentValue(Math.min(control.getCurrentValue() + tpf / val, 1.0f));
+                //distance traveled this frame
+                val = (time * (spline.getTotalLength() / control.getDuration())) - control.getTraveledDistance();
+                // computing total traveled distance
+                traveledDistance = control.getTraveledDistance() + val;
+                //computing interpolation ratio for this frame
+                val = val / spline.getSegmentsLength().get(control.getCurrentWayPoint());
+                control.setCurrentValue(Math.min(control.getCurrentValue() + val, 1.0f));
+                //interpolationg position
                 spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
                 spline.interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
+                //computing line direction
                 if (control.needsDirection()) {
                 if (control.needsDirection()) {
                     tmpVector.set(spline.getControlPoints().get(control.getCurrentWayPoint() + 1));
                     tmpVector.set(spline.getControlPoints().get(control.getCurrentWayPoint() + 1));
                     control.setDirection(tmpVector.subtractLocal(spline.getControlPoints().get(control.getCurrentWayPoint())).normalizeLocal());
                     control.setDirection(tmpVector.subtractLocal(spline.getControlPoints().get(control.getCurrentWayPoint())).normalizeLocal());
@@ -111,11 +133,19 @@ public class MotionPath implements Savable {
         }
         }
         control.getSpatial().setLocalTranslation(temp);
         control.getSpatial().setLocalTranslation(temp);
         vars.release();
         vars.release();
+        return traveledDistance;
     }
     }
 
 
-    private float getDist(MotionTrack control,Vector3f temp, Vector3f tmpVector) {
-        tmpVector.set(temp);
-        return tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).length();
+    /**
+     * computes the distance between the spatial position and the temp vector.
+     * @param control the control holding the psatial 
+     * @param temp the temp position
+     * @param store a temp vector3f to store the result
+     * @return 
+     */
+    private float getDist(MotionTrack control, Vector3f temp, Vector3f store) {
+        store.set(temp);
+        return store.subtractLocal(control.getSpatial().getLocalTranslation()).length();
     }
     }
 
 
     private void attachDebugNode(Node root) {
     private void attachDebugNode(Node root) {
@@ -178,6 +208,26 @@ public class MotionPath implements Savable {
 
 
     }
     }
 
 
+    /**
+     * compute the index of the waypoint and the interpolation value according to a distance
+     * returns a vector 2 containing the index in the x field and the interpolation value in the y field
+     * @param distance the distance traveled on this path
+     * @return the waypoint index and the interpolation value in a vector2
+     */
+    public Vector2f getWayPointIndexForDistance(float distance) {
+        float sum = 0;
+        distance = distance % spline.getTotalLength();
+        int i = 0;
+        for (Float len : spline.getSegmentsLength()) {
+            if (sum + len >= distance) {
+                return new Vector2f((float) i, (distance - sum) / len);
+            }
+            sum += len;
+            i++;
+        }
+        return new Vector2f((float) spline.getControlPoints().size() - 1, 1.0f);
+    }
+
     /**
     /**
      * Addsa waypoint to the path
      * Addsa waypoint to the path
      * @param wayPoint a position in world space
      * @param wayPoint a position in world space
@@ -332,10 +382,10 @@ public class MotionPath implements Savable {
         }
         }
     }
     }
 
 
-    public void clearWayPoints(){
+    public void clearWayPoints() {
         spline.clearControlPoints();
         spline.clearControlPoints();
     }
     }
-    
+
     /**
     /**
      * Sets the path to be a cycle
      * Sets the path to be a cycle
      * @param cycle
      * @param cycle
@@ -360,4 +410,8 @@ public class MotionPath implements Savable {
     public boolean isCycle() {
     public boolean isCycle() {
         return spline.isCycle();
         return spline.isCycle();
     }
     }
+
+    public Spline getSpline() {
+        return spline;
+    }
 }
 }

+ 4 - 0
engine/src/core/com/jme3/cinematic/TimeLine.java

@@ -89,6 +89,10 @@ public class TimeLine extends HashMap<Integer, KeyFrame> implements Savable {
     public int getKeyFrameIndexFromTime(float time) {
     public int getKeyFrameIndexFromTime(float time) {
         return Math.round(time * keyFramesPerSeconds);
         return Math.round(time * keyFramesPerSeconds);
     }
     }
+    
+    public float getKeyFrameTime(KeyFrame keyFrame) {
+        return (float)keyFrame.getIndex()/(float)keyFramesPerSeconds;
+    }
 
 
     public Collection<KeyFrame> getAllKeyFrames() {
     public Collection<KeyFrame> getAllKeyFrames() {
         return values();
         return values();

+ 19 - 0
engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java

@@ -320,4 +320,23 @@ public abstract class AbstractCinematicEvent implements CinematicEvent {
     public void removeListener(CinematicEventListener listener) {
     public void removeListener(CinematicEventListener listener) {
         getListeners().remove(listener);
         getListeners().remove(listener);
     }
     }
+
+    /**
+     * When this method is invoked, the event should fast forward to the given time according tim 0 is the start of the event.
+     * @param time the time to fast forward to
+     */
+    public void setTime(float time) {
+        elapsedTimePause = time/speed;
+        if(playState == PlayState.Playing){
+            start = timer.getTimeInSeconds();
+        }
+    }
+
+    public float getTime() {
+        return time;
+    }
+    
+    
+    
+    
 }
 }

+ 21 - 0
engine/src/core/com/jme3/cinematic/events/AnimationTrack.java

@@ -103,12 +103,33 @@ public class AnimationTrack extends AbstractCinematicEvent {
         }
         }
     }
     }
 
 
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);
+        float t = time;
+        if(loopMode == loopMode.Loop){
+            t = t % channel.getAnimMaxTime();
+        }
+        if(loopMode == loopMode.Cycle){
+            float parity = (float)Math.ceil(time / channel.getAnimMaxTime());
+            if(parity >0 && parity%2 ==0){
+                t = channel.getAnimMaxTime() - t % channel.getAnimMaxTime();
+            }else{
+                t = t % channel.getAnimMaxTime();
+            }
+            
+        }
+        channel.setTime(t);
+    }
+
     @Override
     @Override
     public void onPlay() {
     public void onPlay() {
         channel.getControl().setEnabled(true);
         channel.getControl().setEnabled(true);
         if (playState == PlayState.Stopped) {
         if (playState == PlayState.Stopped) {
             channel.setAnim(animationName);
             channel.setAnim(animationName);
             channel.setSpeed(speed);
             channel.setSpeed(speed);
+            channel.setLoopMode(loopMode);
+            channel.setTime(time);
         }
         }
     }
     }
 
 

+ 27 - 5
engine/src/core/com/jme3/cinematic/events/CinematicEvent.java

@@ -60,7 +60,7 @@ public interface CinematicEvent extends Savable {
 
 
     /**
     /**
      * Returns the actual duration of the animation
      * Returns the actual duration of the animation
-     * @return
+     * @return the duration
      */
      */
     public float getDuration();
     public float getDuration();
 
 
@@ -72,13 +72,13 @@ public interface CinematicEvent extends Savable {
 
 
     /**
     /**
      * returns the speed of the animation
      * returns the speed of the animation
-     * @return
+     * @return the speed
      */
      */
     public float getSpeed();
     public float getSpeed();
 
 
     /**
     /**
      * returns the PlayState of the animation
      * returns the PlayState of the animation
-     * @return
+     * @return the plat state
      */
      */
     public PlayState getPlayState();
     public PlayState getPlayState();
 
 
@@ -104,7 +104,7 @@ public interface CinematicEvent extends Savable {
 
 
     /**
     /**
      * returns the initial duration of the animation at speed = 1 in seconds.
      * returns the initial duration of the animation at speed = 1 in seconds.
-     * @return
+     * @return the initial duration
      */
      */
     public float getInitialDuration();
     public float getInitialDuration();
 
 
@@ -114,8 +114,30 @@ public interface CinematicEvent extends Savable {
      */
      */
     public void setInitialDuration(float initialDuration);
     public void setInitialDuration(float initialDuration);
 
 
+    /**
+     * called internally in the update method, place here anything you want to run in the update loop
+     * @param tpf time per frame
+     */
     public void internalUpdate(float tpf);
     public void internalUpdate(float tpf);
 
 
+    /**
+     * initialize this event
+     * @param app the application
+     * @param cinematic the cinematic
+     */
     public void initEvent(Application app, Cinematic cinematic);
     public void initEvent(Application app, Cinematic cinematic);
-
+    
+    /**
+     * When this method is invoked, the event should fast forward to the given time according tim 0 is the start of the event.
+     * @param time the time to fast forward to
+     */
+    public void setTime(float time);
+    
+    /**
+     * returns the current time of the cinematic event
+     * @return the time
+     */
+    public float getTime();
+        
+    
 }
 }

+ 42 - 4
engine/src/core/com/jme3/cinematic/events/MotionTrack.java

@@ -41,11 +41,13 @@ import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.export.OutputCapsule;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.control.Control;
 import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
 import java.io.IOException;
 import java.io.IOException;
 
 
 /**
 /**
@@ -67,6 +69,11 @@ public class MotionTrack extends AbstractCinematicEvent implements Control {
     protected Direction directionType = Direction.None;
     protected Direction directionType = Direction.None;
     protected MotionPath path;
     protected MotionPath path;
     private boolean isControl = true;
     private boolean isControl = true;
+    
+    /**
+     * the distance traveled by the spatial on the path
+     */
+    protected float traveledDistance = 0;
 
 
 
 
     /**
     /**
@@ -167,18 +174,39 @@ public class MotionTrack extends AbstractCinematicEvent implements Control {
                 }
                 }
             }
             }
         }
         }
-
     }
     }
 
 
     @Override
     @Override
     public void initEvent(Application app, Cinematic cinematic) {
     public void initEvent(Application app, Cinematic cinematic) {
         super.initEvent(app, cinematic);
         super.initEvent(app, cinematic);
         isControl = false;
         isControl = false;
-       // timer = null;
     }
     }
 
 
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);        
+       
+        //computing traveled distance according to new time
+        traveledDistance = time * (path.getLength() / initialDuration);
+        
+        TempVars vars = TempVars.get();
+        Vector3f temp = vars.vect1;
+        //getting waypoint index and current value from new traveled distance
+        Vector2f v = path.getWayPointIndexForDistance(traveledDistance);
+        //setting values
+        currentWayPoint = (int)v.x;
+        setCurrentValue(v.y);
+        //interpolating new position
+        path.getSpline().interpolate(getCurrentValue(), getCurrentWayPoint(), temp);
+        //setting new position to the spatial
+        spatial.setLocalTranslation(temp);
+        vars.release();
+    }
+    
+   
+
     public void onUpdate(float tpf) {
     public void onUpdate(float tpf) {
-        path.interpolatePath(tpf * speed, this);
+        traveledDistance = path.interpolatePath(time, this);
         computeTargetDirection();
         computeTargetDirection();
 
 
         if (currentValue >= 1.0f) {
         if (currentValue >= 1.0f) {
@@ -192,7 +220,7 @@ public class MotionTrack extends AbstractCinematicEvent implements Control {
             } else {
             } else {
                 stop();
                 stop();
             }
             }
-        }
+        }       
     }
     }
 
 
     @Override
     @Override
@@ -423,4 +451,14 @@ public class MotionTrack extends AbstractCinematicEvent implements Control {
     public Spatial getSpatial() {
     public Spatial getSpatial() {
         return spatial;
         return spatial;
     }
     }
+    
+    /**
+     * return the distance traveled by the spatial on the path
+     * @return 
+     */
+    public float getTraveledDistance() {
+        return traveledDistance;
+    }
+
+    
 }
 }

+ 6 - 2
engine/src/core/com/jme3/cinematic/events/SoundTrack.java

@@ -110,13 +110,17 @@ public class SoundTrack extends AbstractCinematicEvent {
         super.initEvent(app, cinematic);
         super.initEvent(app, cinematic);
         audioNode = new AudioNode(app.getAssetManager(), path, stream);
         audioNode = new AudioNode(app.getAssetManager(), path, stream);
         setLoopMode(loopMode);
         setLoopMode(loopMode);
+    }
 
 
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);
+        //TODO has to be implemented in the audioRenderer
     }
     }
 
 
     @Override
     @Override
     public void onPlay() {
     public void onPlay() {
-        audioNode.play();
-        
+        audioNode.play();        
     }
     }
 
 
     @Override
     @Override

+ 8 - 1
engine/src/core/com/jme3/post/filters/FadeFilter.java

@@ -136,6 +136,10 @@ public class FadeFilter extends Filter {
 
 
     }
     }
 
 
+    public void pause() {
+        playing = false;
+    }
+    
     @Override
     @Override
     public void write(JmeExporter ex) throws IOException {
     public void write(JmeExporter ex) throws IOException {
         super.write(ex);
         super.write(ex);
@@ -165,6 +169,9 @@ public class FadeFilter extends Filter {
      * @param value 
      * @param value 
      */
      */
     public void setValue(float value) {
     public void setValue(float value) {
-        this.value = value;
+        this.value = value;       
+        if (material != null) {
+            material.setFloat("Value", value);
+        }
     }
     }
 }
 }

+ 7 - 9
engine/src/niftygui/com/jme3/cinematic/events/GuiTrack.java

@@ -52,36 +52,34 @@ public class GuiTrack extends AbstractCinematicEvent {
 
 
     public GuiTrack() {
     public GuiTrack() {
     }
     }
-    
+
     public GuiTrack(Nifty nifty, String screen) {
     public GuiTrack(Nifty nifty, String screen) {
         this.screen = screen;
         this.screen = screen;
+        this.nifty = nifty;
     }
     }
 
 
     public GuiTrack(Nifty nifty, String screen, float initialDuration) {
     public GuiTrack(Nifty nifty, String screen, float initialDuration) {
         super(initialDuration);
         super(initialDuration);
         this.screen = screen;
         this.screen = screen;
-
+        this.nifty = nifty;
     }
     }
 
 
     public GuiTrack(Nifty nifty, String screen, LoopMode loopMode) {
     public GuiTrack(Nifty nifty, String screen, LoopMode loopMode) {
         super(loopMode);
         super(loopMode);
         this.screen = screen;
         this.screen = screen;
+        this.nifty = nifty;
     }
     }
 
 
     public GuiTrack(Nifty nifty, String screen, float initialDuration, LoopMode loopMode) {
     public GuiTrack(Nifty nifty, String screen, float initialDuration, LoopMode loopMode) {
         super(initialDuration, loopMode);
         super(initialDuration, loopMode);
         this.screen = screen;
         this.screen = screen;
-    }
-
-    @Override
-    public void initEvent(Application app, Cinematic cinematic) {
-        super.initEvent(app, cinematic);
-//        nifty = cinematic.getNifty();
+        this.nifty = nifty;
     }
     }
 
 
     @Override
     @Override
     public void onPlay() {
     public void onPlay() {
-        nifty.gotoScreen(screen);        
+        System.out.println("screen should be "+screen);
+        nifty.gotoScreen(screen);
     }
     }
 
 
     @Override
     @Override

+ 102 - 54
engine/src/test/jme3test/animation/TestCinematic.java

@@ -57,7 +57,6 @@ import com.jme3.scene.Geometry;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.shape.Box;
 import com.jme3.scene.shape.Box;
 import com.jme3.shadow.PssmShadowRenderer;
 import com.jme3.shadow.PssmShadowRenderer;
-import com.jme3.system.NanoTimer;
 import de.lessvoid.nifty.Nifty;
 import de.lessvoid.nifty.Nifty;
 
 
 public class TestCinematic extends SimpleApplication {
 public class TestCinematic extends SimpleApplication {
@@ -70,6 +69,7 @@ public class TestCinematic extends SimpleApplication {
     private ChaseCamera chaseCam;
     private ChaseCamera chaseCam;
     private FilterPostProcessor fpp;
     private FilterPostProcessor fpp;
     private FadeFilter fade;
     private FadeFilter fade;
+    private float time = 0;
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
         TestCinematic app = new TestCinematic();
         TestCinematic app = new TestCinematic();
@@ -101,33 +101,13 @@ public class TestCinematic extends SimpleApplication {
         createScene();
         createScene();
 
 
         cinematic = new Cinematic(rootNode, 20);
         cinematic = new Cinematic(rootNode, 20);
-//        cinematic.bindUi("Interface/Nifty/CinematicTest.xml");
         stateManager.attach(cinematic);
         stateManager.attach(cinematic);
 
 
         createCameraMotion();
         createCameraMotion();
 
 
 
 
-        cinematic.addCinematicEvent(0, new AbstractCinematicEvent() {
-
-            @Override
-            public void onPlay() {
-                fade.setDuration(1f / cinematic.getSpeed());
-                fade.setValue(0);
-                fade.fadeIn();
-            }
-
-            @Override
-            public void onUpdate(float tpf) {
-            }
-
-            @Override
-            public void onStop() {
-            }
-
-            @Override
-            public void onPause() {
-            }
-        });
+        //fade in
+        cinematic.addCinematicEvent(0, new FadeEvent(true));
         cinematic.addCinematicEvent(0, new PositionTrack(teapot, new Vector3f(10, 0, 10), 0));
         cinematic.addCinematicEvent(0, new PositionTrack(teapot, new Vector3f(10, 0, 10), 0));
         cinematic.addCinematicEvent(0, new ScaleTrack(teapot, new Vector3f(1, 1, 1), 0));
         cinematic.addCinematicEvent(0, new ScaleTrack(teapot, new Vector3f(1, 1, 1), 0));
         Quaternion q = new Quaternion();
         Quaternion q = new Quaternion();
@@ -143,46 +123,45 @@ public class TestCinematic extends SimpleApplication {
         cinematic.activateCamera(0, "aroundCam");
         cinematic.activateCamera(0, "aroundCam");
         cinematic.addCinematicEvent(0, cameraMotionTrack);
         cinematic.addCinematicEvent(0, cameraMotionTrack);
         cinematic.addCinematicEvent(0, new SoundTrack("Sound/Environment/Nature.ogg", LoopMode.Loop));
         cinematic.addCinematicEvent(0, new SoundTrack("Sound/Environment/Nature.ogg", LoopMode.Loop));
-        cinematic.addCinematicEvent(3, new SoundTrack("Sound/Effects/kick.wav"));
+        cinematic.addCinematicEvent(3f, new SoundTrack("Sound/Effects/kick.wav"));
         cinematic.addCinematicEvent(3, new SubtitleTrack(nifty, "start", 3, "jMonkey engine really kicks A..."));
         cinematic.addCinematicEvent(3, new SubtitleTrack(nifty, "start", 3, "jMonkey engine really kicks A..."));
-        cinematic.addCinematicEvent(5.0f, new SoundTrack("Sound/Effects/Beep.ogg", 1));
-        cinematic.addCinematicEvent(6, new AnimationTrack(model, "Walk", LoopMode.Loop));
+        cinematic.addCinematicEvent(5.1f, new SoundTrack("Sound/Effects/Beep.ogg", 1));
+        cinematic.addCinematicEvent(2, new AnimationTrack(model, "Walk", LoopMode.Loop));
         cinematic.activateCamera(6, "topView");
         cinematic.activateCamera(6, "topView");
         cinematic.activateCamera(10, "aroundCam");
         cinematic.activateCamera(10, "aroundCam");
 
 
-        cinematic.addCinematicEvent(19, new AbstractCinematicEvent() {
-
-            @Override
-            public void onPlay() {
-                fade.setDuration(1f / cinematic.getSpeed());
-                fade.fadeOut();
-            }
-
-            @Override
-            public void onUpdate(float tpf) {
-            }
-
-            @Override
-            public void onStop() {
-            }
-
-            @Override
-            public void onPause() {
-            }
-        });
+        //fade out
+        cinematic.addCinematicEvent(19, new FadeEvent(false));
+//        cinematic.addCinematicEvent(19, new AbstractCinematicEvent() {
+//
+//            @Override
+//            public void onPlay() {
+//                fade.setDuration(1f / cinematic.getSpeed());
+//                fade.fadeOut();
+//
+//            }
+//
+//            @Override
+//            public void onUpdate(float tpf) {
+//            }
+//
+//            @Override
+//            public void onStop() {
+//            }
+//
+//            @Override
+//            public void onPause() {
+//            }
+//        });
 
 
-        final NanoTimer myTimer = new NanoTimer();
         cinematic.addListener(new CinematicEventListener() {
         cinematic.addListener(new CinematicEventListener() {
 
 
             public void onPlay(CinematicEvent cinematic) {
             public void onPlay(CinematicEvent cinematic) {
                 chaseCam.setEnabled(false);
                 chaseCam.setEnabled(false);
-                myTimer.reset();
-
                 System.out.println("play");
                 System.out.println("play");
             }
             }
 
 
             public void onPause(CinematicEvent cinematic) {
             public void onPause(CinematicEvent cinematic) {
-                chaseCam.setEnabled(true);
                 System.out.println("pause");
                 System.out.println("pause");
             }
             }
 
 
@@ -190,12 +169,10 @@ public class TestCinematic extends SimpleApplication {
                 chaseCam.setEnabled(true);
                 chaseCam.setEnabled(true);
                 fade.setValue(1);
                 fade.setValue(1);
                 System.out.println("stop");
                 System.out.println("stop");
-                System.out.println((float) myTimer.getTime() / (float) myTimer.getResolution());
-
             }
             }
         });
         });
 
 
-        cinematic.setSpeed(2);
+        //cinematic.setSpeed(2);
         flyCam.setEnabled(false);
         flyCam.setEnabled(false);
         chaseCam = new ChaseCamera(cam, model, inputManager);
         chaseCam = new ChaseCamera(cam, model, inputManager);
         initInputs();
         initInputs();
@@ -269,19 +246,90 @@ public class TestCinematic extends SimpleApplication {
 
 
     private void initInputs() {
     private void initInputs() {
         inputManager.addMapping("togglePause", new KeyTrigger(keyInput.KEY_RETURN));
         inputManager.addMapping("togglePause", new KeyTrigger(keyInput.KEY_RETURN));
+        inputManager.addMapping("navFwd", new KeyTrigger(keyInput.KEY_RIGHT));
+        inputManager.addMapping("navBack", new KeyTrigger(keyInput.KEY_LEFT));
         ActionListener acl = new ActionListener() {
         ActionListener acl = new ActionListener() {
 
 
             public void onAction(String name, boolean keyPressed, float tpf) {
             public void onAction(String name, boolean keyPressed, float tpf) {
                 if (name.equals("togglePause") && keyPressed) {
                 if (name.equals("togglePause") && keyPressed) {
                     if (cinematic.getPlayState() == PlayState.Playing) {
                     if (cinematic.getPlayState() == PlayState.Playing) {
                         cinematic.pause();
                         cinematic.pause();
+                        time = cinematic.getTime();
                     } else {
                     } else {
                         cinematic.play();
                         cinematic.play();
                     }
                     }
                 }
                 }
 
 
+                if (cinematic.getPlayState() != PlayState.Playing) {
+                    if (name.equals("navFwd") && keyPressed) {
+                        time += 0.25;
+                        FastMath.clamp(time, 0, cinematic.getInitialDuration());
+                        cinematic.setTime(time);
+                    }
+                    if (name.equals("navBack") && keyPressed) {
+                        time -= 0.25;
+                        FastMath.clamp(time, 0, cinematic.getInitialDuration());
+                        cinematic.setTime(time);
+                    }
+
+                }
             }
             }
         };
         };
-        inputManager.addListener(acl, "togglePause");
+        inputManager.addListener(acl, "togglePause", "navFwd", "navBack");
+    }
+
+    private class FadeEvent extends AbstractCinematicEvent {
+
+        boolean in = true;
+        float value = 0;        
+
+        public FadeEvent(boolean in) {
+            super(1);
+            this.in = in;
+            value = in ? 0 : 1;
+        }
+
+        @Override
+        public void onPlay() {
+            
+            fade.setDuration(1f / cinematic.getSpeed());
+            if (in) {
+                fade.fadeIn();
+            } else {
+                fade.fadeOut();
+            }
+            fade.setValue(value);
+
+        }
+
+        @Override
+        public void setTime(float time) {
+            super.setTime(time);
+            if (time >= fade.getDuration()) {
+                value = in ? 1 : 0;
+                fade.setValue(value);
+            } else {
+                value = time;
+                if (in) {
+                    fade.setValue(time / cinematic.getSpeed());
+                } else {
+                    fade.setValue(1 - time / cinematic.getSpeed());
+                }
+            }
+        }
+
+        @Override
+        public void onUpdate(float tpf) {
+        }
+
+        @Override
+        public void onStop() {
+        }
+
+        @Override
+        public void onPause() {
+            value = fade.getValue();
+            fade.pause();
+        }
     }
     }
 }
 }

+ 1 - 1
engine/src/test/jme3test/animation/TestMotionPath.java

@@ -83,7 +83,7 @@ public class TestMotionPath extends SimpleApplication {
         motionControl.setDirectionType(MotionTrack.Direction.PathAndRotation);
         motionControl.setDirectionType(MotionTrack.Direction.PathAndRotation);
         motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));
         motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));
         motionControl.setInitialDuration(10f);
         motionControl.setInitialDuration(10f);
-        motionControl.setSpeed(0.1f);
+        motionControl.setSpeed(2f);
 
 
         guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
         guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
         final BitmapText wayPointsText = new BitmapText(guiFont, false);
         final BitmapText wayPointsText = new BitmapText(guiFont, false);

+ 3 - 3
engine/test-data/Interface/Nifty/CinematicTest.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
-<nifty xmlns="http://nifty-gui.sourceforge.net/nifty.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty.xsd http://nifty-gui.sourceforge.net/nifty.xsd">
-  <screen id="start" controller="de.lessvoid.nifty.examples.helloworld.HelloWorldStartScreen">
+<nifty>
+  <screen id="start" controller="jme3test.niftygui.TestNiftyGui">
     <layer id="layer" backgroundColor="#0000" childLayout="center">
     <layer id="layer" backgroundColor="#0000" childLayout="center">
-      <panel id="panel"  width="100%"  backgroundColor="#0000" childLayout="center" visibleToMouse="true" >
+      <panel id="panel"  width="100%" height="100%" backgroundColor="#0000" childLayout="center" visibleToMouse="true" >
           <text id="text" font="aurulent-sans-16.fnt" color="#000f" text="" align="center" valign="bottom"  />
           <text id="text" font="aurulent-sans-16.fnt" color="#000f" text="" align="center" valign="bottom"  />
       </panel>
       </panel>
     </layer>
     </layer>