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.asset.AssetManager;
     35 import com.jme3.cinematic.events.MotionTrack;
     36 import com.jme3.export.*;
     37 import com.jme3.material.Material;
     38 import com.jme3.math.ColorRGBA;
     39 import com.jme3.math.Spline;
     40 import com.jme3.math.Spline.SplineType;
     41 import com.jme3.math.Vector2f;
     42 import com.jme3.math.Vector3f;
     43 import com.jme3.scene.Geometry;
     44 import com.jme3.scene.Node;
     45 import com.jme3.scene.shape.Box;
     46 import com.jme3.scene.shape.Curve;
     47 import com.jme3.util.TempVars;
     48 import java.io.IOException;
     49 import java.util.ArrayList;
     50 import java.util.Iterator;
     51 import java.util.List;
     52 
     53 /**
     54  * Motion path is used to create a path between way points.
     55  * @author Nehon
     56  */
     57 public class MotionPath implements Savable {
     58 
     59     private Node debugNode;
     60     private AssetManager assetManager;
     61     private List<MotionPathListener> listeners;
     62     private Spline spline = new Spline();
     63     private float eps = 0.0001f;
     64 
     65     /**
     66      * Create a motion Path
     67      */
     68     public MotionPath() {
     69     }
     70 
     71     /**
     72      * interpolate the path giving the time since the beginnin and the motionControl
     73      * this methods sets the new localTranslation to the spatial of the motionTrack control.
     74      * @param time the time since the animation started
     75      * @param control the ocntrol over the moving spatial
     76      */
     77     public float interpolatePath(float time, MotionTrack control) {
     78 
     79         float traveledDistance = 0;
     80         TempVars vars = TempVars.get();
     81         Vector3f temp = vars.vect1;
     82         Vector3f tmpVector = vars.vect2;
     83         //computing traveled distance according to new time
     84         traveledDistance = time * (getLength() / control.getInitialDuration());
     85 
     86         //getting waypoint index and current value from new traveled distance
     87         Vector2f v = getWayPointIndexForDistance(traveledDistance);
     88 
     89         //setting values
     90         control.setCurrentWayPoint((int) v.x);
     91         control.setCurrentValue(v.y);
     92 
     93         //interpolating new position
     94         getSpline().interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
     95         if (control.needsDirection()) {
     96             tmpVector.set(temp);
     97             control.setDirection(tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).normalizeLocal());
     98         }
     99 
    100         control.getSpatial().setLocalTranslation(temp);
    101         vars.release();
    102         return traveledDistance;
    103     }
    104 
    105     private void attachDebugNode(Node root) {
    106         if (debugNode == null) {
    107             debugNode = new Node();
    108             Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
    109             for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
    110                 Vector3f cp = it.next();
    111                 Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f));
    112                 geo.setMaterial(m);
    113                 debugNode.attachChild(geo);
    114 
    115             }
    116             switch (spline.getType()) {
    117                 case CatmullRom:
    118                     debugNode.attachChild(CreateCatmullRomPath());
    119                     break;
    120                 case Linear:
    121                     debugNode.attachChild(CreateLinearPath());
    122                     break;
    123                 default:
    124                     debugNode.attachChild(CreateLinearPath());
    125                     break;
    126             }
    127 
    128             root.attachChild(debugNode);
    129         }
    130     }
    131 
    132     private Geometry CreateLinearPath() {
    133 
    134         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    135         mat.getAdditionalRenderState().setWireframe(true);
    136         mat.setColor("Color", ColorRGBA.Blue);
    137         Geometry lineGeometry = new Geometry("line", new Curve(spline, 0));
    138         lineGeometry.setMaterial(mat);
    139         return lineGeometry;
    140     }
    141 
    142     private Geometry CreateCatmullRomPath() {
    143 
    144         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    145         mat.getAdditionalRenderState().setWireframe(true);
    146         mat.setColor("Color", ColorRGBA.Blue);
    147         Geometry lineGeometry = new Geometry("line", new Curve(spline, 10));
    148         lineGeometry.setMaterial(mat);
    149         return lineGeometry;
    150     }
    151 
    152     @Override
    153     public void write(JmeExporter ex) throws IOException {
    154         OutputCapsule oc = ex.getCapsule(this);
    155         oc.write(spline, "spline", null);
    156     }
    157 
    158     @Override
    159     public void read(JmeImporter im) throws IOException {
    160         InputCapsule in = im.getCapsule(this);
    161         spline = (Spline) in.readSavable("spline", null);
    162 
    163     }
    164 
    165     /**
    166      * compute the index of the waypoint and the interpolation value according to a distance
    167      * returns a vector 2 containing the index in the x field and the interpolation value in the y field
    168      * @param distance the distance traveled on this path
    169      * @return the waypoint index and the interpolation value in a vector2
    170      */
    171     public Vector2f getWayPointIndexForDistance(float distance) {
    172         float sum = 0;
    173         distance = distance % spline.getTotalLength();
    174         int i = 0;
    175         for (Float len : spline.getSegmentsLength()) {
    176             if (sum + len >= distance) {
    177                 return new Vector2f((float) i, (distance - sum) / len);
    178             }
    179             sum += len;
    180             i++;
    181         }
    182         return new Vector2f((float) spline.getControlPoints().size() - 1, 1.0f);
    183     }
    184 
    185     /**
    186      * Addsa waypoint to the path
    187      * @param wayPoint a position in world space
    188      */
    189     public void addWayPoint(Vector3f wayPoint) {
    190         spline.addControlPoint(wayPoint);
    191     }
    192 
    193     /**
    194      * retruns the length of the path in world units
    195      * @return the length
    196      */
    197     public float getLength() {
    198         return spline.getTotalLength();
    199     }
    200 
    201     /**
    202      * returns the waypoint at the given index
    203      * @param i the index
    204      * @return returns the waypoint position
    205      */
    206     public Vector3f getWayPoint(int i) {
    207         return spline.getControlPoints().get(i);
    208     }
    209 
    210     /**
    211      * remove the waypoint from the path
    212      * @param wayPoint the waypoint to remove
    213      */
    214     public void removeWayPoint(Vector3f wayPoint) {
    215         spline.removeControlPoint(wayPoint);
    216     }
    217 
    218     /**
    219      * remove the waypoint at the given index from the path
    220      * @param i the index of the waypoint to remove
    221      */
    222     public void removeWayPoint(int i) {
    223         removeWayPoint(spline.getControlPoints().get(i));
    224     }
    225 
    226     /**
    227      * returns an iterator on the waypoints collection
    228      * @return
    229      */
    230     public Iterator<Vector3f> iterator() {
    231         return spline.getControlPoints().iterator();
    232     }
    233 
    234     /**
    235      * return the type of spline used for the path interpolation for this path
    236      * @return the path interpolation spline type
    237      */
    238     public SplineType getPathSplineType() {
    239         return spline.getType();
    240     }
    241 
    242     /**
    243      * sets the type of spline used for the path interpolation for this path
    244      * @param pathSplineType
    245      */
    246     public void setPathSplineType(SplineType pathSplineType) {
    247         spline.setType(pathSplineType);
    248         if (debugNode != null) {
    249             Node parent = debugNode.getParent();
    250             debugNode.removeFromParent();
    251             debugNode.detachAllChildren();
    252             debugNode = null;
    253             attachDebugNode(parent);
    254         }
    255     }
    256 
    257     /**
    258      * disable the display of the path and the waypoints
    259      */
    260     public void disableDebugShape() {
    261 
    262         debugNode.detachAllChildren();
    263         debugNode = null;
    264         assetManager = null;
    265     }
    266 
    267     /**
    268      * enable the display of the path and the waypoints
    269      * @param manager the assetManager
    270      * @param rootNode the node where the debug shapes must be attached
    271      */
    272     public void enableDebugShape(AssetManager manager, Node rootNode) {
    273         assetManager = manager;
    274         // computeTotalLentgh();
    275         attachDebugNode(rootNode);
    276     }
    277 
    278     /**
    279      * Adds a motion pathListener to the path
    280      * @param listener the MotionPathListener to attach
    281      */
    282     public void addListener(MotionPathListener listener) {
    283         if (listeners == null) {
    284             listeners = new ArrayList<MotionPathListener>();
    285         }
    286         listeners.add(listener);
    287     }
    288 
    289     /**
    290      * remove the given listener
    291      * @param listener the listener to remove
    292      */
    293     public void removeListener(MotionPathListener listener) {
    294         if (listeners != null) {
    295             listeners.remove(listener);
    296         }
    297     }
    298 
    299     /**
    300      * return the number of waypoints of this path
    301      * @return
    302      */
    303     public int getNbWayPoints() {
    304         return spline.getControlPoints().size();
    305     }
    306 
    307     public void triggerWayPointReach(int wayPointIndex, MotionTrack control) {
    308         if (listeners != null) {
    309             for (Iterator<MotionPathListener> it = listeners.iterator(); it.hasNext();) {
    310                 MotionPathListener listener = it.next();
    311                 listener.onWayPointReach(control, wayPointIndex);
    312             }
    313         }
    314     }
    315 
    316     /**
    317      * Returns the curve tension
    318      * @return
    319      */
    320     public float getCurveTension() {
    321         return spline.getCurveTension();
    322     }
    323 
    324     /**
    325      * sets the tension of the curve (only for catmull rom) 0.0 will give a linear curve, 1.0 a round curve
    326      * @param curveTension
    327      */
    328     public void setCurveTension(float curveTension) {
    329         spline.setCurveTension(curveTension);
    330         if (debugNode != null) {
    331             Node parent = debugNode.getParent();
    332             debugNode.removeFromParent();
    333             debugNode.detachAllChildren();
    334             debugNode = null;
    335             attachDebugNode(parent);
    336         }
    337     }
    338 
    339     public void clearWayPoints() {
    340         spline.clearControlPoints();
    341     }
    342 
    343     /**
    344      * Sets the path to be a cycle
    345      * @param cycle
    346      */
    347     public void setCycle(boolean cycle) {
    348 
    349         spline.setCycle(cycle);
    350         if (debugNode != null) {
    351             Node parent = debugNode.getParent();
    352             debugNode.removeFromParent();
    353             debugNode.detachAllChildren();
    354             debugNode = null;
    355             attachDebugNode(parent);
    356         }
    357 
    358     }
    359 
    360     /**
    361      * returns true if the path is a cycle
    362      * @return
    363      */
    364     public boolean isCycle() {
    365         return spline.isCycle();
    366     }
    367 
    368     public Spline getSpline() {
    369         return spline;
    370     }
    371 }
    372