blob: cdb31d0114db5a9430935ba69b147e880e08732c [file] [log] [blame]
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.cinematic;
import com.jme3.asset.AssetManager;
import com.jme3.cinematic.events.MotionTrack;
import com.jme3.export.*;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Spline;
import com.jme3.math.Spline.SplineType;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Curve;
import com.jme3.util.TempVars;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Motion path is used to create a path between way points.
* @author Nehon
*/
public class MotionPath implements Savable {
private Node debugNode;
private AssetManager assetManager;
private List<MotionPathListener> listeners;
private Spline spline = new Spline();
private float eps = 0.0001f;
/**
* Create a motion Path
*/
public MotionPath() {
}
/**
* 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.
* @param time the time since the animation started
* @param control the ocntrol over the moving spatial
*/
public float interpolatePath(float time, MotionTrack control) {
float traveledDistance = 0;
TempVars vars = TempVars.get();
Vector3f temp = vars.vect1;
Vector3f tmpVector = vars.vect2;
//computing traveled distance according to new time
traveledDistance = time * (getLength() / control.getInitialDuration());
//getting waypoint index and current value from new traveled distance
Vector2f v = getWayPointIndexForDistance(traveledDistance);
//setting values
control.setCurrentWayPoint((int) v.x);
control.setCurrentValue(v.y);
//interpolating new position
getSpline().interpolate(control.getCurrentValue(), control.getCurrentWayPoint(), temp);
if (control.needsDirection()) {
tmpVector.set(temp);
control.setDirection(tmpVector.subtractLocal(control.getSpatial().getLocalTranslation()).normalizeLocal());
}
control.getSpatial().setLocalTranslation(temp);
vars.release();
return traveledDistance;
}
private void attachDebugNode(Node root) {
if (debugNode == null) {
debugNode = new Node();
Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
Vector3f cp = it.next();
Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f));
geo.setMaterial(m);
debugNode.attachChild(geo);
}
switch (spline.getType()) {
case CatmullRom:
debugNode.attachChild(CreateCatmullRomPath());
break;
case Linear:
debugNode.attachChild(CreateLinearPath());
break;
default:
debugNode.attachChild(CreateLinearPath());
break;
}
root.attachChild(debugNode);
}
}
private Geometry CreateLinearPath() {
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setWireframe(true);
mat.setColor("Color", ColorRGBA.Blue);
Geometry lineGeometry = new Geometry("line", new Curve(spline, 0));
lineGeometry.setMaterial(mat);
return lineGeometry;
}
private Geometry CreateCatmullRomPath() {
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setWireframe(true);
mat.setColor("Color", ColorRGBA.Blue);
Geometry lineGeometry = new Geometry("line", new Curve(spline, 10));
lineGeometry.setMaterial(mat);
return lineGeometry;
}
@Override
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(spline, "spline", null);
}
@Override
public void read(JmeImporter im) throws IOException {
InputCapsule in = im.getCapsule(this);
spline = (Spline) in.readSavable("spline", null);
}
/**
* 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
* @param wayPoint a position in world space
*/
public void addWayPoint(Vector3f wayPoint) {
spline.addControlPoint(wayPoint);
}
/**
* retruns the length of the path in world units
* @return the length
*/
public float getLength() {
return spline.getTotalLength();
}
/**
* returns the waypoint at the given index
* @param i the index
* @return returns the waypoint position
*/
public Vector3f getWayPoint(int i) {
return spline.getControlPoints().get(i);
}
/**
* remove the waypoint from the path
* @param wayPoint the waypoint to remove
*/
public void removeWayPoint(Vector3f wayPoint) {
spline.removeControlPoint(wayPoint);
}
/**
* remove the waypoint at the given index from the path
* @param i the index of the waypoint to remove
*/
public void removeWayPoint(int i) {
removeWayPoint(spline.getControlPoints().get(i));
}
/**
* returns an iterator on the waypoints collection
* @return
*/
public Iterator<Vector3f> iterator() {
return spline.getControlPoints().iterator();
}
/**
* return the type of spline used for the path interpolation for this path
* @return the path interpolation spline type
*/
public SplineType getPathSplineType() {
return spline.getType();
}
/**
* sets the type of spline used for the path interpolation for this path
* @param pathSplineType
*/
public void setPathSplineType(SplineType pathSplineType) {
spline.setType(pathSplineType);
if (debugNode != null) {
Node parent = debugNode.getParent();
debugNode.removeFromParent();
debugNode.detachAllChildren();
debugNode = null;
attachDebugNode(parent);
}
}
/**
* disable the display of the path and the waypoints
*/
public void disableDebugShape() {
debugNode.detachAllChildren();
debugNode = null;
assetManager = null;
}
/**
* enable the display of the path and the waypoints
* @param manager the assetManager
* @param rootNode the node where the debug shapes must be attached
*/
public void enableDebugShape(AssetManager manager, Node rootNode) {
assetManager = manager;
// computeTotalLentgh();
attachDebugNode(rootNode);
}
/**
* Adds a motion pathListener to the path
* @param listener the MotionPathListener to attach
*/
public void addListener(MotionPathListener listener) {
if (listeners == null) {
listeners = new ArrayList<MotionPathListener>();
}
listeners.add(listener);
}
/**
* remove the given listener
* @param listener the listener to remove
*/
public void removeListener(MotionPathListener listener) {
if (listeners != null) {
listeners.remove(listener);
}
}
/**
* return the number of waypoints of this path
* @return
*/
public int getNbWayPoints() {
return spline.getControlPoints().size();
}
public void triggerWayPointReach(int wayPointIndex, MotionTrack control) {
if (listeners != null) {
for (Iterator<MotionPathListener> it = listeners.iterator(); it.hasNext();) {
MotionPathListener listener = it.next();
listener.onWayPointReach(control, wayPointIndex);
}
}
}
/**
* Returns the curve tension
* @return
*/
public float getCurveTension() {
return spline.getCurveTension();
}
/**
* sets the tension of the curve (only for catmull rom) 0.0 will give a linear curve, 1.0 a round curve
* @param curveTension
*/
public void setCurveTension(float curveTension) {
spline.setCurveTension(curveTension);
if (debugNode != null) {
Node parent = debugNode.getParent();
debugNode.removeFromParent();
debugNode.detachAllChildren();
debugNode = null;
attachDebugNode(parent);
}
}
public void clearWayPoints() {
spline.clearControlPoints();
}
/**
* Sets the path to be a cycle
* @param cycle
*/
public void setCycle(boolean cycle) {
spline.setCycle(cycle);
if (debugNode != null) {
Node parent = debugNode.getParent();
debugNode.removeFromParent();
debugNode.detachAllChildren();
debugNode = null;
attachDebugNode(parent);
}
}
/**
* returns true if the path is a cycle
* @return
*/
public boolean isCycle() {
return spline.isCycle();
}
public Spline getSpline() {
return spline;
}
}