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 */ MotionPath()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 */ interpolatePath(float time, MotionTrack control)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 attachDebugNode(Node root)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 CreateLinearPath()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 CreateCatmullRomPath()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 write(JmeExporter ex)153 public void write(JmeExporter ex) throws IOException { 154 OutputCapsule oc = ex.getCapsule(this); 155 oc.write(spline, "spline", null); 156 } 157 158 @Override read(JmeImporter im)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 */ getWayPointIndexForDistance(float distance)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 */ addWayPoint(Vector3f wayPoint)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 */ getLength()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 */ getWayPoint(int i)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 */ removeWayPoint(Vector3f wayPoint)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 */ removeWayPoint(int i)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 */ iterator()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 */ getPathSplineType()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 */ setPathSplineType(SplineType pathSplineType)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 */ disableDebugShape()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 */ enableDebugShape(AssetManager manager, Node rootNode)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 */ addListener(MotionPathListener listener)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 */ removeListener(MotionPathListener listener)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 */ getNbWayPoints()303 public int getNbWayPoints() { 304 return spline.getControlPoints().size(); 305 } 306 triggerWayPointReach(int wayPointIndex, MotionTrack control)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 */ getCurveTension()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 */ setCurveTension(float curveTension)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 clearWayPoints()339 public void clearWayPoints() { 340 spline.clearControlPoints(); 341 } 342 343 /** 344 * Sets the path to be a cycle 345 * @param cycle 346 */ setCycle(boolean cycle)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 */ isCycle()364 public boolean isCycle() { 365 return spline.isCycle(); 366 } 367 getSpline()368 public Spline getSpline() { 369 return spline; 370 } 371 } 372