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.events; 33 34 import com.jme3.animation.LoopMode; 35 import com.jme3.app.Application; 36 import com.jme3.cinematic.Cinematic; 37 import com.jme3.cinematic.MotionPath; 38 import com.jme3.cinematic.PlayState; 39 import com.jme3.export.InputCapsule; 40 import com.jme3.export.JmeExporter; 41 import com.jme3.export.JmeImporter; 42 import com.jme3.export.OutputCapsule; 43 import com.jme3.math.Quaternion; 44 import com.jme3.math.Vector2f; 45 import com.jme3.math.Vector3f; 46 import com.jme3.renderer.RenderManager; 47 import com.jme3.renderer.ViewPort; 48 import com.jme3.scene.Spatial; 49 import com.jme3.scene.control.Control; 50 import com.jme3.util.TempVars; 51 import java.io.IOException; 52 53 /** 54 * A MotionTrack is a control over the spatial that manage the position and direction of the spatial while following a motion Path 55 * 56 * You must first create a MotionPath and then create a MotionTrack to associate a spatial and the path. 57 * 58 * @author Nehon 59 */ 60 public class MotionTrack extends AbstractCinematicEvent implements Control { 61 62 protected Spatial spatial; 63 protected int currentWayPoint; 64 protected float currentValue; 65 protected Vector3f direction = new Vector3f(); 66 protected Vector3f lookAt; 67 protected Vector3f upVector; 68 protected Quaternion rotation; 69 protected Direction directionType = Direction.None; 70 protected MotionPath path; 71 private boolean isControl = true; 72 /** 73 * the distance traveled by the spatial on the path 74 */ 75 protected float traveledDistance = 0; 76 77 /** 78 * Enum for the different type of target direction behavior 79 */ 80 public enum Direction { 81 82 /** 83 * the target stay in the starting direction 84 */ 85 None, 86 /** 87 * The target rotates with the direction of the path 88 */ 89 Path, 90 /** 91 * The target rotates with the direction of the path but with the additon of a rtotation 92 * you need to use the setRotation mathod when using this Direction 93 */ 94 PathAndRotation, 95 /** 96 * The target rotates with the given rotation 97 */ 98 Rotation, 99 /** 100 * The target looks at a point 101 * You need to use the setLookAt method when using this direction 102 */ 103 LookAt 104 } 105 106 /** 107 * Create MotionTrack, 108 * when using this constructor don't forget to assign spatial and path 109 */ MotionTrack()110 public MotionTrack() { 111 super(); 112 } 113 114 /** 115 * Creates a MotionPath for the given spatial on the given motion path 116 * @param spatial 117 * @param path 118 */ MotionTrack(Spatial spatial, MotionPath path)119 public MotionTrack(Spatial spatial, MotionPath path) { 120 super(); 121 this.spatial = spatial; 122 spatial.addControl(this); 123 this.path = path; 124 } 125 126 /** 127 * Creates a MotionPath for the given spatial on the given motion path 128 * @param spatial 129 * @param path 130 */ MotionTrack(Spatial spatial, MotionPath path, float initialDuration)131 public MotionTrack(Spatial spatial, MotionPath path, float initialDuration) { 132 super(initialDuration); 133 this.spatial = spatial; 134 spatial.addControl(this); 135 this.path = path; 136 } 137 138 /** 139 * Creates a MotionPath for the given spatial on the given motion path 140 * @param spatial 141 * @param path 142 */ MotionTrack(Spatial spatial, MotionPath path, LoopMode loopMode)143 public MotionTrack(Spatial spatial, MotionPath path, LoopMode loopMode) { 144 super(); 145 this.spatial = spatial; 146 spatial.addControl(this); 147 this.path = path; 148 this.loopMode = loopMode; 149 } 150 151 /** 152 * Creates a MotionPath for the given spatial on the given motion path 153 * @param spatial 154 * @param path 155 */ MotionTrack(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode)156 public MotionTrack(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) { 157 super(initialDuration); 158 this.spatial = spatial; 159 spatial.addControl(this); 160 this.path = path; 161 this.loopMode = loopMode; 162 } 163 update(float tpf)164 public void update(float tpf) { 165 if (isControl) { 166 167 if (playState == PlayState.Playing) { 168 time = time + (tpf * speed); 169 170 if (time >= initialDuration && loopMode == loopMode.DontLoop) { 171 stop(); 172 } else { 173 onUpdate(tpf); 174 } 175 } 176 } 177 } 178 179 @Override initEvent(Application app, Cinematic cinematic)180 public void initEvent(Application app, Cinematic cinematic) { 181 super.initEvent(app, cinematic); 182 isControl = false; 183 } 184 185 @Override setTime(float time)186 public void setTime(float time) { 187 super.setTime(time); 188 onUpdate(0); 189 } 190 onUpdate(float tpf)191 public void onUpdate(float tpf) { 192 traveledDistance = path.interpolatePath(time, this); 193 computeTargetDirection(); 194 } 195 196 @Override write(JmeExporter ex)197 public void write(JmeExporter ex) throws IOException { 198 super.write(ex); 199 OutputCapsule oc = ex.getCapsule(this); 200 oc.write(lookAt, "lookAt", Vector3f.ZERO); 201 oc.write(upVector, "upVector", Vector3f.UNIT_Y); 202 oc.write(rotation, "rotation", Quaternion.IDENTITY); 203 oc.write(directionType, "directionType", Direction.None); 204 oc.write(path, "path", null); 205 } 206 207 @Override read(JmeImporter im)208 public void read(JmeImporter im) throws IOException { 209 super.read(im); 210 InputCapsule in = im.getCapsule(this); 211 lookAt = (Vector3f) in.readSavable("lookAt", Vector3f.ZERO); 212 upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y); 213 rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY); 214 directionType = in.readEnum("directionType", Direction.class, Direction.None); 215 path = (MotionPath) in.readSavable("path", null); 216 } 217 218 /** 219 * this method is meant to be called by the motion path only 220 * @return 221 */ needsDirection()222 public boolean needsDirection() { 223 return directionType == Direction.Path || directionType == Direction.PathAndRotation; 224 } 225 computeTargetDirection()226 private void computeTargetDirection() { 227 switch (directionType) { 228 case Path: 229 Quaternion q = new Quaternion(); 230 q.lookAt(direction, Vector3f.UNIT_Y); 231 spatial.setLocalRotation(q); 232 break; 233 case LookAt: 234 if (lookAt != null) { 235 spatial.lookAt(lookAt, upVector); 236 } 237 break; 238 case PathAndRotation: 239 if (rotation != null) { 240 Quaternion q2 = new Quaternion(); 241 q2.lookAt(direction, Vector3f.UNIT_Y); 242 q2.multLocal(rotation); 243 spatial.setLocalRotation(q2); 244 } 245 break; 246 case Rotation: 247 if (rotation != null) { 248 spatial.setLocalRotation(rotation); 249 } 250 break; 251 case None: 252 break; 253 default: 254 break; 255 } 256 } 257 258 /** 259 * Clone this control for the given spatial 260 * @param spatial 261 * @return 262 */ cloneForSpatial(Spatial spatial)263 public Control cloneForSpatial(Spatial spatial) { 264 MotionTrack control = new MotionTrack(spatial, path); 265 control.playState = playState; 266 control.currentWayPoint = currentWayPoint; 267 control.currentValue = currentValue; 268 control.direction = direction.clone(); 269 control.lookAt = lookAt.clone(); 270 control.upVector = upVector.clone(); 271 control.rotation = rotation.clone(); 272 control.initialDuration = initialDuration; 273 control.speed = speed; 274 control.loopMode = loopMode; 275 control.directionType = directionType; 276 277 return control; 278 } 279 280 @Override onPlay()281 public void onPlay() { 282 traveledDistance = 0; 283 } 284 285 @Override onStop()286 public void onStop() { 287 currentWayPoint = 0; 288 } 289 290 @Override onPause()291 public void onPause() { 292 } 293 294 /** 295 * this method is meant to be called by the motion path only 296 * @return 297 */ getCurrentValue()298 public float getCurrentValue() { 299 return currentValue; 300 } 301 302 /** 303 * this method is meant to be called by the motion path only 304 * 305 */ setCurrentValue(float currentValue)306 public void setCurrentValue(float currentValue) { 307 this.currentValue = currentValue; 308 } 309 310 /** 311 * this method is meant to be called by the motion path only 312 * @return 313 */ getCurrentWayPoint()314 public int getCurrentWayPoint() { 315 return currentWayPoint; 316 } 317 318 /** 319 * this method is meant to be called by the motion path only 320 * 321 */ setCurrentWayPoint(int currentWayPoint)322 public void setCurrentWayPoint(int currentWayPoint) { 323 if (this.currentWayPoint != currentWayPoint) { 324 this.currentWayPoint = currentWayPoint; 325 path.triggerWayPointReach(currentWayPoint, this); 326 } 327 } 328 329 /** 330 * returns the direction the spatial is moving 331 * @return 332 */ getDirection()333 public Vector3f getDirection() { 334 return direction; 335 } 336 337 /** 338 * Sets the direction of the spatial 339 * This method is used by the motion path. 340 * @param direction 341 */ setDirection(Vector3f direction)342 public void setDirection(Vector3f direction) { 343 this.direction.set(direction); 344 } 345 346 /** 347 * returns the direction type of the target 348 * @return the direction type 349 */ getDirectionType()350 public Direction getDirectionType() { 351 return directionType; 352 } 353 354 /** 355 * Sets the direction type of the target 356 * On each update the direction given to the target can have different behavior 357 * See the Direction Enum for explanations 358 * @param directionType the direction type 359 */ setDirectionType(Direction directionType)360 public void setDirectionType(Direction directionType) { 361 this.directionType = directionType; 362 } 363 364 /** 365 * Set the lookAt for the target 366 * This can be used only if direction Type is Direction.LookAt 367 * @param lookAt the position to look at 368 * @param upVector the up vector 369 */ setLookAt(Vector3f lookAt, Vector3f upVector)370 public void setLookAt(Vector3f lookAt, Vector3f upVector) { 371 this.lookAt = lookAt; 372 this.upVector = upVector; 373 } 374 375 /** 376 * returns the rotation of the target 377 * @return the rotation quaternion 378 */ getRotation()379 public Quaternion getRotation() { 380 return rotation; 381 } 382 383 /** 384 * sets the rotation of the target 385 * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation 386 * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion. 387 * With Rotation the rotation of the target will be set with the given Quaternion. 388 * @param rotation the rotation quaternion 389 */ setRotation(Quaternion rotation)390 public void setRotation(Quaternion rotation) { 391 this.rotation = rotation; 392 } 393 394 /** 395 * retun the motion path this control follows 396 * @return 397 */ getPath()398 public MotionPath getPath() { 399 return path; 400 } 401 402 /** 403 * Sets the motion path to follow 404 * @param path 405 */ setPath(MotionPath path)406 public void setPath(MotionPath path) { 407 this.path = path; 408 } 409 setEnabled(boolean enabled)410 public void setEnabled(boolean enabled) { 411 if (enabled) { 412 play(); 413 } else { 414 pause(); 415 } 416 } 417 isEnabled()418 public boolean isEnabled() { 419 return playState != PlayState.Stopped; 420 } 421 render(RenderManager rm, ViewPort vp)422 public void render(RenderManager rm, ViewPort vp) { 423 } 424 setSpatial(Spatial spatial)425 public void setSpatial(Spatial spatial) { 426 this.spatial = spatial; 427 } 428 getSpatial()429 public Spatial getSpatial() { 430 return spatial; 431 } 432 433 /** 434 * return the distance traveled by the spatial on the path 435 * @return 436 */ getTraveledDistance()437 public float getTraveledDistance() { 438 return traveledDistance; 439 } 440 } 441