com.jme3.cinematic.MotionPath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-core Show documentation
Show all versions of jme3-core Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
/*
* Copyright (c) 2009-2021 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.MotionEvent;
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 listeners;
private Spline spline = new Spline();
int prevWayPoint = 0;
/**
* Create a motion Path
*/
public MotionPath() {
}
/**
* interpolate the path giving the time since the beginning and the motionControl
* this methods sets the new localTranslation to the spatial of the MotionEvent control.
* @param time the time since the animation started
* @param control the control over the moving spatial
* @param tpf time per frame (in seconds)
* @return the distance travelled (in world units)
*/
public float interpolatePath(float time, MotionEvent control, float tpf) {
float traveledDistance = 0;
TempVars vars = TempVars.get();
Vector3f temp = vars.vect1;
Vector3f tmpVector = vars.vect2;
Vector2f v = vars.vect2d;
//computing traveled distance according to new time
traveledDistance = time * (getLength() / control.getInitialDuration());
//getting waypoint index and current value from new traveled distance
v = getWayPointIndexForDistance(traveledDistance, v);
//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());
}
checkWayPoint(control, tpf);
control.getSpatial().setLocalTranslation(temp);
vars.release();
return traveledDistance;
}
public void checkWayPoint(MotionEvent control, float tpf) {
//Epsilon varies with the tpf to avoid missing a waypoint on low framerate.
float epsilon = tpf * 4f;
if (control.getCurrentWayPoint() != prevWayPoint) {
if (control.getCurrentValue() >= 0f && control.getCurrentValue() < epsilon) {
triggerWayPointReach(control.getCurrentWayPoint(), control);
prevWayPoint = control.getCurrentWayPoint();
}
}
}
private void attachDebugNode(Node root) {
if (debugNode == null) {
debugNode = new Node();
Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
for (Iterator it = spline.getControlPoints().iterator(); it.hasNext();) {
Vector3f cp = it.next();
Geometry geo = new Geometry("box", new Box(0.3f, 0.3f, 0.3f));
geo.setLocalTranslation(cp);
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
* @param store storage for the result (not null, modified)
* @return the waypoint index and the interpolation value in a vector2
*/
public Vector2f getWayPointIndexForDistance(float distance, Vector2f store) {
float sum = 0;
if (spline.getTotalLength() == 0) {
store.set(0, 0);
return store;
}
distance = distance % spline.getTotalLength();
int i = 0;
for (Float len : spline.getSegmentsLength()) {
if (sum + len >= distance) {
return new Vector2f(i, (distance - sum) / len);
}
sum += len;
i++;
}
store.set((float) spline.getControlPoints().size() - 1, 1.0f);
return store;
}
/**
* Add a waypoint to the path
* @param wayPoint a position in world space
*/
public void addWayPoint(Vector3f wayPoint) {
spline.addControlPoint(wayPoint);
}
/**
* Return 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 an iterator
*/
public Iterator 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 the desired type
*/
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;
// computeTotalLength();
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();
}
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 the count (≥0)
*/
public int getNbWayPoints() {
return spline.getControlPoints().size();
}
public void triggerWayPointReach(int wayPointIndex, MotionEvent control) {
if (listeners != null) {
for (Iterator it = listeners.iterator(); it.hasNext();) {
MotionPathListener listener = it.next();
listener.onWayPointReach(control, wayPointIndex);
}
}
}
/**
* Returns the curve tension
* @return the curve tension
*/
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 the desired value
*/
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 true for a cycle, false for a non-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 true if the path is a cycle
*/
public boolean isCycle() {
return spline.isCycle();
}
public Spline getSpline() {
return spline;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy