diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java index 44b836115..0cd24cab8 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java @@ -1,365 +1,369 @@ -/* - * Copyright (c) 2009-2012 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.animation; - -import com.jme3.math.FastMath; -import com.jme3.util.TempVars; -import java.util.BitSet; - -/** - * AnimChannel provides controls, such as play, pause, - * fast forward, etc, for an animation. The animation - * channel may influence the entire model or specific bones of the model's - * skeleton. A single model may have multiple animation channels influencing - * various parts of its body. For example, a character model may have an - * animation channel for its feet, and another for its torso, and - * the animations for each channel are controlled independently. - * - * @author Kirill Vainer - */ -public final class AnimChannel { - - private static final float DEFAULT_BLEND_TIME = 0.15f; - - private AnimControl control; - - private BitSet affectedBones; - - private Animation animation; - private Animation blendFrom; - private float time; - private float speed; - private float timeBlendFrom; - private float blendTime; - private float speedBlendFrom; - private boolean notified=false; - - private LoopMode loopMode, loopModeBlendFrom; - - private float blendAmount = 1f; - private float blendRate = 0; - - AnimChannel(AnimControl control){ - this.control = control; - } - - /** - * Returns the parent control of this AnimChannel. - * - * @return the parent control of this AnimChannel. - * @see AnimControl - */ - public AnimControl getControl() { - return control; - } - - /** - * @return The name of the currently playing animation, or null if - * none is assigned. - * - * @see AnimChannel#setAnim(java.lang.String) - */ - public String getAnimationName() { - return animation != null ? animation.getName() : null; - } - - /** - * @return The loop mode currently set for the animation. The loop mode - * determines what will happen to the animation once it finishes - * playing. - * - * For more information, see the LoopMode enum class. - * @see LoopMode - * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode) - */ - public LoopMode getLoopMode() { - return loopMode; - } - - /** - * @param loopMode Set the loop mode for the channel. The loop mode - * determines what will happen to the animation once it finishes - * playing. - * - * For more information, see the LoopMode enum class. - * @see LoopMode - */ - public void setLoopMode(LoopMode loopMode) { - this.loopMode = loopMode; - } - - /** - * @return The speed that is assigned to the animation channel. The speed - * is a scale value starting from 0.0, at 1.0 the animation will play - * at its default speed. - * - * @see AnimChannel#setSpeed(float) - */ - public float getSpeed() { - return speed; - } - - /** - * @param speed Set the speed of the animation channel. The speed - * is a scale value starting from 0.0, at 1.0 the animation will play - * at its default speed. - */ - public void setSpeed(float speed) { - this.speed = speed; - if(blendTime>0){ - this.speedBlendFrom = speed; - blendTime = Math.min(blendTime, animation.getLength() / speed); - blendRate = 1/ blendTime; - } - } - - /** - * @return The time of the currently playing animation. The time - * starts at 0 and continues on until getAnimMaxTime(). - * - * @see AnimChannel#setTime(float) - */ - public float getTime() { - return time; - } - - /** - * @param time Set the time of the currently playing animation, the time - * is clamped from 0 to {@link #getAnimMaxTime()}. - */ - public void setTime(float time) { - this.time = FastMath.clamp(time, 0, getAnimMaxTime()); - } - - /** - * @return The length of the currently playing animation, or zero - * if no animation is playing. - * - * @see AnimChannel#getTime() - */ - public float getAnimMaxTime(){ - return animation != null ? animation.getLength() : 0f; - } - - /** - * Set the current animation that is played by this AnimChannel. - *

- * This resets the time to zero, and optionally blends the animation - * over blendTime seconds with the currently playing animation. - * Notice that this method will reset the control's speed to 1.0. - * - * @param name The name of the animation to play - * @param blendTime The blend time over which to blend the new animation - * with the old one. If zero, then no blending will occur and the new - * animation will be applied instantly. - */ - public void setAnim(String name, float blendTime){ - if (name == null) - throw new IllegalArgumentException("name cannot be null"); - - if (blendTime < 0f) - throw new IllegalArgumentException("blendTime cannot be less than zero"); - - Animation anim = control.animationMap.get(name); - if (anim == null) - throw new IllegalArgumentException("Cannot find animation named: '"+name+"'"); - - control.notifyAnimChange(this, name); - - if (animation != null && blendTime > 0f){ - this.blendTime = blendTime; - // activate blending - blendTime = Math.min(blendTime, anim.getLength() / speed); - blendFrom = animation; - timeBlendFrom = time; - speedBlendFrom = speed; - loopModeBlendFrom = loopMode; - blendAmount = 0f; - blendRate = 1f / blendTime; - }else{ - blendFrom = null; - } - - animation = anim; - time = 0; - speed = 1f; - loopMode = LoopMode.Loop; - notified = false; - } - - /** - * Set the current animation that is played by this AnimChannel. - *

- * See {@link #setAnim(java.lang.String, float)}. - * The blendTime argument by default is 150 milliseconds. - * - * @param name The name of the animation to play - */ - public void setAnim(String name){ - setAnim(name, DEFAULT_BLEND_TIME); - } - - /** - * Add all the bones of the model's skeleton to be - * influenced by this animation channel. - */ - public void addAllBones() { - affectedBones = null; - } - - /** - * Add a single bone to be influenced by this animation channel. - */ - public void addBone(String name) { - addBone(control.getSkeleton().getBone(name)); - } - - /** - * Add a single bone to be influenced by this animation channel. - */ - public void addBone(Bone bone) { - int boneIndex = control.getSkeleton().getBoneIndex(bone); - if(affectedBones == null) { - affectedBones = new BitSet(control.getSkeleton().getBoneCount()); - } - affectedBones.set(boneIndex); - } - - /** - * Add bones to be influenced by this animation channel starting from the - * given bone name and going toward the root bone. - */ - public void addToRootBone(String name) { - addToRootBone(control.getSkeleton().getBone(name)); - } - - /** - * Add bones to be influenced by this animation channel starting from the - * given bone and going toward the root bone. - */ - public void addToRootBone(Bone bone) { - addBone(bone); - while (bone.getParent() != null) { - bone = bone.getParent(); - addBone(bone); - } - } - - /** - * Add bones to be influenced by this animation channel, starting - * from the given named bone and going toward its children. - */ - public void addFromRootBone(String name) { - addFromRootBone(control.getSkeleton().getBone(name)); - } - - /** - * Add bones to be influenced by this animation channel, starting - * from the given bone and going toward its children. - */ - public void addFromRootBone(Bone bone) { - addBone(bone); - if (bone.getChildren() == null) - return; - for (Bone childBone : bone.getChildren()) { - addBone(childBone); - addFromRootBone(childBone); - } - } - - BitSet getAffectedBones(){ - return affectedBones; - } - - public void reset(boolean rewind){ - if(rewind){ - setTime(0); - if(control.getSkeleton()!=null){ - control.getSkeleton().resetAndUpdate(); - }else{ - TempVars vars = TempVars.get(); - update(0, vars); - vars.release(); - } - } - animation = null; - notified = false; - } - - void update(float tpf, TempVars vars) { - if (animation == null) - return; - - if (blendFrom != null && blendAmount != 1.0f){ - // The blendFrom anim is set, the actual animation - // playing will be set -// blendFrom.setTime(timeBlendFrom, 1f, control, this, vars); - blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars); - - timeBlendFrom += tpf * speedBlendFrom; - timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom, - blendFrom.getLength(), - loopModeBlendFrom); - if (timeBlendFrom < 0){ - timeBlendFrom = -timeBlendFrom; - speedBlendFrom = -speedBlendFrom; - } - - blendAmount += tpf * blendRate; - if (blendAmount > 1f){ - blendAmount = 1f; - blendFrom = null; - } - } - - animation.setTime(time, blendAmount, control, this, vars); - time += tpf * speed; - if (animation.getLength() > 0){ - if (!notified && (time >= animation.getLength() || time < 0)) { - if (loopMode == LoopMode.DontLoop) { - // Note that this flag has to be set before calling the notify - // since the notify may start a new animation and then unset - // the flag. - notified = true; - } - control.notifyAnimCycleDone(this, animation.getName()); - } - } - time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode); - if (time < 0){ - // Negative time indicates that speed should be inverted - // (for cycle loop mode only) - time = -time; - speed = -speed; - } - } -} +/* + * Copyright (c) 2009-2012 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.animation; + +import com.jme3.math.FastMath; +import com.jme3.util.TempVars; +import java.util.BitSet; + +/** + * AnimChannel provides controls, such as play, pause, + * fast forward, etc, for an animation. The animation + * channel may influence the entire model or specific bones of the model's + * skeleton. A single model may have multiple animation channels influencing + * various parts of its body. For example, a character model may have an + * animation channel for its feet, and another for its torso, and + * the animations for each channel are controlled independently. + * + * @author Kirill Vainer + */ +public final class AnimChannel { + + private static final float DEFAULT_BLEND_TIME = 0.15f; + + private AnimControl control; + + private BitSet affectedBones; + + private Animation animation; + private Animation blendFrom; + private float time; + private float speed; + private float timeBlendFrom; + private float blendTime; + private float speedBlendFrom; + private boolean notified=false; + + private LoopMode loopMode, loopModeBlendFrom; + + private float blendAmount = 1f; + private float blendRate = 0; + + public AnimChannel(){ + + } + + public AnimChannel(AnimControl control){ + this.control = control; + } + + /** + * Returns the parent control of this AnimChannel. + * + * @return the parent control of this AnimChannel. + * @see AnimControl + */ + public AnimControl getControl() { + return control; + } + + /** + * @return The name of the currently playing animation, or null if + * none is assigned. + * + * @see AnimChannel#setAnim(java.lang.String) + */ + public String getAnimationName() { + return animation != null ? animation.getName() : null; + } + + /** + * @return The loop mode currently set for the animation. The loop mode + * determines what will happen to the animation once it finishes + * playing. + * + * For more information, see the LoopMode enum class. + * @see LoopMode + * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode) + */ + public LoopMode getLoopMode() { + return loopMode; + } + + /** + * @param loopMode Set the loop mode for the channel. The loop mode + * determines what will happen to the animation once it finishes + * playing. + * + * For more information, see the LoopMode enum class. + * @see LoopMode + */ + public void setLoopMode(LoopMode loopMode) { + this.loopMode = loopMode; + } + + /** + * @return The speed that is assigned to the animation channel. The speed + * is a scale value starting from 0.0, at 1.0 the animation will play + * at its default speed. + * + * @see AnimChannel#setSpeed(float) + */ + public float getSpeed() { + return speed; + } + + /** + * @param speed Set the speed of the animation channel. The speed + * is a scale value starting from 0.0, at 1.0 the animation will play + * at its default speed. + */ + public void setSpeed(float speed) { + this.speed = speed; + if(blendTime>0){ + this.speedBlendFrom = speed; + blendTime = Math.min(blendTime, animation.getLength() / speed); + blendRate = 1/ blendTime; + } + } + + /** + * @return The time of the currently playing animation. The time + * starts at 0 and continues on until getAnimMaxTime(). + * + * @see AnimChannel#setTime(float) + */ + public float getTime() { + return time; + } + + /** + * @param time Set the time of the currently playing animation, the time + * is clamped from 0 to {@link #getAnimMaxTime()}. + */ + public void setTime(float time) { + this.time = FastMath.clamp(time, 0, getAnimMaxTime()); + } + + /** + * @return The length of the currently playing animation, or zero + * if no animation is playing. + * + * @see AnimChannel#getTime() + */ + public float getAnimMaxTime(){ + return animation != null ? animation.getLength() : 0f; + } + + /** + * Set the current animation that is played by this AnimChannel. + *

+ * This resets the time to zero, and optionally blends the animation + * over blendTime seconds with the currently playing animation. + * Notice that this method will reset the control's speed to 1.0. + * + * @param name The name of the animation to play + * @param blendTime The blend time over which to blend the new animation + * with the old one. If zero, then no blending will occur and the new + * animation will be applied instantly. + */ + public void setAnim(String name, float blendTime){ + if (name == null) + throw new IllegalArgumentException("name cannot be null"); + + if (blendTime < 0f) + throw new IllegalArgumentException("blendTime cannot be less than zero"); + + Animation anim = control.animationMap.get(name); + if (anim == null) + throw new IllegalArgumentException("Cannot find animation named: '"+name+"'"); + + control.notifyAnimChange(this, name); + + if (animation != null && blendTime > 0f){ + this.blendTime = blendTime; + // activate blending + blendTime = Math.min(blendTime, anim.getLength() / speed); + blendFrom = animation; + timeBlendFrom = time; + speedBlendFrom = speed; + loopModeBlendFrom = loopMode; + blendAmount = 0f; + blendRate = 1f / blendTime; + }else{ + blendFrom = null; + } + + animation = anim; + time = 0; + speed = 1f; + loopMode = LoopMode.Loop; + notified = false; + } + + /** + * Set the current animation that is played by this AnimChannel. + *

+ * See {@link #setAnim(java.lang.String, float)}. + * The blendTime argument by default is 150 milliseconds. + * + * @param name The name of the animation to play + */ + public void setAnim(String name){ + setAnim(name, DEFAULT_BLEND_TIME); + } + + /** + * Add all the bones of the model's skeleton to be + * influenced by this animation channel. + */ + public void addAllBones() { + affectedBones = null; + } + + /** + * Add a single bone to be influenced by this animation channel. + */ + public void addBone(String name) { + addBone(control.getSkeleton().getBone(name)); + } + + /** + * Add a single bone to be influenced by this animation channel. + */ + public void addBone(Bone bone) { + int boneIndex = control.getSkeleton().getBoneIndex(bone); + if(affectedBones == null) { + affectedBones = new BitSet(control.getSkeleton().getBoneCount()); + } + affectedBones.set(boneIndex); + } + + /** + * Add bones to be influenced by this animation channel starting from the + * given bone name and going toward the root bone. + */ + public void addToRootBone(String name) { + addToRootBone(control.getSkeleton().getBone(name)); + } + + /** + * Add bones to be influenced by this animation channel starting from the + * given bone and going toward the root bone. + */ + public void addToRootBone(Bone bone) { + addBone(bone); + while (bone.getParent() != null) { + bone = bone.getParent(); + addBone(bone); + } + } + + /** + * Add bones to be influenced by this animation channel, starting + * from the given named bone and going toward its children. + */ + public void addFromRootBone(String name) { + addFromRootBone(control.getSkeleton().getBone(name)); + } + + /** + * Add bones to be influenced by this animation channel, starting + * from the given bone and going toward its children. + */ + public void addFromRootBone(Bone bone) { + addBone(bone); + if (bone.getChildren() == null) + return; + for (Bone childBone : bone.getChildren()) { + addBone(childBone); + addFromRootBone(childBone); + } + } + + BitSet getAffectedBones(){ + return affectedBones; + } + + public void reset(boolean rewind){ + if(rewind){ + setTime(0); + if(control.getSkeleton()!=null){ + control.getSkeleton().resetAndUpdate(); + }else{ + TempVars vars = TempVars.get(); + update(0, vars); + vars.release(); + } + } + animation = null; + notified = false; + } + + void update(float tpf, TempVars vars) { + if (animation == null) + return; + + if (blendFrom != null && blendAmount != 1.0f){ + // The blendFrom anim is set, the actual animation + // playing will be set +// blendFrom.setTime(timeBlendFrom, 1f, control, this, vars); + blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars); + + timeBlendFrom += tpf * speedBlendFrom; + timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom, + blendFrom.getLength(), + loopModeBlendFrom); + if (timeBlendFrom < 0){ + timeBlendFrom = -timeBlendFrom; + speedBlendFrom = -speedBlendFrom; + } + + blendAmount += tpf * blendRate; + if (blendAmount > 1f){ + blendAmount = 1f; + blendFrom = null; + } + } + + animation.setTime(time, blendAmount, control, this, vars); + time += tpf * speed; + if (animation.getLength() > 0){ + if (!notified && (time >= animation.getLength() || time < 0)) { + if (loopMode == LoopMode.DontLoop) { + // Note that this flag has to be set before calling the notify + // since the notify may start a new animation and then unset + // the flag. + notified = true; + } + control.notifyAnimCycleDone(this, animation.getName()); + } + } + time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode); + if (time < 0){ + // Negative time indicates that speed should be inverted + // (for cycle loop mode only) + time = -time; + speed = -speed; + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java b/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java index 9272fe5e3..9cbe5d7dd 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java @@ -41,7 +41,9 @@ import static com.jme3.animation.LoopMode.Loop; */ public class AnimationUtils { - + public AnimationUtils(){ + + } /** * Clamps the time according to duration and loopMode * @param time diff --git a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java b/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java index 6e1432f4a..8a9e99b65 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java @@ -1,720 +1,706 @@ -/* - * Copyright (c) 2009-2012 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.animation.LoopMode; -import com.jme3.app.Application; -import com.jme3.app.state.AppState; -import com.jme3.app.state.AppStateManager; -import com.jme3.cinematic.events.AbstractCinematicEvent; -import com.jme3.cinematic.events.CinematicEvent; -import com.jme3.export.*; -import com.jme3.renderer.Camera; -import com.jme3.renderer.RenderManager; -import com.jme3.scene.CameraNode; -import com.jme3.scene.Node; -import com.jme3.scene.control.CameraControl; -import com.jme3.scene.control.CameraControl.ControlDirection; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * An appstate for composing and playing cut scenes in a game. The cinematic - * schedules CinematicEvents over a timeline. Once the Cinematic created it has - * to be attached to the stateManager. - * - * You can add various CinematicEvents to a Cinematic, see package - * com.jme3.cinematic.events - * - * Two main methods can be used to add an event : - * - * @see Cinematic#addCinematicEvent(float, - * com.jme3.cinematic.events.CinematicEvent) , that adds an event at the given - * time form the cinematic start. - * - * @see - * Cinematic#enqueueCinematicEvent(com.jme3.cinematic.events.CinematicEvent) - * that enqueue events one after the other according to their initialDuration - * - * a cinematic has convenient methods to handle the playback : - * @see Cinematic#play() - * @see Cinematic#pause() - * @see Cinematic#stop() - * - * A cinematic is itself a CinematicEvent, meaning you can embed several - * Cinematics Embed cinematics must not be added to the stateManager though. - * - * Cinematic has a way to handle several point of view by creating CameraNode - * over a cam and activating them on schedule. - * @see Cinematic#bindCamera(java.lang.String, com.jme3.renderer.Camera) - * @see Cinematic#activateCamera(float, java.lang.String) - * @see Cinematic#setActiveCamera(java.lang.String) - * - * @author Nehon - */ -public class Cinematic extends AbstractCinematicEvent implements AppState { - - private static final Logger logger = Logger.getLogger(Application.class.getName()); - private Node scene; - protected TimeLine timeLine = new TimeLine(); - private int lastFetchedKeyFrame = -1; - private List cinematicEvents = new ArrayList(); - private Map cameras = new HashMap(); - private CameraNode currentCam; - private boolean initialized = false; - private Map> eventsData; - private float nextEnqueue = 0; - - /** - * Used for serialization creates a cinematic, don't use this constructor - * directly - */ - public Cinematic() { - } - - /** - * creates a cinematic - * - * @param scene the scene in which the cinematic should take place - */ - public Cinematic(Node scene) { - this.scene = scene; - } - - /** - * creates a cinematic - * - * @param scene the scene in which the cinematic should take place - * @param initialDuration the duration of the cinematic (without considering - * the speed) - */ - public Cinematic(Node scene, float initialDuration) { - super(initialDuration); - this.scene = scene; - } - - /** - * creates a cinematic - * - * @param scene the scene in which the cinematic should take place - * @param loopMode tells if this cinematic should be looped or not - */ - public Cinematic(Node scene, LoopMode loopMode) { - super(loopMode); - this.scene = scene; - } - - /** - * creates a cinematic - * - * @param scene the scene in which the cinematic should take place - * @param initialDuration the duration of the cinematic (without considering - * the speed) - * @param loopMode tells if this cinematic should be looped or not - */ - public Cinematic(Node scene, float initialDuration, LoopMode loopMode) { - super(initialDuration, loopMode); - this.scene = scene; - } - - /** - * called internally - */ - @Override - public void onPlay() { - if (isInitialized()) { - if (playState == PlayState.Paused) { - for (int i = 0; i < cinematicEvents.size(); i++) { - CinematicEvent ce = cinematicEvents.get(i); - if (ce.getPlayState() == PlayState.Paused) { - ce.play(); - } - } - } - } - } - - /** - * called internally - */ - @Override - public void onStop() { - time = 0; - lastFetchedKeyFrame = -1; - for (int i = 0; i < cinematicEvents.size(); i++) { - CinematicEvent ce = cinematicEvents.get(i); - ce.setTime(0); - ce.forceStop(); - } - setEnableCurrentCam(false); - } - - /** - * called internally - */ - @Override - public void onPause() { - for (int i = 0; i < cinematicEvents.size(); i++) { - CinematicEvent ce = cinematicEvents.get(i); - if (ce.getPlayState() == PlayState.Playing) { - ce.pause(); - } - } - } - - /** - * used internally for serialization - * - * @param ex - * @throws IOException - */ - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - - oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null); - oc.writeStringSavableMap(cameras, "cameras", null); - oc.write(timeLine, "timeLine", null); - - } - - /** - * used internally for serialization - * - * @param im - * @throws IOException - */ - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - - cinematicEvents = ic.readSavableArrayList("cinematicEvents", null); - cameras = (Map) ic.readStringSavableMap("cameras", null); - timeLine = (TimeLine) ic.readSavable("timeLine", null); - } - - /** - * sets the speed of the cinematic. Note that it will set the speed of all - * events in the cinematic. 1 is normal speed. use 0.5f to make the - * cinematic twice slower, use 2 to make it twice faster - * - * @param speed the speed - */ - @Override - public void setSpeed(float speed) { - super.setSpeed(speed); - for (int i = 0; i < cinematicEvents.size(); i++) { - CinematicEvent ce = cinematicEvents.get(i); - ce.setSpeed(speed); - } - - - } - - /** - * used internally - * - * @param stateManager the state manager - * @param app the application - */ - public void initialize(AppStateManager stateManager, Application app) { - initEvent(app, this); - for (CinematicEvent cinematicEvent : cinematicEvents) { - cinematicEvent.initEvent(app, this); - } - - initialized = true; - } - - /** - * used internally - * - * @return - */ - public boolean isInitialized() { - return initialized; - } - - /** - * passing true has the same effect as play() you should use play(), - * pause(), stop() to handle the cinematic playing state. - * - * @param enabled true or false - */ - public void setEnabled(boolean enabled) { - if (enabled) { - play(); - } - } - - /** - * return true if the cinematic appstate is enabled (the cinematic is - * playing) - * - * @return true if enabled - */ - public boolean isEnabled() { - return playState == PlayState.Playing; - } - - /** - * called internally - * - * @param stateManager the state manager - */ - public void stateAttached(AppStateManager stateManager) { - } - - /** - * called internally - * - * @param stateManager the state manager - */ - public void stateDetached(AppStateManager stateManager) { - stop(); - } - - /** - * called internally don't call it directly. - * - * @param tpf - */ - public void update(float tpf) { - if (isInitialized()) { - internalUpdate(tpf); - } - } - - /** - * used internally, don't call this directly. - * - * @param tpf - */ - @Override - public void onUpdate(float tpf) { - int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time); - - //iterate to make sure every key frame is triggered - for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) { - KeyFrame keyFrame = timeLine.get(i); - if (keyFrame != null) { - keyFrame.trigger(); - } - } - - - for (int i = 0; i < cinematicEvents.size(); i++) { - CinematicEvent ce = cinematicEvents.get(i); - ce.internalUpdate(tpf); - } - - - lastFetchedKeyFrame = keyFrameIndex; - } - - /** - * This is used internally but can be called to shuffle through the - * cinematic. - * - * @param time the time to shuffle to. - */ - @Override - public void setTime(float time) { - - //stopping all events - onStop(); - super.setTime(time); - - int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time); - //triggering all the event from start to "time" - //then computing timeOffset for each event - for (int i = 0; i <= keyFrameIndex; i++) { - KeyFrame keyFrame = timeLine.get(i); - if (keyFrame != null) { - for (CinematicEvent ce : keyFrame.getCinematicEvents()) { - float t = this.time - timeLine.getKeyFrameTime(keyFrame); - if (t >= 0 && (t <= ce.getInitialDuration() || ce.getLoopMode() != LoopMode.DontLoop)) { - ce.play(); - } - ce.setTime(t); - } - } - } - lastFetchedKeyFrame = keyFrameIndex; - if (playState != PlayState.Playing) { - pause(); - } - } - - /** - * Adds a cinematic event to this cinematic at the given timestamp. This - * operation returns a keyFrame - * - * @param timeStamp the time when the event will start after the beginning of - * the cinematic - * @param cinematicEvent the cinematic event - * @return the keyFrame for that event. - */ - public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) { - KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp); - if (keyFrame == null) { - keyFrame = new KeyFrame(); - timeLine.addKeyFrameAtTime(timeStamp, keyFrame); - } - keyFrame.cinematicEvents.add(cinematicEvent); - cinematicEvents.add(cinematicEvent); - if (isInitialized()) { - cinematicEvent.initEvent(null, this); - } - return keyFrame; - } - - /** - * enqueue a cinematic event to a cinematic. This is a handy method when you - * want to chain event of a given duration without knowing their initial - * duration - * - * @param cinematicEvent the cinematic event to enqueue - * @return the timestamp the event was scheduled. - */ - public float enqueueCinematicEvent(CinematicEvent cinematicEvent) { - float scheduleTime = nextEnqueue; - addCinematicEvent(scheduleTime, cinematicEvent); - nextEnqueue += cinematicEvent.getInitialDuration(); - return scheduleTime; - } - - /** - * removes the first occurrence found of the given cinematicEvent. - * - * @param cinematicEvent the cinematicEvent to remove - * @return true if the element has been removed - */ - public boolean removeCinematicEvent(CinematicEvent cinematicEvent) { - cinematicEvent.dispose(); - cinematicEvents.remove(cinematicEvent); - for (KeyFrame keyFrame : timeLine.values()) { - if (keyFrame.cinematicEvents.remove(cinematicEvent)) { - return true; - } - } - return false; - } - - /** - * removes the first occurrence found of the given cinematicEvent for the - * given time stamp. - * - * @param timeStamp the timestamp when the cinematicEvent has been added - * @param cinematicEvent the cinematicEvent to remove - * @return true if the element has been removed - */ - public boolean removeCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) { - cinematicEvent.dispose(); - KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp); - return removeCinematicEvent(keyFrame, cinematicEvent); - } - - /** - * removes the first occurrence found of the given cinematicEvent for the - * given keyFrame - * - * @param keyFrame the keyFrame returned by the addCinematicEvent method. - * @param cinematicEvent the cinematicEvent to remove - * @return true if the element has been removed - */ - public boolean removeCinematicEvent(KeyFrame keyFrame, CinematicEvent cinematicEvent) { - cinematicEvent.dispose(); - boolean ret = keyFrame.cinematicEvents.remove(cinematicEvent); - cinematicEvents.remove(cinematicEvent); - if (keyFrame.isEmpty()) { - timeLine.removeKeyFrame(keyFrame.getIndex()); - } - return ret; - } - - /** - * called internally - * - * @see AppState#render(com.jme3.renderer.RenderManager) - */ - public void render(RenderManager rm) { - } - - /** - * called internally - * - * @see AppState#postRender() - */ - public void postRender() { - } - - /** - * called internally - * - * @see AppState#cleanup() - */ - public void cleanup() { - } - - /** - * fits the duration of the cinematic to the duration of all its child - * cinematic events - */ - public void fitDuration() { - KeyFrame kf = timeLine.getKeyFrameAtIndex(timeLine.getLastKeyFrameIndex()); - float d = 0; - for (int i = 0; i < kf.getCinematicEvents().size(); i++) { - CinematicEvent ce = kf.getCinematicEvents().get(i); - float dur = timeLine.getKeyFrameTime(kf) + ce.getDuration() * ce.getSpeed(); - if (d < dur) { - d = dur; - } - } - - initialDuration = d; - } - - /** - * Binds a camera to this cinematic, tagged by a unique name. This methods - * creates and returns a CameraNode for the cam and attach it to the scene. - * The control direction is set to SpatialToCamera. This camera Node can - * then be used in other events to handle the camera movements during the - * playback - * - * @param cameraName the unique tag the camera should have - * @param cam the scene camera. - * @return the created CameraNode. - */ - public CameraNode bindCamera(String cameraName, Camera cam) { - if (cameras.containsKey(cameraName)) { - throw new IllegalArgumentException("Camera " + cameraName + " is already binded to this cinematic"); - } - CameraNode node = new CameraNode(cameraName, cam); - node.setControlDir(ControlDirection.SpatialToCamera); - node.getControl(CameraControl.class).setEnabled(false); - cameras.put(cameraName, node); - scene.attachChild(node); - return node; - } - - /** - * returns a cameraNode given its name - * - * @param cameraName the camera name (as registered in - * Cinematic#bindCamera()) - * @return the cameraNode for this name - */ - public CameraNode getCamera(String cameraName) { - return cameras.get(cameraName); - } - - /** - * enable/disable the camera control of the cameraNode of the current cam - * - * @param enabled - */ - private void setEnableCurrentCam(boolean enabled) { - if (currentCam != null) { - currentCam.getControl(CameraControl.class).setEnabled(enabled); - } - } - - /** - * Sets the active camera instantly (use activateCamera if you want to - * schedule that event) - * - * @param cameraName the camera name (as registered in - * Cinematic#bindCamera()) - */ - public void setActiveCamera(String cameraName) { - setEnableCurrentCam(false); - currentCam = cameras.get(cameraName); - if (currentCam == null) { - logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName); - } - setEnableCurrentCam(true); - } - - /** - * schedule an event that will activate the camera at the given time - * - * @param timeStamp the time to activate the cam - * @param cameraName the camera name (as registered in - * Cinematic#bindCamera()) - */ - public void activateCamera(final float timeStamp, final String cameraName) { - addCinematicEvent(timeStamp, new AbstractCinematicEvent() { - @Override - public void play() { - super.play(); - stop(); - } - - @Override - public void onPlay() { - setActiveCamera(cameraName); - } - - @Override - public void onUpdate(float tpf) { - } - - @Override - public void onStop() { - } - - @Override - public void onPause() { - } - - @Override - public void forceStop() { - } - - @Override - public void setTime(float time) { - play(); - } - }); - } - - /** - * returns the complete eventdata map - * - * @return the eventdata map - */ - private Map> getEventsData() { - if (eventsData == null) { - eventsData = new HashMap>(); - } - return eventsData; - } - - /** - * used internally put an eventdata in the cinematic - * - * @param type the type of data - * @param key the key - * @param object the data - */ - public void putEventData(String type, Object key, Object object) { - Map> data = getEventsData(); - Map row = data.get(type); - if (row == null) { - row = new HashMap(); - } - row.put(key, object); - data.put(type, row); - } - - /** - * used internally return and event data - * - * @param type the type of data - * @param key the key - * @return - */ - public Object getEventData(String type, Object key) { - if (eventsData != null) { - Map row = eventsData.get(type); - if (row != null) { - return row.get(key); - } - } - return null; - } - - /** - * Used internally remove an eventData - * - * @param type the type of data - * @param key the key of the data - */ - public void removeEventData(String type, Object key) { - if (eventsData != null) { - Map row = eventsData.get(type); - if (row != null) { - row.remove(key); - } - } - } - - /** - * sets the scene to use for this cinematic it is expected that the scene is - * added before adding events to the cinematic - * - * @param scene the scene where the cinematic should take place. - */ - public void setScene(Node scene) { - this.scene = scene; - } - - /** - * return the scene where the cinematic occur - * - * @return the scene - */ - public Node getScene() { - return scene; - } - - /** - * clear the cinematic of its events. - */ - public void clear() { - dispose(); - cinematicEvents.clear(); - timeLine.clear(); - if (eventsData != null) { - eventsData.clear(); - } - } - - /** - * used internally to cleanup the cinematic. Called when the clear() method - * is called - */ - @Override - public void dispose() { - for (CinematicEvent event : cinematicEvents) { - event.dispose(); - } - } -} +/* + * Copyright (c) 2009-2012 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.animation.LoopMode; +import com.jme3.app.Application; +import com.jme3.app.state.AppState; +import com.jme3.app.state.AppStateManager; +import com.jme3.cinematic.events.AbstractCinematicEvent; +import com.jme3.cinematic.events.CameraEvent; +import com.jme3.cinematic.events.CinematicEvent; +import com.jme3.export.*; +import com.jme3.renderer.Camera; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.CameraNode; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.CameraControl; +import com.jme3.scene.control.CameraControl.ControlDirection; +import com.jme3.scene.control.Control; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An appstate for composing and playing cut scenes in a game. The cinematic + * schedules CinematicEvents over a timeline. Once the Cinematic created it has + * to be attached to the stateManager. + * + * You can add various CinematicEvents to a Cinematic, see package + * com.jme3.cinematic.events + * + * Two main methods can be used to add an event : + * + * @see Cinematic#addCinematicEvent(float, + * com.jme3.cinematic.events.CinematicEvent) , that adds an event at the given + * time form the cinematic start. + * + * @see + * Cinematic#enqueueCinematicEvent(com.jme3.cinematic.events.CinematicEvent) + * that enqueue events one after the other according to their initialDuration + * + * a cinematic has convenient methods to handle the playback : + * @see Cinematic#play() + * @see Cinematic#pause() + * @see Cinematic#stop() + * + * A cinematic is itself a CinematicEvent, meaning you can embed several + * Cinematics Embed cinematics must not be added to the stateManager though. + * + * Cinematic has a way to handle several point of view by creating CameraNode + * over a cam and activating them on schedule. + * @see Cinematic#bindCamera(java.lang.String, com.jme3.renderer.Camera) + * @see Cinematic#activateCamera(float, java.lang.String) + * @see Cinematic#setActiveCamera(java.lang.String) + * + * @author Nehon + */ +public class Cinematic extends AbstractCinematicEvent implements AppState { + + private static final Logger logger = Logger.getLogger(Cinematic.class.getName()); + private Node scene; + protected TimeLine timeLine = new TimeLine(); + private int lastFetchedKeyFrame = -1; + private List cinematicEvents = new ArrayList<>(); + private Map cameras = new HashMap<>(); + private CameraNode currentCam; + private boolean initialized = false; + private Map> eventsData; + private float nextEnqueue = 0; + + /** + * Used for serialization creates a cinematic, don't use this constructor + * directly + */ + public Cinematic() { + super(); + } + + public Cinematic(float initialDuration) { + super(initialDuration); + } + + public Cinematic(LoopMode loopMode) { + super(loopMode); + } + + public Cinematic(float initialDuration, LoopMode loopMode) { + super(initialDuration, loopMode); + } + + /** + * creates a cinematic + * + * @param scene the scene in which the cinematic should take place + */ + public Cinematic(Node scene) { + this.scene = scene; + } + + /** + * creates a cinematic + * + * @param scene the scene in which the cinematic should take place + * @param initialDuration the duration of the cinematic (without considering + * the speed) + */ + public Cinematic(Node scene, float initialDuration) { + super(initialDuration); + this.scene = scene; + } + + /** + * creates a cinematic + * + * @param scene the scene in which the cinematic should take place + * @param loopMode tells if this cinematic should be looped or not + */ + public Cinematic(Node scene, LoopMode loopMode) { + super(loopMode); + this.scene = scene; + } + + /** + * creates a cinematic + * + * @param scene the scene in which the cinematic should take place + * @param initialDuration the duration of the cinematic (without considering + * the speed) + * @param loopMode tells if this cinematic should be looped or not + */ + public Cinematic(Node scene, float initialDuration, LoopMode loopMode) { + super(initialDuration, loopMode); + this.scene = scene; + } + + /** + * called internally + */ + @Override + public void onPlay() { + if (isInitialized()) { + if (playState == PlayState.Paused) { + for (int i = 0; i < cinematicEvents.size(); i++) { + CinematicEvent ce = cinematicEvents.get(i); + if (ce.getPlayState() == PlayState.Paused) { + ce.play(); + } + } + } + } + } + + /** + * called internally + */ + @Override + public void onStop() { + time = 0; + lastFetchedKeyFrame = -1; + for (int i = 0; i < cinematicEvents.size(); i++) { + CinematicEvent ce = cinematicEvents.get(i); + ce.setTime(0); + ce.forceStop(); + } + setEnableCurrentCam(false); + } + + /** + * called internally + */ + @Override + public void onPause() { + for (int i = 0; i < cinematicEvents.size(); i++) { + CinematicEvent ce = cinematicEvents.get(i); + if (ce.getPlayState() == PlayState.Playing) { + ce.pause(); + } + } + } + + /** + * used internally for serialization + * + * @param ex + * @throws IOException + */ + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(cinematicEvents.toArray(new CinematicEvent[cinematicEvents.size()]), "cinematicEvents", null); + oc.writeStringSavableMap(cameras, "cameras", null); + oc.write(timeLine, "timeLine", null); + + } + + /** + * used internally for serialization + * + * @param im + * @throws IOException + */ + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + + Savable[] events = ic.readSavableArray("cinematicEvents", null); + for (Savable c : events) { +// addCinematicEvent(((CinematicEvent) c).getTime(), (CinematicEvent) c) + cinematicEvents.add((CinematicEvent) c); + } + cameras = (Map) ic.readStringSavableMap("cameras", null); + timeLine = (TimeLine) ic.readSavable("timeLine", null); + } + + /** + * sets the speed of the cinematic. Note that it will set the speed of all + * events in the cinematic. 1 is normal speed. use 0.5f to make the + * cinematic twice slower, use 2 to make it twice faster + * + * @param speed the speed + */ + @Override + public void setSpeed(float speed) { + super.setSpeed(speed); + for (int i = 0; i < cinematicEvents.size(); i++) { + CinematicEvent ce = cinematicEvents.get(i); + ce.setSpeed(speed); + } + + } + + /** + * used internally + * + * @param stateManager the state manager + * @param app the application + */ + public void initialize(AppStateManager stateManager, Application app) { + initEvent(app, this); + for (CinematicEvent cinematicEvent : cinematicEvents) { + cinematicEvent.initEvent(app, this); + } + + initialized = true; + } + + /** + * used internally + * + * @return + */ + public boolean isInitialized() { + return initialized; + } + + /** + * passing true has the same effect as play() you should use play(), + * pause(), stop() to handle the cinematic playing state. + * + * @param enabled true or false + */ + public void setEnabled(boolean enabled) { + if (enabled) { + play(); + } + } + + /** + * return true if the cinematic appstate is enabled (the cinematic is + * playing) + * + * @return true if enabled + */ + public boolean isEnabled() { + return playState == PlayState.Playing; + } + + /** + * called internally + * + * @param stateManager the state manager + */ + public void stateAttached(AppStateManager stateManager) { + } + + /** + * called internally + * + * @param stateManager the state manager + */ + public void stateDetached(AppStateManager stateManager) { + stop(); + } + + /** + * called internally don't call it directly. + * + * @param tpf + */ + @Override + public void update(float tpf) { + if (isInitialized()) { + internalUpdate(tpf); + } + } + + /** + * used internally, don't call this directly. + * + * @param tpf + */ + @Override + public void onUpdate(float tpf) { + int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time); + + //iterate to make sure every key frame is triggered + for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) { + KeyFrame keyFrame = timeLine.get(i); + if (keyFrame != null) { + keyFrame.trigger(); + } + } + + for (int i = 0; i < cinematicEvents.size(); i++) { + CinematicEvent ce = cinematicEvents.get(i); + ce.internalUpdate(tpf); + } + + lastFetchedKeyFrame = keyFrameIndex; + } + + /** + * This is used internally but can be called to shuffle through the + * cinematic. + * + * @param time the time to shuffle to. + */ + @Override + public void setTime(float time) { + + //stopping all events + onStop(); + super.setTime(time); + + int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time); + //triggering all the event from start to "time" + //then computing timeOffset for each event + for (int i = 0; i <= keyFrameIndex; i++) { + KeyFrame keyFrame = timeLine.get(i); + if (keyFrame != null) { + for (CinematicEvent ce : keyFrame.getCinematicEvents()) { + float t = this.time - timeLine.getKeyFrameTime(keyFrame); + if (t >= 0 && (t <= ce.getInitialDuration() || ce.getLoopMode() != LoopMode.DontLoop)) { + ce.play(); + } + ce.setTime(t); + } + } + } + lastFetchedKeyFrame = keyFrameIndex; + if (playState != PlayState.Playing) { + pause(); + } + } + + /** + * Adds a cinematic event to this cinematic at the given timestamp. This + * operation returns a keyFrame + * + * @param timeStamp the time when the event will start after the beginning + * of the cinematic + * @param cinematicEvent the cinematic event + * @return the keyFrame for that event. + */ + public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) { + KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp); + if (keyFrame == null) { + keyFrame = new KeyFrame(); + timeLine.addKeyFrameAtTime(timeStamp, keyFrame); + } + keyFrame.cinematicEvents.add(cinematicEvent); + cinematicEvents.add(cinematicEvent); + if (isInitialized()) { + cinematicEvent.initEvent(null, this); + } + return keyFrame; + } + + /** + * enqueue a cinematic event to a cinematic. This is a handy method when you + * want to chain event of a given duration without knowing their initial + * duration + * + * @param cinematicEvent the cinematic event to enqueue + * @return the timestamp the event was scheduled. + */ + public float enqueueCinematicEvent(CinematicEvent cinematicEvent) { + float scheduleTime = nextEnqueue; + addCinematicEvent(scheduleTime, cinematicEvent); + nextEnqueue += cinematicEvent.getInitialDuration(); + return scheduleTime; + } + + /** + * removes the first occurrence found of the given cinematicEvent. + * + * @param cinematicEvent the cinematicEvent to remove + * @return true if the element has been removed + */ + public boolean removeCinematicEvent(CinematicEvent cinematicEvent) { + cinematicEvent.dispose(); + cinematicEvents.remove(cinematicEvent); + for (KeyFrame keyFrame : timeLine.values()) { + if (keyFrame.cinematicEvents.remove(cinematicEvent)) { + return true; + } + } + return false; + } + + /** + * removes the first occurrence found of the given cinematicEvent for the + * given time stamp. + * + * @param timeStamp the timestamp when the cinematicEvent has been added + * @param cinematicEvent the cinematicEvent to remove + * @return true if the element has been removed + */ + public boolean removeCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) { + cinematicEvent.dispose(); + KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp); + return removeCinematicEvent(keyFrame, cinematicEvent); + } + + /** + * removes the first occurrence found of the given cinematicEvent for the + * given keyFrame + * + * @param keyFrame the keyFrame returned by the addCinematicEvent method. + * @param cinematicEvent the cinematicEvent to remove + * @return true if the element has been removed + */ + public boolean removeCinematicEvent(KeyFrame keyFrame, CinematicEvent cinematicEvent) { + cinematicEvent.dispose(); + boolean ret = keyFrame.cinematicEvents.remove(cinematicEvent); + cinematicEvents.remove(cinematicEvent); + if (keyFrame.isEmpty()) { + timeLine.removeKeyFrame(keyFrame.getIndex()); + } + return ret; + } + + /** + * called internally + * + * @see AppState#render(com.jme3.renderer.RenderManager) + */ + public void render(RenderManager rm) { + } + + /** + * called internally + * + * @see AppState#postRender() + */ + public void postRender() { + } + + /** + * called internally + * + * @see AppState#cleanup() + */ + public void cleanup() { + } + + /** + * fits the duration of the cinematic to the duration of all its child + * cinematic events + */ + public void fitDuration() { + KeyFrame kf = timeLine.getKeyFrameAtIndex(timeLine.getLastKeyFrameIndex()); + float d = 0; + for (int i = 0; i < kf.getCinematicEvents().size(); i++) { + CinematicEvent ce = kf.getCinematicEvents().get(i); + float dur = timeLine.getKeyFrameTime(kf) + ce.getDuration() * ce.getSpeed(); + if (d < dur) { + d = dur; + } + } + + initialDuration = d; + } + + /** + * Binds a camera to this cinematic, tagged by a unique name. This methods + * creates and returns a CameraNode for the cam and attach it to the scene. + * The control direction is set to SpatialToCamera. This camera Node can + * then be used in other events to handle the camera movements during the + * playback + * + * @param cameraName the unique tag the camera should have + * @param cam the scene camera. + * @return the created CameraNode. + */ + public CameraNode bindCamera(String cameraName, Camera cam) { + if (cameras.containsKey(cameraName)) { + throw new IllegalArgumentException("Camera " + cameraName + " is already binded to this cinematic"); + } + CameraNode node = new CameraNode(cameraName, cam); + node.setControlDir(ControlDirection.SpatialToCamera); + node.getControl(CameraControl.class).setEnabled(false); + cameras.put(cameraName, node); + scene.attachChild(node); + return node; + } + + /** + * returns a cameraNode given its name + * + * @param cameraName the camera name (as registered in + * Cinematic#bindCamera()) + * @return the cameraNode for this name + */ + public CameraNode getCamera(String cameraName) { + return cameras.get(cameraName); + } + + /** + * enable/disable the camera control of the cameraNode of the current cam + * + * @param enabled + */ + private void setEnableCurrentCam(boolean enabled) { + if (currentCam != null) { + currentCam.getControl(CameraControl.class).setEnabled(enabled); + } + } + + /** + * Sets the active camera instantly (use activateCamera if you want to + * schedule that event) + * + * @param cameraName the camera name (as registered in + * Cinematic#bindCamera()) + */ + public void setActiveCamera(String cameraName) { + setEnableCurrentCam(false); + currentCam = cameras.get(cameraName); + if (currentCam == null) { + logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName); + } + setEnableCurrentCam(true); + } + + /** + * schedule an event that will activate the camera at the given time + * + * @param timeStamp the time to activate the cam + * @param cameraName the camera name (as registered in + * Cinematic#bindCamera()) + */ + public void activateCamera(final float timeStamp, final String cameraName) { + addCinematicEvent(timeStamp, new CameraEvent(this, cameraName)); + } + + /** + * returns the complete eventdata map + * + * @return the eventdata map + */ + private Map> getEventsData() { + if (eventsData == null) { + eventsData = new HashMap>(); + } + return eventsData; + } + + /** + * used internally put an eventdata in the cinematic + * + * @param type the type of data + * @param key the key + * @param object the data + */ + public void putEventData(String type, Object key, Object object) { + Map> data = getEventsData(); + Map row = data.get(type); + if (row == null) { + row = new HashMap(); + } + row.put(key, object); + data.put(type, row); + } + + /** + * used internally return and event data + * + * @param type the type of data + * @param key the key + * @return + */ + public Object getEventData(String type, Object key) { + if (eventsData != null) { + Map row = eventsData.get(type); + if (row != null) { + return row.get(key); + } + } + return null; + } + + /** + * Used internally remove an eventData + * + * @param type the type of data + * @param key the key of the data + */ + public void removeEventData(String type, Object key) { + if (eventsData != null) { + Map row = eventsData.get(type); + if (row != null) { + row.remove(key); + } + } + } + + /** + * sets the scene to use for this cinematic it is expected that the scene is + * added before adding events to the cinematic + * + * @param scene the scene where the cinematic should take place. + */ + public void setScene(Node scene) { + this.scene = scene; + } + + /** + * return the scene where the cinematic occur + * + * @return the scene + */ + public Node getScene() { + return scene; + } + + /** + * clear the cinematic of its events. + */ + public void clear() { + dispose(); + cinematicEvents.clear(); + timeLine.clear(); + if (eventsData != null) { + eventsData.clear(); + } + } + + /** + * used internally to cleanup the cinematic. Called when the clear() method + * is called + */ + @Override + public void dispose() { + for (CinematicEvent event : cinematicEvents) { + event.dispose(); + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java b/jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java index a794e3cd0..458a482a9 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java @@ -1,89 +1,93 @@ -/* - * Copyright (c) 2009-2012 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.cinematic.events.CinematicEvent; -import com.jme3.export.*; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Nehon - */ -public class KeyFrame implements Savable { - - List cinematicEvents = new ArrayList(); - private int index; - - public List getCinematicEvents() { - return cinematicEvents; - } - - public void setCinematicEvents(List cinematicEvents) { - this.cinematicEvents = cinematicEvents; - } - - public List trigger() { - for (CinematicEvent event : cinematicEvents) { - event.play(); - } - return cinematicEvents; - } - - public boolean isEmpty(){ - return cinematicEvents.isEmpty(); - } - - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null); - oc.write(index, "index", 0); - } - - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - cinematicEvents = ic.readSavableArrayList("cinematicEvents", null); - index=ic.readInt("index", 0); - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - -} +/* + * Copyright (c) 2009-2012 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.cinematic.events.CinematicEvent; +import com.jme3.export.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Nehon + */ +public class KeyFrame implements Savable { + + public KeyFrame(){ + + } + + List cinematicEvents = new ArrayList<>(); + private int index; + + public List getCinematicEvents() { + return cinematicEvents; + } + + public void setCinematicEvents(List cinematicEvents) { + this.cinematicEvents = cinematicEvents; + } + + public List trigger() { + for (CinematicEvent event : cinematicEvents) { + event.play(); + } + return cinematicEvents; + } + + public boolean isEmpty(){ + return cinematicEvents.isEmpty(); + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null); + oc.write(index, "index", 0); + } + + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + cinematicEvents = ic.readSavableArrayList("cinematicEvents", null); + index=ic.readInt("index", 0); + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + +} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java index c77da581d..a3b8b7976 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java @@ -1,447 +1,448 @@ -/* - * Copyright (c) 2009-2012 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.events; - -import com.jme3.animation.AnimChannel; -import com.jme3.animation.AnimControl; -import com.jme3.animation.LoopMode; -import com.jme3.app.Application; -import com.jme3.cinematic.Cinematic; -import com.jme3.cinematic.PlayState; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.scene.Spatial; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Logger; - -/** - * An event based on an animation of a model. The model has to hold an - * AnimControl with valid animation (bone or spatial animations). - * - * It helps to schedule the playback of an animation on a model in a Cinematic. - * - * - * @author Nehon - */ -public class AnimationEvent extends AbstractCinematicEvent { - - // Version #2: directly keeping track on the model instead of trying to retrieve - //it from the scene according to its name, because the name is not supposed to be unique - //For backward compatibility, if the model is null it's looked up into the scene - public static final int SAVABLE_VERSION = 2; - private static final Logger log = Logger.getLogger(AnimationEvent.class.getName()); - public static final String MODEL_CHANNELS = "modelChannels"; - protected AnimChannel channel; - protected String animationName; - protected Spatial model; - //kept for backward compatibility - protected String modelName; - protected float blendTime = 0; - protected int channelIndex = 0; - // parent cinematic - protected Cinematic cinematic; - - /** - * used for serialization don't call directly use one of the following - * constructors - */ - public AnimationEvent() { - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - */ - public AnimationEvent(Spatial model, String animationName) { - this.model = model; - this.animationName = animationName; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration) { - super(initialDuration); - this.model = model; - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode) { - super(initialDuration, loopMode); - this.model = model; - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param blendTime the time during the animation are gonna be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, float blendTime) { - super(initialDuration); - this.model = model; - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - * @param blendTime the time during the animation are gonna be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, float blendTime) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - * @param blendTime the time during the animation are gonna be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, float blendTime) { - super(initialDuration, loopMode); - this.model = model; - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, int channelIndex) { - this.model = model; - this.animationName = animationName; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param channelIndex the index of the channel default is 0. Events on the - * @param blendTime the time during the animation are gonna be blended - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex, float blendTime) { - this.model = model; - this.animationName = animationName; - this.loopMode = loopMode; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.channelIndex = channelIndex; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, int channelIndex) { - super(initialDuration); - this.model = model; - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, int channelIndex) { - super(initialDuration, loopMode); - this.model = model; - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - @Override - public void initEvent(Application app, Cinematic cinematic) { - super.initEvent(app, cinematic); - this.cinematic = cinematic; - if (channel == null) { - Object s = cinematic.getEventData(MODEL_CHANNELS, model); - if (s == null) { - s = new HashMap(); - int numChannels = model.getControl(AnimControl.class).getNumChannels(); - for(int i = 0; i < numChannels; i++){ - ((HashMap)s).put(i, model.getControl(AnimControl.class).getChannel(i)); - } - cinematic.putEventData(MODEL_CHANNELS, model, s); - } - - Map map = (Map) s; - this.channel = map.get(channelIndex); - if (this.channel == null) { - if (model == null) { - //the model is null we try to find it according to the name - //this should occur only when loading an old saved cinematic - //othewise it's an error - model = cinematic.getScene().getChild(modelName); - } - if (model != null) { - channel = model.getControl(AnimControl.class).createChannel(); - map.put(channelIndex, channel); - } else { - //it's an error - throw new UnsupportedOperationException("model should not be null"); - } - } - - } - } - - @Override - public void setTime(float time) { - super.setTime(time); - if (!animationName.equals(channel.getAnimationName())) { - channel.setAnim(animationName, blendTime); - } - float t = time; - if (loopMode == loopMode.Loop) { - t = t % channel.getAnimMaxTime(); - } - if (loopMode == loopMode.Cycle) { - float parity = (float) Math.ceil(time / channel.getAnimMaxTime()); - if (parity > 0 && parity % 2 == 0) { - t = channel.getAnimMaxTime() - t % channel.getAnimMaxTime(); - } else { - t = t % channel.getAnimMaxTime(); - } - - } - if (t < 0) { - channel.setTime(0); - channel.reset(true); - } - if (t > channel.getAnimMaxTime()) { - channel.setTime(t); - channel.getControl().update(0); - stop(); - } else { - channel.setTime(t); - channel.getControl().update(0); - } - - } - - @Override - public void onPlay() { - channel.getControl().setEnabled(true); - if (playState == PlayState.Stopped) { - channel.setAnim(animationName, blendTime); - channel.setSpeed(speed); - channel.setLoopMode(loopMode); - channel.setTime(0); - } - } - - @Override - public void setSpeed(float speed) { - super.setSpeed(speed); - if (channel != null) { - channel.setSpeed(speed); - } - } - - @Override - public void onUpdate(float tpf) { - } - - @Override - public void onStop() { - } - - @Override - public void forceStop() { - if (channel != null) { - channel.setTime(time); - channel.reset(false); - } - super.forceStop(); - } - - @Override - public void onPause() { - if (channel != null) { - channel.getControl().setEnabled(false); - } - } - - @Override - public void setLoopMode(LoopMode loopMode) { - super.setLoopMode(loopMode); - if (channel != null) { - channel.setLoopMode(loopMode); - } - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - - oc.write(model, "model", null); - oc.write(animationName, "animationName", ""); - oc.write(blendTime, "blendTime", 0f); - oc.write(channelIndex, "channelIndex", 0); - - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - if (im.getFormatVersion() == 0) { - modelName = ic.readString("modelName", ""); - } - //FIXME always the same issue, because of the clonning of assets, this won't work - //we have to somehow store userdata in the spatial and then recurse the - //scene sub scenegraph to find the correct instance of the model - //This brings a reflaxion about the cinematic being an appstate, - //shouldn't it be a control over the scene - // this would allow to use the cloneForSpatial method and automatically - //rebind cloned references of original objects. - //for now as nobody probably ever saved a cinematic, this is not a critical issue - model = (Spatial) ic.readSavable("model", null); - animationName = ic.readString("animationName", ""); - blendTime = ic.readFloat("blendTime", 0f); - channelIndex = ic.readInt("channelIndex", 0); - } - - @Override - public void dispose() { - super.dispose(); - if (cinematic != null) { - Object o = cinematic.getEventData(MODEL_CHANNELS, model); - if (o != null) { - Collection values = ((HashMap) o).values(); - while (values.remove(channel)); - if (values.isEmpty()) { - cinematic.removeEventData(MODEL_CHANNELS, model); - } - } - cinematic = null; - channel = null; - } - } -} +/* + * Copyright (c) 2009-2012 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.events; + +import com.jme3.animation.AnimChannel; +import com.jme3.animation.AnimControl; +import com.jme3.animation.LoopMode; +import com.jme3.app.Application; +import com.jme3.cinematic.Cinematic; +import com.jme3.cinematic.PlayState; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.scene.Spatial; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * An event based on an animation of a model. The model has to hold an + * AnimControl with valid animation (bone or spatial animations). + * + * It helps to schedule the playback of an animation on a model in a Cinematic. + * + * + * @author Nehon + */ +public class AnimationEvent extends AbstractCinematicEvent { + + // Version #2: directly keeping track on the model instead of trying to retrieve + //it from the scene according to its name, because the name is not supposed to be unique + //For backward compatibility, if the model is null it's looked up into the scene + public static final int SAVABLE_VERSION = 2; + private static final Logger log = Logger.getLogger(AnimationEvent.class.getName()); + public static final String MODEL_CHANNELS = "modelChannels"; + protected AnimChannel channel; + protected String animationName; + protected Spatial model; + //kept for backward compatibility + protected String modelName; + protected float blendTime = 0; + protected int channelIndex = 0; + // parent cinematic + protected Cinematic cinematic; + + /** + * used for serialization don't call directly use one of the following + * constructors + */ + public AnimationEvent() { + super(); + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + */ + public AnimationEvent(Spatial model, String animationName) { + this.model = model; + this.animationName = animationName; + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration) { + super(initialDuration); + this.model = model; + this.animationName = animationName; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param loopMode the loopMode + * @see LoopMode + */ + public AnimationEvent(Spatial model, String animationName, LoopMode loopMode) { + super(loopMode); + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + this.model = model; + this.animationName = animationName; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + * @param loopMode the loopMode + * @see LoopMode + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode) { + super(initialDuration, loopMode); + this.model = model; + this.animationName = animationName; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + * @param blendTime the time during the animation are gonna be blended + * @see AnimChannel#setAnim(java.lang.String, float) + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration, float blendTime) { + super(initialDuration); + this.model = model; + this.animationName = animationName; + this.blendTime = blendTime; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param loopMode the loopMode + * @see LoopMode + * @param blendTime the time during the animation are gonna be blended + * @see AnimChannel#setAnim(java.lang.String, float) + */ + public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, float blendTime) { + super(loopMode); + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + this.model = model; + this.animationName = animationName; + this.blendTime = blendTime; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + * @param loopMode the loopMode + * @see LoopMode + * @param blendTime the time during the animation are gonna be blended + * @see AnimChannel#setAnim(java.lang.String, float) + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, float blendTime) { + super(initialDuration, loopMode); + this.model = model; + this.animationName = animationName; + this.blendTime = blendTime; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param loopMode the loopMode + * @see LoopMode + * @param channelIndex the index of the channel default is 0. Events on the + * same channelIndex will use the same channel. + */ + public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex) { + super(loopMode); + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + this.model = model; + this.animationName = animationName; + this.channelIndex = channelIndex; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param channelIndex the index of the channel default is 0. Events on the + * same channelIndex will use the same channel. + */ + public AnimationEvent(Spatial model, String animationName, int channelIndex) { + this.model = model; + this.animationName = animationName; + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + this.channelIndex = channelIndex; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param channelIndex the index of the channel default is 0. Events on the + * @param blendTime the time during the animation are gonna be blended + * same channelIndex will use the same channel. + */ + public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex, float blendTime) { + this.model = model; + this.animationName = animationName; + this.loopMode = loopMode; + initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); + this.channelIndex = channelIndex; + this.blendTime = blendTime; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + * @param channelIndex the index of the channel default is 0. Events on the + * same channelIndex will use the same channel. + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration, int channelIndex) { + super(initialDuration); + this.model = model; + this.animationName = animationName; + this.channelIndex = channelIndex; + } + + /** + * creates an animation event + * + * @param model the model on which the animation will be played + * @param animationName the name of the animation to play + * @param initialDuration the initial duration of the event + * @param loopMode the loopMode + * @see LoopMode + * @param channelIndex the index of the channel default is 0. Events on the + * same channelIndex will use the same channel. + */ + public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, int channelIndex) { + super(initialDuration, loopMode); + this.model = model; + this.animationName = animationName; + this.channelIndex = channelIndex; + } + + @Override + public void initEvent(Application app, Cinematic cinematic) { + super.initEvent(app, cinematic); + this.cinematic = cinematic; + if (channel == null) { + Object s = cinematic.getEventData(MODEL_CHANNELS, model); + if (s == null) { + s = new HashMap(); + int numChannels = model.getControl(AnimControl.class).getNumChannels(); + for(int i = 0; i < numChannels; i++){ + ((HashMap)s).put(i, model.getControl(AnimControl.class).getChannel(i)); + } + cinematic.putEventData(MODEL_CHANNELS, model, s); + } + + Map map = (Map) s; + this.channel = map.get(channelIndex); + if (this.channel == null) { + if (model == null) { + //the model is null we try to find it according to the name + //this should occur only when loading an old saved cinematic + //othewise it's an error + model = cinematic.getScene().getChild(modelName); + } + if (model != null) { + channel = model.getControl(AnimControl.class).createChannel(); + map.put(channelIndex, channel); + } else { + //it's an error + throw new UnsupportedOperationException("model should not be null"); + } + } + + } + } + + @Override + public void setTime(float time) { + super.setTime(time); + if (!animationName.equals(channel.getAnimationName())) { + channel.setAnim(animationName, blendTime); + } + float t = time; + if (loopMode == loopMode.Loop) { + t = t % channel.getAnimMaxTime(); + } + if (loopMode == loopMode.Cycle) { + float parity = (float) Math.ceil(time / channel.getAnimMaxTime()); + if (parity > 0 && parity % 2 == 0) { + t = channel.getAnimMaxTime() - t % channel.getAnimMaxTime(); + } else { + t = t % channel.getAnimMaxTime(); + } + + } + if (t < 0) { + channel.setTime(0); + channel.reset(true); + } + if (t > channel.getAnimMaxTime()) { + channel.setTime(t); + channel.getControl().update(0); + stop(); + } else { + channel.setTime(t); + channel.getControl().update(0); + } + + } + + @Override + public void onPlay() { + channel.getControl().setEnabled(true); + if (playState == PlayState.Stopped) { + channel.setAnim(animationName, blendTime); + channel.setSpeed(speed); + channel.setLoopMode(loopMode); + channel.setTime(0); + } + } + + @Override + public void setSpeed(float speed) { + super.setSpeed(speed); + if (channel != null) { + channel.setSpeed(speed); + } + } + + @Override + public void onUpdate(float tpf) { + } + + @Override + public void onStop() { + } + + @Override + public void forceStop() { + if (channel != null) { + channel.setTime(time); + channel.reset(false); + } + super.forceStop(); + } + + @Override + public void onPause() { + if (channel != null) { + channel.getControl().setEnabled(false); + } + } + + @Override + public void setLoopMode(LoopMode loopMode) { + super.setLoopMode(loopMode); + if (channel != null) { + channel.setLoopMode(loopMode); + } + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + + oc.write(model, "model", null); + oc.write(animationName, "animationName", ""); + oc.write(blendTime, "blendTime", 0f); + oc.write(channelIndex, "channelIndex", 0); + + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + if (im.getFormatVersion() == 0) { + modelName = ic.readString("modelName", ""); + } + //FIXME always the same issue, because of the clonning of assets, this won't work + //we have to somehow store userdata in the spatial and then recurse the + //scene sub scenegraph to find the correct instance of the model + //This brings a reflaxion about the cinematic being an appstate, + //shouldn't it be a control over the scene + // this would allow to use the cloneForSpatial method and automatically + //rebind cloned references of original objects. + //for now as nobody probably ever saved a cinematic, this is not a critical issue + model = (Spatial) ic.readSavable("model", null); + animationName = ic.readString("animationName", ""); + blendTime = ic.readFloat("blendTime", 0f); + channelIndex = ic.readInt("channelIndex", 0); + } + + @Override + public void dispose() { + super.dispose(); + if (cinematic != null) { + Object o = cinematic.getEventData(MODEL_CHANNELS, model); + if (o != null) { + Collection values = ((HashMap) o).values(); + while (values.remove(channel)); + if (values.isEmpty()) { + cinematic.removeEventData(MODEL_CHANNELS, model); + } + } + cinematic = null; + channel = null; + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java new file mode 100644 index 000000000..80d677e3c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2009-2017 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.events; + +import com.jme3.app.Application; +import com.jme3.cinematic.Cinematic; +import com.jme3.cinematic.TimeLine; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; +import com.jme3.scene.CameraNode; +import java.io.IOException; +import java.util.Map; + +/** + * + * @author Rickard + */ +public class CameraEvent extends AbstractCinematicEvent{ + + private String cameraName; + private Cinematic cinematic; + + public String getCameraName() { + return cameraName; + } + + public void setCameraName(String cameraName) { + this.cameraName = cameraName; + } + + public CameraEvent(){ + + } + + public CameraEvent(Cinematic parentEvent, String cameraName){ + this.cinematic = parentEvent; + this.cameraName = cameraName; + } + + @Override + public void initEvent(Application app, Cinematic cinematic) { + super.initEvent(app, cinematic); + this.cinematic = cinematic; + } + + @Override + public void play() { + super.play(); + stop(); + } + + @Override + public void onPlay() { + cinematic.setActiveCamera(cameraName); + } + + @Override + public void onUpdate(float tpf) { + } + + @Override + public void onStop() { + } + + @Override + public void onPause() { + } + + @Override + public void forceStop() { + } + + @Override + public void setTime(float time) { + play(); + } + + public Cinematic getCinematic() { + return cinematic; + } + + public void setCinematic(Cinematic cinematic) { + this.cinematic = cinematic; + } + + + + /** + * used internally for serialization + * + * @param ex + * @throws IOException + */ + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(cameraName, "cameraName", null); + + } + + /** + * used internally for serialization + * + * @param im + * @throws IOException + */ + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + cameraName = ic.readString("cameraName", null); + } +} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java index b94bd25d7..176c3cb67 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java @@ -1,491 +1,491 @@ -/* - * Copyright (c) 2009-2016 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.events; - -import com.jme3.animation.AnimationUtils; -import com.jme3.animation.LoopMode; -import com.jme3.app.Application; -import com.jme3.cinematic.Cinematic; -import com.jme3.cinematic.MotionPath; -import com.jme3.cinematic.PlayState; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Spatial; -import com.jme3.scene.control.Control; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; - -/** - * A MotionEvent is a control over the spatial that manages the position and direction of the spatial while following a motion Path. - * - * You must first create a MotionPath and then create a MotionEvent to associate a spatial and the path. - * - * @author Nehon - */ -public class MotionEvent extends AbstractCinematicEvent implements Control, JmeCloneable { - - protected Spatial spatial; - protected int currentWayPoint; - protected float currentValue; - protected Vector3f direction = new Vector3f(); - protected Vector3f lookAt = null; - protected Vector3f upVector = Vector3f.UNIT_Y; - protected Quaternion rotation = null; - protected Direction directionType = Direction.None; - protected MotionPath path; - private boolean isControl = true; - private int travelDirection = 1; - /** - * the distance traveled by the spatial on the path - */ - protected float traveledDistance = 0; - - /** - * Enum for the different type of target direction behavior. - */ - public enum Direction { - - /** - * The target stays in the starting direction. - */ - None, - /** - * The target rotates with the direction of the path. - */ - Path, - /** - * The target rotates with the direction of the path but with the addition of a rotation. - * You need to use the setRotation method when using this Direction. - */ - PathAndRotation, - /** - * The target rotates with the given rotation. - */ - Rotation, - /** - * The target looks at a point. - * You need to use the setLookAt method when using this direction. - */ - LookAt - } - - /** - * Create MotionEvent, - * when using this constructor don't forget to assign spatial and path. - */ - public MotionEvent() { - super(); - } - - /** - * Creates a MotionPath for the given spatial on the given motion path. - * @param spatial - * @param path - */ - public MotionEvent(Spatial spatial, MotionPath path) { - super(); - spatial.addControl(this); - this.path = path; - } - - /** - * Creates a MotionPath for the given spatial on the given motion path. - * @param spatial - * @param path - */ - public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) { - super(initialDuration); - spatial.addControl(this); - this.path = path; - } - - /** - * Creates a MotionPath for the given spatial on the given motion path. - * @param spatial - * @param path - */ - public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) { - super(); - spatial.addControl(this); - this.path = path; - this.loopMode = loopMode; - } - - /** - * Creates a MotionPath for the given spatial on the given motion path. - * @param spatial - * @param path - */ - public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) { - super(initialDuration); - spatial.addControl(this); - this.path = path; - this.loopMode = loopMode; - } - - public void update(float tpf) { - if (isControl) { - internalUpdate(tpf); - } - } - - @Override - public void internalUpdate(float tpf) { - if (playState == PlayState.Playing) { - time = time + (tpf * speed); - if (loopMode == LoopMode.Loop && time < 0) { - time = initialDuration; - } - if ((time >= initialDuration || time < 0) && loopMode == LoopMode.DontLoop) { - if (time >= initialDuration) { - path.triggerWayPointReach(path.getNbWayPoints() - 1, this); - } - stop(); - } else { - time = AnimationUtils.clampWrapTime(time, initialDuration, loopMode); - if(time<0){ - speed = - speed; - time = - time; - } - onUpdate(tpf); - } - } - } - - @Override - public void initEvent(Application app, Cinematic cinematic) { - super.initEvent(app, cinematic); - isControl = false; - } - - @Override - public void setTime(float time) { - super.setTime(time); - onUpdate(0); - } - - public void onUpdate(float tpf) { - traveledDistance = path.interpolatePath(time, this, tpf); - computeTargetDirection(); - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(lookAt, "lookAt", null); - oc.write(upVector, "upVector", Vector3f.UNIT_Y); - oc.write(rotation, "rotation", null); - oc.write(directionType, "directionType", Direction.None); - oc.write(path, "path", null); - oc.write(spatial, "spatial", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule in = im.getCapsule(this); - lookAt = (Vector3f) in.readSavable("lookAt", null); - upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y); - rotation = (Quaternion) in.readSavable("rotation", null); - directionType = in.readEnum("directionType", Direction.class, Direction.None); - path = (MotionPath) in.readSavable("path", null); - spatial = (Spatial) in.readSavable("spatial", null); - } - - /** - * This method is meant to be called by the motion path only. - * @return - */ - public boolean needsDirection() { - return directionType == Direction.Path || directionType == Direction.PathAndRotation; - } - - private void computeTargetDirection() { - switch (directionType) { - case Path: - Quaternion q = new Quaternion(); - q.lookAt(direction, upVector); - spatial.setLocalRotation(q); - break; - case LookAt: - if (lookAt != null) { - spatial.lookAt(lookAt, upVector); - } - break; - case PathAndRotation: - if (rotation != null) { - Quaternion q2 = new Quaternion(); - q2.lookAt(direction, upVector); - q2.multLocal(rotation); - spatial.setLocalRotation(q2); - } - break; - case Rotation: - if (rotation != null) { - spatial.setLocalRotation(rotation); - } - break; - case None: - break; - default: - break; - } - } - - /** - * Clone this control for the given spatial. - * @param spatial - * @return - */ - @Override - public Control cloneForSpatial(Spatial spatial) { - MotionEvent control = new MotionEvent(); - control.setPath(path); - control.playState = playState; - control.currentWayPoint = currentWayPoint; - control.currentValue = currentValue; - control.direction = direction.clone(); - control.lookAt = lookAt; - control.upVector = upVector.clone(); - control.rotation = rotation; - control.initialDuration = initialDuration; - control.speed = speed; - control.loopMode = loopMode; - control.directionType = directionType; - - return control; - } - - @Override - public Object jmeClone() { - MotionEvent control = new MotionEvent(); - control.path = path; - control.playState = playState; - control.currentWayPoint = currentWayPoint; - control.currentValue = currentValue; - control.direction = direction.clone(); - control.lookAt = lookAt; - control.upVector = upVector.clone(); - control.rotation = rotation; - control.initialDuration = initialDuration; - control.speed = speed; - control.loopMode = loopMode; - control.directionType = directionType; - control.spatial = spatial; - - return control; - } - - @Override - public void cloneFields( Cloner cloner, Object original ) { - this.spatial = cloner.clone(spatial); - } - - @Override - public void onPlay() { - traveledDistance = 0; - } - - @Override - public void onStop() { - currentWayPoint = 0; - } - - @Override - public void onPause() { - } - - /** - * This method is meant to be called by the motion path only. - * @return - */ - public float getCurrentValue() { - return currentValue; - } - - /** - * This method is meant to be called by the motion path only. - * - */ - public void setCurrentValue(float currentValue) { - this.currentValue = currentValue; - } - - /** - * This method is meant to be called by the motion path only. - * @return - */ - public int getCurrentWayPoint() { - return currentWayPoint; - } - - /** - * This method is meant to be called by the motion path only. - * - */ - public void setCurrentWayPoint(int currentWayPoint) { - this.currentWayPoint = currentWayPoint; - } - - /** - * Returns the direction the spatial is moving. - * @return - */ - public Vector3f getDirection() { - return direction; - } - - /** - * Sets the direction of the spatial, using the Y axis as the up vector. - * Use MotionEvent#setDirection((Vector3f direction,Vector3f upVector) if - * you want a custum up vector. - * This method is used by the motion path. - * @param direction - */ - public void setDirection(Vector3f direction) { - setDirection(direction, Vector3f.UNIT_Y); - } - - /** - * Sets the direction of the spatial with the given up vector. - * This method is used by the motion path. - * @param direction - * @param upVector the up vector to consider for this direction. - */ - public void setDirection(Vector3f direction,Vector3f upVector) { - this.direction.set(direction); - this.upVector.set(upVector); - } - - /** - * Returns the direction type of the target. - * @return the direction type. - */ - public Direction getDirectionType() { - return directionType; - } - - /** - * Sets the direction type of the target. - * On each update the direction given to the target can have different behavior. - * See the Direction Enum for explanations. - * @param directionType the direction type. - */ - public void setDirectionType(Direction directionType) { - this.directionType = directionType; - } - - /** - * Set the lookAt for the target. - * This can be used only if direction Type is Direction.LookAt. - * @param lookAt the position to look at. - * @param upVector the up vector. - */ - public void setLookAt(Vector3f lookAt, Vector3f upVector) { - this.lookAt = lookAt; - this.upVector = upVector; - } - - /** - * Returns the rotation of the target. - * @return the rotation quaternion. - */ - public Quaternion getRotation() { - return rotation; - } - - /** - * Sets the rotation of the target. - * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation. - * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion. - * With Rotation the rotation of the target will be set with the given Quaternion. - * @param rotation the rotation quaternion. - */ - public void setRotation(Quaternion rotation) { - this.rotation = rotation; - } - - /** - * Return the motion path this control follows. - * @return - */ - public MotionPath getPath() { - return path; - } - - /** - * Sets the motion path to follow. - * @param path - */ - public void setPath(MotionPath path) { - this.path = path; - } - - public void setEnabled(boolean enabled) { - if (enabled) { - play(); - } else { - pause(); - } - } - - public boolean isEnabled() { - return playState != PlayState.Stopped; - } - - public void render(RenderManager rm, ViewPort vp) { - } - - public void setSpatial(Spatial spatial) { - this.spatial = spatial; - } - - public Spatial getSpatial() { - return spatial; - } - - /** - * Return the distance traveled by the spatial on the path. - * @return - */ - public float getTraveledDistance() { - return traveledDistance; - } -} +/* + * Copyright (c) 2009-2016 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.events; + +import com.jme3.animation.AnimationUtils; +import com.jme3.animation.LoopMode; +import com.jme3.app.Application; +import com.jme3.cinematic.Cinematic; +import com.jme3.cinematic.MotionPath; +import com.jme3.cinematic.PlayState; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.Control; +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; +import java.io.IOException; + +/** + * A MotionEvent is a control over the spatial that manages the position and direction of the spatial while following a motion Path. + * + * You must first create a MotionPath and then create a MotionEvent to associate a spatial and the path. + * + * @author Nehon + */ +public class MotionEvent extends AbstractCinematicEvent implements Control, JmeCloneable { + + protected Spatial spatial; + protected int currentWayPoint; + protected float currentValue; + protected Vector3f direction = new Vector3f(); + protected Vector3f lookAt = null; + protected Vector3f upVector = Vector3f.UNIT_Y; + protected Quaternion rotation = null; + protected Direction directionType = Direction.None; + protected MotionPath path; + private boolean isControl = true; + private int travelDirection = 1; + /** + * the distance traveled by the spatial on the path + */ + protected float traveledDistance = 0; + + /** + * Enum for the different type of target direction behavior. + */ + public enum Direction { + + /** + * The target stays in the starting direction. + */ + None, + /** + * The target rotates with the direction of the path. + */ + Path, + /** + * The target rotates with the direction of the path but with the addition of a rotation. + * You need to use the setRotation method when using this Direction. + */ + PathAndRotation, + /** + * The target rotates with the given rotation. + */ + Rotation, + /** + * The target looks at a point. + * You need to use the setLookAt method when using this direction. + */ + LookAt + } + + /** + * Create MotionEvent, + * when using this constructor don't forget to assign spatial and path. + */ + public MotionEvent() { + super(); + } + + /** + * Creates a MotionPath for the given spatial on the given motion path. + * @param spatial + * @param path + */ + public MotionEvent(Spatial spatial, MotionPath path) { + super(); + spatial.addControl(this); + this.path = path; + } + + /** + * Creates a MotionPath for the given spatial on the given motion path. + * @param spatial + * @param path + */ + public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) { + super(initialDuration); + spatial.addControl(this); + this.path = path; + } + + /** + * Creates a MotionPath for the given spatial on the given motion path. + * @param spatial + * @param path + */ + public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) { + super(); + spatial.addControl(this); + this.path = path; + this.loopMode = loopMode; + } + + /** + * Creates a MotionPath for the given spatial on the given motion path. + * @param spatial + * @param path + */ + public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) { + super(initialDuration); + spatial.addControl(this); + this.path = path; + this.loopMode = loopMode; + } + + public void update(float tpf) { + if (isControl) { + internalUpdate(tpf); + } + } + + @Override + public void internalUpdate(float tpf) { + if (playState == PlayState.Playing) { + time = time + (tpf * speed); + if (loopMode == LoopMode.Loop && time < 0) { + time = initialDuration; + } + if ((time >= initialDuration || time < 0) && loopMode == LoopMode.DontLoop) { + if (time >= initialDuration) { + path.triggerWayPointReach(path.getNbWayPoints() - 1, this); + } + stop(); + } else { + time = AnimationUtils.clampWrapTime(time, initialDuration, loopMode); + if(time<0){ + speed = - speed; + time = - time; + } + onUpdate(tpf); + } + } + } + + @Override + public void initEvent(Application app, Cinematic cinematic) { + super.initEvent(app, cinematic); + isControl = false; + } + + @Override + public void setTime(float time) { + super.setTime(time); + onUpdate(0); + } + + public void onUpdate(float tpf) { + traveledDistance = path.interpolatePath(time, this, tpf); + computeTargetDirection(); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(lookAt, "lookAt", null); + oc.write(upVector, "upVector", Vector3f.UNIT_Y); + oc.write(rotation, "rotation", null); + oc.write(directionType, "directionType", Direction.None); + oc.write(path, "path", null); + oc.write(spatial, "spatial", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule in = im.getCapsule(this); + lookAt = (Vector3f) in.readSavable("lookAt", null); + upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y); + rotation = (Quaternion) in.readSavable("rotation", null); + directionType = in.readEnum("directionType", Direction.class, Direction.None); + path = (MotionPath) in.readSavable("path", null); + spatial = (Spatial) in.readSavable("spatial", null); + } + + /** + * This method is meant to be called by the motion path only. + * @return + */ + public boolean needsDirection() { + return directionType == Direction.Path || directionType == Direction.PathAndRotation; + } + + private void computeTargetDirection() { + switch (directionType) { + case Path: + Quaternion q = new Quaternion(); + q.lookAt(direction, upVector); + spatial.setLocalRotation(q); + break; + case LookAt: + if (lookAt != null) { + spatial.lookAt(lookAt, upVector); + } + break; + case PathAndRotation: + if (rotation != null) { + Quaternion q2 = new Quaternion(); + q2.lookAt(direction, upVector); + q2.multLocal(rotation); + spatial.setLocalRotation(q2); + } + break; + case Rotation: + if (rotation != null) { + spatial.setLocalRotation(rotation); + } + break; + case None: + break; + default: + break; + } + } + + /** + * Clone this control for the given spatial. + * @param spatial + * @return + */ + @Override + public Control cloneForSpatial(Spatial spatial) { + MotionEvent control = new MotionEvent(); + control.setPath(path); + control.playState = playState; + control.currentWayPoint = currentWayPoint; + control.currentValue = currentValue; + control.direction = direction.clone(); + control.lookAt = lookAt; + control.upVector = upVector.clone(); + control.rotation = rotation; + control.initialDuration = initialDuration; + control.speed = speed; + control.loopMode = loopMode; + control.directionType = directionType; + + return control; + } + + @Override + public Object jmeClone() { + MotionEvent control = new MotionEvent(); + control.path = path; + control.playState = playState; + control.currentWayPoint = currentWayPoint; + control.currentValue = currentValue; + control.direction = direction.clone(); + control.lookAt = lookAt; + control.upVector = upVector.clone(); + control.rotation = rotation; + control.initialDuration = initialDuration; + control.speed = speed; + control.loopMode = loopMode; + control.directionType = directionType; + control.spatial = spatial; + + return control; + } + + @Override + public void cloneFields( Cloner cloner, Object original ) { + this.spatial = cloner.clone(spatial); + } + + @Override + public void onPlay() { + traveledDistance = 0; + } + + @Override + public void onStop() { + currentWayPoint = 0; + } + + @Override + public void onPause() { + } + + /** + * This method is meant to be called by the motion path only. + * @return + */ + public float getCurrentValue() { + return currentValue; + } + + /** + * This method is meant to be called by the motion path only. + * + */ + public void setCurrentValue(float currentValue) { + this.currentValue = currentValue; + } + + /** + * This method is meant to be called by the motion path only. + * @return + */ + public int getCurrentWayPoint() { + return currentWayPoint; + } + + /** + * This method is meant to be called by the motion path only. + * + */ + public void setCurrentWayPoint(int currentWayPoint) { + this.currentWayPoint = currentWayPoint; + } + + /** + * Returns the direction the spatial is moving. + * @return + */ + public Vector3f getDirection() { + return direction; + } + + /** + * Sets the direction of the spatial, using the Y axis as the up vector. + * Use MotionEvent#setDirection((Vector3f direction,Vector3f upVector) if + * you want a custum up vector. + * This method is used by the motion path. + * @param direction + */ + public void setDirection(Vector3f direction) { + setDirection(direction, Vector3f.UNIT_Y); + } + + /** + * Sets the direction of the spatial with the given up vector. + * This method is used by the motion path. + * @param direction + * @param upVector the up vector to consider for this direction. + */ + public void setDirection(Vector3f direction,Vector3f upVector) { + this.direction.set(direction); + this.upVector.set(upVector); + } + + /** + * Returns the direction type of the target. + * @return the direction type. + */ + public Direction getDirectionType() { + return directionType; + } + + /** + * Sets the direction type of the target. + * On each update the direction given to the target can have different behavior. + * See the Direction Enum for explanations. + * @param directionType the direction type. + */ + public void setDirectionType(Direction directionType) { + this.directionType = directionType; + } + + /** + * Set the lookAt for the target. + * This can be used only if direction Type is Direction.LookAt. + * @param lookAt the position to look at. + * @param upVector the up vector. + */ + public void setLookAt(Vector3f lookAt, Vector3f upVector) { + this.lookAt = lookAt; + this.upVector = upVector; + } + + /** + * Returns the rotation of the target. + * @return the rotation quaternion. + */ + public Quaternion getRotation() { + return rotation; + } + + /** + * Sets the rotation of the target. + * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation. + * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion. + * With Rotation the rotation of the target will be set with the given Quaternion. + * @param rotation the rotation quaternion. + */ + public void setRotation(Quaternion rotation) { + this.rotation = rotation; + } + + /** + * Return the motion path this control follows. + * @return + */ + public MotionPath getPath() { + return path; + } + + /** + * Sets the motion path to follow. + * @param path + */ + public void setPath(MotionPath path) { + this.path = path; + } + + public void setEnabled(boolean enabled) { + if (enabled) { + play(); + } else { + pause(); + } + } + + public boolean isEnabled() { + return playState != PlayState.Stopped; + } + + public void render(RenderManager rm, ViewPort vp) { + } + + public void setSpatial(Spatial spatial) { + this.spatial = spatial; + } + + public Spatial getSpatial() { + return spatial; + } + + /** + * Return the distance traveled by the spatial on the path. + * @return + */ + public float getTraveledDistance() { + return traveledDistance; + } +} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java index 02c3ae754..6de7e21f6 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java @@ -1,229 +1,230 @@ -/* - * Copyright (c) 2009-2012 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.events; - -import com.jme3.animation.LoopMode; -import com.jme3.app.Application; -import com.jme3.audio.AudioNode; -import com.jme3.audio.AudioSource; -import com.jme3.cinematic.Cinematic; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import java.io.IOException; - -/** - * A sound track to be played in a cinematic. - * @author Nehon - */ -public class SoundEvent extends AbstractCinematicEvent { - - protected String path; - protected AudioNode audioNode; - protected boolean stream = false; - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - */ - public SoundEvent(String path) { - this.path = path; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param stream true to make the audio data streamed - */ - public SoundEvent(String path, boolean stream) { - this(path); - this.stream = stream; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param stream true to make the audio data streamed - * @param initialDuration the initial duration of the event - */ - public SoundEvent(String path, boolean stream, float initialDuration) { - super(initialDuration); - this.path = path; - this.stream = stream; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param stream true to make the audio data streamed - * @param loopMode the loopMode - * @see LoopMode - */ - public SoundEvent(String path, boolean stream, LoopMode loopMode) { - super(loopMode); - this.path = path; - this.stream = stream; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param stream true to make the audio data streamed - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - */ - public SoundEvent(String path, boolean stream, float initialDuration, LoopMode loopMode) { - super(initialDuration, loopMode); - this.path = path; - this.stream = stream; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param initialDuration the initial duration of the event - */ - public SoundEvent(String path, float initialDuration) { - super(initialDuration); - this.path = path; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param loopMode the loopMode - * @see LoopMode - */ - public SoundEvent(String path, LoopMode loopMode) { - super(loopMode); - this.path = path; - } - - /** - * creates a sound track from the given resource path - * @param path the path to an audio file (ie : "Sounds/mySound.wav") - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - */ - public SoundEvent(String path, float initialDuration, LoopMode loopMode) { - super(initialDuration, loopMode); - this.path = path; - } - - /** - * creates a sound event - * used for serialization - */ - public SoundEvent() { - } - - @Override - public void initEvent(Application app, Cinematic cinematic) { - super.initEvent(app, cinematic); - audioNode = new AudioNode(app.getAssetManager(), path, stream); - audioNode.setPositional(false); - setLoopMode(loopMode); - } - - @Override - public void setTime(float time) { - super.setTime(time); - //can occur on rewind - if (time < 0f) { - stop(); - }else{ - audioNode.setTimeOffset(time); - } - } - - @Override - public void onPlay() { - audioNode.play(); - } - - @Override - public void onStop() { - audioNode.stop(); - - } - - @Override - public void onPause() { - audioNode.pause(); - } - - @Override - public void onUpdate(float tpf) { - if (audioNode.getStatus() == AudioSource.Status.Stopped) { - stop(); - } - } - - /** - * Returns the underlying audio node of this sound track - * @return - */ - public AudioNode getAudioNode() { - return audioNode; - } - - @Override - public void setLoopMode(LoopMode loopMode) { - super.setLoopMode(loopMode); - - if (loopMode != LoopMode.DontLoop) { - audioNode.setLooping(true); - } else { - audioNode.setLooping(false); - } - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(path, "path", ""); - oc.write(stream, "stream", false); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - path = ic.readString("path", ""); - stream = ic.readBoolean("stream", false); - - } -} +/* + * Copyright (c) 2009-2012 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.events; + +import com.jme3.animation.LoopMode; +import com.jme3.app.Application; +import com.jme3.audio.AudioNode; +import com.jme3.audio.AudioSource; +import com.jme3.cinematic.Cinematic; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * A sound track to be played in a cinematic. + * @author Nehon + */ +public class SoundEvent extends AbstractCinematicEvent { + + protected String path; + protected AudioNode audioNode; + protected boolean stream = false; + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + */ + public SoundEvent(String path) { + this.path = path; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param stream true to make the audio data streamed + */ + public SoundEvent(String path, boolean stream) { + this(path); + this.stream = stream; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param stream true to make the audio data streamed + * @param initialDuration the initial duration of the event + */ + public SoundEvent(String path, boolean stream, float initialDuration) { + super(initialDuration); + this.path = path; + this.stream = stream; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param stream true to make the audio data streamed + * @param loopMode the loopMode + * @see LoopMode + */ + public SoundEvent(String path, boolean stream, LoopMode loopMode) { + super(loopMode); + this.path = path; + this.stream = stream; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param stream true to make the audio data streamed + * @param initialDuration the initial duration of the event + * @param loopMode the loopMode + * @see LoopMode + */ + public SoundEvent(String path, boolean stream, float initialDuration, LoopMode loopMode) { + super(initialDuration, loopMode); + this.path = path; + this.stream = stream; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param initialDuration the initial duration of the event + */ + public SoundEvent(String path, float initialDuration) { + super(initialDuration); + this.path = path; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param loopMode the loopMode + * @see LoopMode + */ + public SoundEvent(String path, LoopMode loopMode) { + super(loopMode); + this.path = path; + } + + /** + * creates a sound track from the given resource path + * @param path the path to an audio file (ie : "Sounds/mySound.wav") + * @param initialDuration the initial duration of the event + * @param loopMode the loopMode + * @see LoopMode + */ + public SoundEvent(String path, float initialDuration, LoopMode loopMode) { + super(initialDuration, loopMode); + this.path = path; + } + + /** + * creates a sound event + * used for serialization + */ + public SoundEvent() { + super(); + } + + @Override + public void initEvent(Application app, Cinematic cinematic) { + super.initEvent(app, cinematic); + audioNode = new AudioNode(app.getAssetManager(), path, stream); + audioNode.setPositional(false); + setLoopMode(loopMode); + } + + @Override + public void setTime(float time) { + super.setTime(time); + //can occur on rewind + if (time < 0f) { + stop(); + }else{ + audioNode.setTimeOffset(time); + } + } + + @Override + public void onPlay() { + audioNode.play(); + } + + @Override + public void onStop() { + audioNode.stop(); + + } + + @Override + public void onPause() { + audioNode.pause(); + } + + @Override + public void onUpdate(float tpf) { + if (audioNode.getStatus() == AudioSource.Status.Stopped) { + stop(); + } + } + + /** + * Returns the underlying audio node of this sound track + * @return + */ + public AudioNode getAudioNode() { + return audioNode; + } + + @Override + public void setLoopMode(LoopMode loopMode) { + super.setLoopMode(loopMode); + + if (loopMode != LoopMode.DontLoop) { + audioNode.setLooping(true); + } else { + audioNode.setLooping(false); + } + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(path, "path", ""); + oc.write(stream, "stream", false); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + path = ic.readString("path", ""); + stream = ic.readBoolean("stream", false); + + } +} diff --git a/jme3-core/src/main/java/com/jme3/scene/CameraNode.java b/jme3-core/src/main/java/com/jme3/scene/CameraNode.java index 44fed8208..220686f56 100644 --- a/jme3-core/src/main/java/com/jme3/scene/CameraNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/CameraNode.java @@ -1,122 +1,123 @@ -/* - * Copyright (c) 2009-2012 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.scene; - -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.renderer.Camera; -import com.jme3.scene.control.CameraControl; -import com.jme3.scene.control.CameraControl.ControlDirection; -import com.jme3.util.clone.Cloner; -import java.io.IOException; - -/** - * CameraNode simply uses {@link CameraControl} to implement - * linking of camera and node data. - * - * @author Tim8Dev - */ -public class CameraNode extends Node { - - private CameraControl camControl; - - /** - * Serialization only. Do not use. - */ - public CameraNode() { - } - - public CameraNode(String name, Camera camera) { - this(name, new CameraControl(camera)); - } - - public CameraNode(String name, CameraControl control) { - super(name); - addControl(control); - camControl = control; - } - - public void setEnabled(boolean enabled) { - camControl.setEnabled(enabled); - } - - public boolean isEnabled() { - return camControl.isEnabled(); - } - - public void setControlDir(ControlDirection controlDir) { - camControl.setControlDir(controlDir); - } - - public void setCamera(Camera camera) { - camControl.setCamera(camera); - } - - public ControlDirection getControlDir() { - return camControl.getControlDir(); - } - - public Camera getCamera() { - return camControl.getCamera(); - } - -// @Override -// public void lookAt(Vector3f position, Vector3f upVector) { -// this.lookAt(position, upVector); -// camControl.getCamera().lookAt(position, upVector); -// } - - /** - * Called internally by com.jme3.util.clone.Cloner. Do not call directly. - */ - @Override - public void cloneFields( Cloner cloner, Object original ) { - super.cloneFields(cloner, original); - - // A change in behavior... I think previously CameraNode was probably - // not really cloneable... or at least its camControl would be pointing - // to the wrong control. -pspeed - this.camControl = cloner.clone(camControl); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - camControl = (CameraControl)im.getCapsule(this).readSavable("camControl", null); - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - ex.getCapsule(this).write(camControl, "camControl", null); - } -} +/* + * Copyright (c) 2009-2012 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.scene; + +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.renderer.Camera; +import com.jme3.scene.control.CameraControl; +import com.jme3.scene.control.CameraControl.ControlDirection; +import com.jme3.util.clone.Cloner; +import java.io.IOException; + +/** + * CameraNode simply uses {@link CameraControl} to implement + * linking of camera and node data. + * + * @author Tim8Dev + */ +public class CameraNode extends Node { + + private CameraControl camControl; + + /** + * Serialization only. Do not use. + */ + public CameraNode() { + super(); + } + + public CameraNode(String name, Camera camera) { + this(name, new CameraControl(camera)); + } + + public CameraNode(String name, CameraControl control) { + super(name); + addControl(control); + camControl = control; + } + + public void setEnabled(boolean enabled) { + camControl.setEnabled(enabled); + } + + public boolean isEnabled() { + return camControl.isEnabled(); + } + + public void setControlDir(ControlDirection controlDir) { + camControl.setControlDir(controlDir); + } + + public void setCamera(Camera camera) { + camControl.setCamera(camera); + } + + public ControlDirection getControlDir() { + return camControl.getControlDir(); + } + + public Camera getCamera() { + return camControl.getCamera(); + } + +// @Override +// public void lookAt(Vector3f position, Vector3f upVector) { +// this.lookAt(position, upVector); +// camControl.getCamera().lookAt(position, upVector); +// } + + /** + * Called internally by com.jme3.util.clone.Cloner. Do not call directly. + */ + @Override + public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + + // A change in behavior... I think previously CameraNode was probably + // not really cloneable... or at least its camControl would be pointing + // to the wrong control. -pspeed + this.camControl = cloner.clone(camControl); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + camControl = (CameraControl)im.getCapsule(this).readSavable("camControl", null); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + ex.getCapsule(this).write(camControl, "camControl", null); + } +}