Merge pull request #2 from jMonkeyEngine/master
update fork from jMonkeyEngine to jmecnempirephoenix-patch-1
commit
189c8a5a6f
Binary file not shown.
@ -1,6 +1,6 @@ |
||||
#Fri Nov 25 13:05:50 EST 2016 |
||||
#Sun Sep 17 22:55:30 EDT 2017 |
||||
distributionBase=GRADLE_USER_HOME |
||||
distributionPath=wrapper/dists |
||||
zipStoreBase=GRADLE_USER_HOME |
||||
zipStorePath=wrapper/dists |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip |
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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; |
||||
|
||||
/** |
||||
* <code>AnimChannel</code> 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. |
||||
* <p> |
||||
* This resets the time to zero, and optionally blends the animation |
||||
* over <code>blendTime</code> 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. |
||||
* <p> |
||||
* 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; |
||||
|
||||
/** |
||||
* <code>AnimChannel</code> 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. |
||||
* <p> |
||||
* This resets the time to zero, and optionally blends the animation |
||||
* over <code>blendTime</code> 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. |
||||
* <p> |
||||
* 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; |
||||
} |
||||
} |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>(); |
||||
private int index; |
||||
|
||||
public List<CinematicEvent> getCinematicEvents() { |
||||
return cinematicEvents; |
||||
} |
||||
|
||||
public void setCinematicEvents(List<CinematicEvent> cinematicEvents) { |
||||
this.cinematicEvents = cinematicEvents; |
||||
} |
||||
|
||||
public List<CinematicEvent> 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<CinematicEvent> cinematicEvents = new ArrayList<>(); |
||||
private int index; |
||||
|
||||
public List<CinematicEvent> getCinematicEvents() { |
||||
return cinematicEvents; |
||||
} |
||||
|
||||
public void setCinematicEvents(List<CinematicEvent> cinematicEvents) { |
||||
this.cinematicEvents = cinematicEvents; |
||||
} |
||||
|
||||
public List<CinematicEvent> 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; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -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 <neph1 @ github> |
||||
*/ |
||||
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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
|
@ -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); |
||||
|
||||
} |
||||
} |
||||
|
@ -1,176 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 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.environment.generation; |
||||
|
||||
import com.jme3.environment.util.CubeMapWrapper; |
||||
import com.jme3.environment.util.EnvMapUtils; |
||||
import com.jme3.app.Application; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.texture.TextureCubeMap; |
||||
import static com.jme3.environment.util.EnvMapUtils.shBandFactor; |
||||
import com.jme3.util.BufferUtils; |
||||
import java.nio.ByteBuffer; |
||||
import java.util.concurrent.Callable; |
||||
|
||||
/** |
||||
* |
||||
* Generates the Irrafiance map for PBR. This job can be lauched from a separate |
||||
* thread. |
||||
* |
||||
* TODO there is a lot of duplicate code here with the EnvMapUtils. |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
//TODO there is a lot of duplicate code here with the EnvMapUtils. We should,
|
||||
//either leverage the code from the util class either remove it and only allow
|
||||
//parallel generation using this runnable.
|
||||
public class IrradianceMapGenerator extends RunnableWithProgress { |
||||
|
||||
private int targetMapSize; |
||||
private EnvMapUtils.FixSeamsMethod fixSeamsMethod; |
||||
private TextureCubeMap sourceMap; |
||||
private TextureCubeMap store; |
||||
private final Application app; |
||||
|
||||
/** |
||||
* Creates an Irradiance map generator. The app is needed to enqueue the |
||||
* call to the EnvironmentCamera when the generation is done, so that this |
||||
* process is thread safe. |
||||
* |
||||
* @param app the Application |
||||
* @param listener |
||||
*/ |
||||
public IrradianceMapGenerator(Application app, JobProgressListener<Integer> listener) { |
||||
super(listener); |
||||
this.app = app; |
||||
} |
||||
|
||||
/** |
||||
* Fills all the genration parameters |
||||
* |
||||
* @param sourceMap the source cube map |
||||
* @param targetMapSize the size of the generated map (width or height in |
||||
* pixel) |
||||
* @param fixSeamsMethod the method used to fix seams as described here |
||||
* {@link EnvMapUtils.FixSeamsMethod} |
||||
* |
||||
* @param store The cube map to store the result in. |
||||
*/ |
||||
public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { |
||||
this.sourceMap = sourceMap; |
||||
this.targetMapSize = targetMapSize; |
||||
this.fixSeamsMethod = fixSeamsMethod; |
||||
this.store = store; |
||||
reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
app.enqueue(new Callable<Void>() { |
||||
|
||||
@Override |
||||
public Void call() throws Exception { |
||||
listener.start(); |
||||
return null; |
||||
} |
||||
}); |
||||
try { |
||||
Vector3f[] shCoeffs = EnvMapUtils.getSphericalHarmonicsCoefficents(sourceMap); |
||||
store = generateIrradianceMap(shCoeffs, targetMapSize, fixSeamsMethod, store); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
app.enqueue(new Callable<Void>() { |
||||
|
||||
@Override |
||||
public Void call() throws Exception { |
||||
listener.done(6); |
||||
return null; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Generates the Irradiance map (used for image based difuse lighting) from |
||||
* Spherical Harmonics coefficients previously computed with |
||||
* {@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)} |
||||
* |
||||
* @param shCoeffs the SH coeffs |
||||
* @param targetMapSize the size of the irradiance map to generate |
||||
* @param fixSeamsMethod the method to fix seams |
||||
* @param store |
||||
* @return The irradiance cube map for the given coefficients |
||||
*/ |
||||
public TextureCubeMap generateIrradianceMap(Vector3f[] shCoeffs, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) { |
||||
TextureCubeMap irrCubeMap = store; |
||||
|
||||
setEnd(6 + 6); |
||||
for (int i = 0; i < 6; i++) { |
||||
ByteBuffer buf = BufferUtils.createByteBuffer(targetMapSize * targetMapSize * store.getImage().getFormat().getBitsPerPixel() / 8); |
||||
irrCubeMap.getImage().setData(i, buf); |
||||
progress(); |
||||
} |
||||
|
||||
Vector3f texelVect = new Vector3f(); |
||||
ColorRGBA color = new ColorRGBA(ColorRGBA.Black); |
||||
float[] shDir = new float[9]; |
||||
CubeMapWrapper envMapWriter = new CubeMapWrapper(irrCubeMap); |
||||
for (int face = 0; face < 6; face++) { |
||||
|
||||
for (int y = 0; y < targetMapSize; y++) { |
||||
for (int x = 0; x < targetMapSize; x++) { |
||||
EnvMapUtils.getVectorFromCubemapFaceTexCoord(x, y, targetMapSize, face, texelVect, fixSeamsMethod); |
||||
EnvMapUtils.evalShBasis(texelVect, shDir); |
||||
color.set(0, 0, 0, 0); |
||||
for (int i = 0; i < EnvMapUtils.NUM_SH_COEFFICIENT; i++) { |
||||
color.set(color.r + shCoeffs[i].x * shDir[i] * shBandFactor[i], |
||||
color.g + shCoeffs[i].y * shDir[i] * shBandFactor[i], |
||||
color.b + shCoeffs[i].z * shDir[i] * shBandFactor[i], |
||||
1.0f); |
||||
} |
||||
|
||||
//clamping the color because very low value close to zero produce artifacts
|
||||
color.r = Math.max(0.0001f, color.r); |
||||
color.g = Math.max(0.0001f, color.g); |
||||
color.b = Math.max(0.0001f, color.b); |
||||
|
||||
envMapWriter.setPixel(x, y, face, color); |
||||
|
||||
} |
||||
} |
||||
progress(); |
||||
} |
||||
return irrCubeMap; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,117 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 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.environment.generation; |
||||
|
||||
import com.jme3.app.Application; |
||||
import com.jme3.environment.util.CubeMapWrapper; |
||||
import com.jme3.environment.util.EnvMapUtils; |
||||
import com.jme3.light.LightProbe; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.texture.TextureCubeMap; |
||||
import com.jme3.util.BufferUtils; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.concurrent.Callable; |
||||
|
||||
import static com.jme3.environment.util.EnvMapUtils.shBandFactor; |
||||
|
||||
/** |
||||
* Generates the Irradiance map for PBR. This job can be launched from a separate |
||||
* thread. |
||||
* <p> |
||||
* This is not used as we use spherical harmonics directly now, but we may need this code again at some point |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class IrradianceSphericalHarmonicsGenerator extends RunnableWithProgress { |
||||
|
||||
private TextureCubeMap sourceMap; |
||||
private LightProbe store; |
||||
private final Application app; |
||||
|
||||
/** |
||||
* Creates an Irradiance map generator. The app is needed to enqueue the |
||||
* call to the EnvironmentCamera when the generation is done, so that this |
||||
* process is thread safe. |
||||
* |
||||
* @param app the Application |
||||
* @param listener |
||||
*/ |
||||
public IrradianceSphericalHarmonicsGenerator(Application app, JobProgressListener<Integer> listener) { |
||||
super(listener); |
||||
this.app = app; |
||||
} |
||||
|
||||
/** |
||||
* Fills all the genration parameters |
||||
* |
||||
* @param sourceMap the source cube map |
||||
* {@link EnvMapUtils.FixSeamsMethod} |
||||
* @param store The cube map to store the result in. |
||||
*/ |
||||
public void setGenerationParam(TextureCubeMap sourceMap, LightProbe store) { |
||||
this.sourceMap = sourceMap; |
||||
|
||||
this.store = store; |
||||
reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
app.enqueue(new Callable<Void>() { |
||||
|
||||
@Override |
||||
public Void call() throws Exception { |
||||
listener.start(); |
||||
return null; |
||||
} |
||||
}); |
||||
try { |
||||
Vector3f[] shCoeffs = EnvMapUtils.getSphericalHarmonicsCoefficents(sourceMap); |
||||
EnvMapUtils.prepareShCoefs(shCoeffs); |
||||
store.setShCoeffs(shCoeffs); |
||||
|
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
app.enqueue(new Callable<Void>() { |
||||
|
||||
@Override |
||||
public Void call() throws Exception { |
||||
listener.done(6); |
||||
return null; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
@ -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; |
||||
|
||||
/** |
||||
* <code>CameraNode</code> 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; |
||||
|
||||
/** |
||||
* <code>CameraNode</code> 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); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,417 @@ |
||||
/* |
||||
* 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. |
||||
*/ |
||||
// $Id: Cylinder.java 4131 2009-03-19 20:15:28Z blaine.dev $
|
||||
package com.jme3.scene.debug.custom; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.VertexBuffer.Type; |
||||
import com.jme3.scene.mesh.IndexBuffer; |
||||
import com.jme3.util.BufferUtils; |
||||
|
||||
import static com.jme3.util.BufferUtils.*; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.FloatBuffer; |
||||
|
||||
/** |
||||
* A simple cylinder, defined by it's height and radius. |
||||
* (Ported to jME3) |
||||
* |
||||
* @author Mark Powell |
||||
* @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ |
||||
*/ |
||||
public class BoneShape extends Mesh { |
||||
|
||||
private int axisSamples; |
||||
|
||||
private int radialSamples; |
||||
|
||||
private float radius; |
||||
private float radius2; |
||||
|
||||
private float height; |
||||
private boolean closed; |
||||
private boolean inverted; |
||||
|
||||
/** |
||||
* Default constructor for serialization only. Do not use. |
||||
*/ |
||||
public BoneShape() { |
||||
} |
||||
|
||||
/** |
||||
* Creates a new Cylinder. By default its center is the origin. Usually, a |
||||
* higher sample number creates a better looking cylinder, but at the cost |
||||
* of more vertex information. |
||||
* |
||||
* @param axisSamples Number of triangle samples along the axis. |
||||
* @param radialSamples Number of triangle samples along the radial. |
||||
* @param radius The radius of the cylinder. |
||||
* @param height The cylinder's height. |
||||
*/ |
||||
public BoneShape(int axisSamples, int radialSamples, |
||||
float radius, float height) { |
||||
this(axisSamples, radialSamples, radius, height, false); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new Cylinder. By default its center is the origin. Usually, a |
||||
* higher sample number creates a better looking cylinder, but at the cost |
||||
* of more vertex information. <br> |
||||
* If the cylinder is closed the texture is split into axisSamples parts: |
||||
* top most and bottom most part is used for top and bottom of the cylinder, |
||||
* rest of the texture for the cylinder wall. The middle of the top is |
||||
* mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need |
||||
* a suited distorted texture. |
||||
* |
||||
* @param axisSamples Number of triangle samples along the axis. |
||||
* @param radialSamples Number of triangle samples along the radial. |
||||
* @param radius The radius of the cylinder. |
||||
* @param height The cylinder's height. |
||||
* @param closed true to create a cylinder with top and bottom surface |
||||
*/ |
||||
public BoneShape(int axisSamples, int radialSamples, |
||||
float radius, float height, boolean closed) { |
||||
this(axisSamples, radialSamples, radius, height, closed, false); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new Cylinder. By default its center is the origin. Usually, a |
||||
* higher sample number creates a better looking cylinder, but at the cost |
||||
* of more vertex information. <br> |
||||
* If the cylinder is closed the texture is split into axisSamples parts: |
||||
* top most and bottom most part is used for top and bottom of the cylinder, |
||||
* rest of the texture for the cylinder wall. The middle of the top is |
||||
* mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need |
||||
* a suited distorted texture. |
||||
* |
||||
* @param axisSamples Number of triangle samples along the axis. |
||||
* @param radialSamples Number of triangle samples along the radial. |
||||
* @param radius The radius of the cylinder. |
||||
* @param height The cylinder's height. |
||||
* @param closed true to create a cylinder with top and bottom surface |
||||
* @param inverted true to create a cylinder that is meant to be viewed from the |
||||
* interior. |
||||
*/ |
||||
public BoneShape(int axisSamples, int radialSamples, |
||||
float radius, float height, boolean closed, boolean inverted) { |
||||
this(axisSamples, radialSamples, radius, radius, height, closed, inverted); |
||||
} |
||||
|
||||
public BoneShape(int axisSamples, int radialSamples, |
||||
float radius, float radius2, float height, boolean closed, boolean inverted) { |
||||
super(); |
||||
updateGeometry(axisSamples, radialSamples, radius, radius2, height, closed, inverted); |
||||
} |
||||
|
||||
/** |
||||
* @return the number of samples along the cylinder axis |
||||
*/ |
||||
public int getAxisSamples() { |
||||
return axisSamples; |
||||
} |
||||
|
||||
/** |
||||
* @return Returns the height. |
||||
*/ |
||||
public float getHeight() { |
||||
return height; |
||||
} |
||||
|
||||
/** |
||||
* @return number of samples around cylinder |
||||
*/ |
||||
public int getRadialSamples() { |
||||
return radialSamples; |
||||
} |
||||
|
||||
/** |
||||
* @return Returns the radius. |
||||
*/ |
||||
public float getRadius() { |
||||
return radius; |
||||
} |
||||
|
||||
public float getRadius2() { |
||||
return radius2; |
||||
} |
||||
|
||||
/** |
||||
* @return true if end caps are used. |
||||
*/ |
||||
public boolean isClosed() { |
||||
return closed; |
||||
} |
||||
|
||||
/** |
||||
* @return true if normals and uvs are created for interior use |
||||
*/ |
||||
public boolean isInverted() { |
||||
return inverted; |
||||
} |
||||
|
||||
/** |
||||
* Rebuilds the cylinder based on a new set of parameters. |
||||
* |
||||
* @param axisSamples the number of samples along the axis. |
||||
* @param radialSamples the number of samples around the radial. |
||||
* @param radius the radius of the bottom of the cylinder. |
||||
* @param radius2 the radius of the top of the cylinder. |
||||
* @param height the cylinder's height. |
||||
* @param closed should the cylinder have top and bottom surfaces. |
||||
* @param inverted is the cylinder is meant to be viewed from the inside. |
||||
*/ |
||||
public void updateGeometry(int axisSamples, int radialSamples, |
||||
float radius, float radius2, float height, boolean closed, boolean inverted) { |
||||
this.axisSamples = axisSamples + (closed ? 2 : 0); |
||||
this.radialSamples = radialSamples; |
||||
this.radius = radius; |
||||
this.radius2 = radius2; |
||||
this.height = height; |
||||
this.closed = closed; |
||||
this.inverted = inverted; |
||||
|
||||
// VertexBuffer pvb = getBuffer(Type.Position);
|
||||
// VertexBuffer nvb = getBuffer(Type.Normal);
|
||||
// VertexBuffer tvb = getBuffer(Type.TexCoord);
|
||||
|
||||
// Vertices
|
||||
int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); |
||||
|
||||
setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount)); |
||||
|
||||
// Normals
|
||||
setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount)); |
||||
|
||||
// Texture co-ordinates
|
||||
setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount)); |
||||
|
||||
int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples; |
||||
|
||||
setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount)); |
||||
|
||||
//Color
|
||||
setBuffer(Type.Color, 4, createFloatBuffer(vertCount * 4)); |
||||
|
||||
// generate geometry
|
||||
float inverseRadial = 1.0f / radialSamples; |
||||
float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1); |
||||
float inverseAxisLessTexture = 1.0f / (axisSamples - 1); |
||||
float halfHeight = 0.5f * height; |
||||
|
||||
// Generate points on the unit circle to be used in computing the mesh
|
||||
// points on a cylinder slice.
|
||||
float[] sin = new float[radialSamples + 1]; |
||||
float[] cos = new float[radialSamples + 1]; |
||||
|
||||
for (int radialCount = 0; radialCount < radialSamples; radialCount++) { |
||||
float angle = FastMath.TWO_PI * inverseRadial * radialCount; |
||||
cos[radialCount] = FastMath.cos(angle); |
||||
sin[radialCount] = FastMath.sin(angle); |
||||
} |
||||
sin[radialSamples] = sin[0]; |
||||
cos[radialSamples] = cos[0]; |
||||
|
||||
// calculate normals
|
||||
Vector3f[] vNormals = null; |
||||
Vector3f vNormal = Vector3f.UNIT_Z; |
||||
|
||||
if ((height != 0.0f) && (radius != radius2)) { |
||||
vNormals = new Vector3f[radialSamples]; |
||||
Vector3f vHeight = Vector3f.UNIT_Z.mult(height); |
||||
Vector3f vRadial = new Vector3f(); |
||||
|
||||
for (int radialCount = 0; radialCount < radialSamples; radialCount++) { |
||||
vRadial.set(cos[radialCount], sin[radialCount], 0.0f); |
||||
Vector3f vRadius = vRadial.mult(radius); |
||||
Vector3f vRadius2 = vRadial.mult(radius2); |
||||
Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius)); |
||||
Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z); |
||||
vNormals[radialCount] = vMantle.cross(vTangent).normalize(); |
||||
} |
||||
} |
||||
|
||||
FloatBuffer nb = getFloatBuffer(Type.Normal); |
||||
FloatBuffer pb = getFloatBuffer(Type.Position); |
||||
FloatBuffer tb = getFloatBuffer(Type.TexCoord); |
||||
FloatBuffer cb = getFloatBuffer(Type.Color); |
||||
|
||||
cb.rewind(); |
||||
for (int i = 0; i < vertCount; i++) { |
||||
cb.put(0.05f).put(0.05f).put(0.05f).put(1f); |
||||
} |
||||
|
||||
// generate the cylinder itself
|
||||
Vector3f tempNormal = new Vector3f(); |
||||
for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) { |
||||
float axisFraction; |
||||
float axisFractionTexture; |
||||
int topBottom = 0; |
||||
if (!closed) { |
||||
axisFraction = axisCount * inverseAxisLess; // in [0,1]
|
||||
axisFractionTexture = axisFraction; |
||||
} else { |
||||
if (axisCount == 0) { |
||||
topBottom = -1; // bottom
|
||||
axisFraction = 0; |
||||
axisFractionTexture = inverseAxisLessTexture; |
||||
} else if (axisCount == axisSamples - 1) { |
||||
topBottom = 1; // top
|
||||
axisFraction = 1; |
||||
axisFractionTexture = 1 - inverseAxisLessTexture; |
||||
} else { |
||||
axisFraction = (axisCount - 1) * inverseAxisLess; |
||||
axisFractionTexture = axisCount * inverseAxisLessTexture; |
||||
} |
||||
} |
||||
|
||||
// compute center of slice
|
||||
float z = height * axisFraction; |
||||
Vector3f sliceCenter = new Vector3f(0, 0, z); |
||||
|
||||
// compute slice vertices with duplication at end point
|
||||
int save = i; |
||||
for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) { |
||||
float radialFraction = radialCount * inverseRadial; // in [0,1)
|
||||
tempNormal.set(cos[radialCount], sin[radialCount], 0.0f); |
||||
|
||||
if (vNormals != null) { |
||||
vNormal = vNormals[radialCount]; |
||||
} else if (radius == radius2) { |
||||
vNormal = tempNormal; |
||||
} |
||||
|
||||
if (topBottom == 0) { |
||||
if (!inverted) |
||||
nb.put(vNormal.x).put(vNormal.y).put(vNormal.z); |
||||
else |
||||
nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z); |
||||
} else { |
||||
nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1)); |
||||
} |
||||
|
||||
tempNormal.multLocal((radius - radius2) * axisFraction + radius2) |
||||
.addLocal(sliceCenter); |
||||
pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z); |
||||
|
||||
tb.put((inverted ? 1 - radialFraction : radialFraction)) |
||||
.put(axisFractionTexture); |
||||
} |
||||
|
||||
BufferUtils.copyInternalVector3(pb, save, i); |
||||
BufferUtils.copyInternalVector3(nb, save, i); |
||||
|
||||
tb.put((inverted ? 0.0f : 1.0f)) |
||||
.put(axisFractionTexture); |
||||
} |
||||
|
||||
if (closed) { |
||||
pb.put(0).put(0).put(-halfHeight); // bottom center
|
||||
nb.put(0).put(0).put(-1 * (inverted ? -1 : 1)); |
||||
tb.put(0.5f).put(0); |
||||
pb.put(0).put(0).put(halfHeight); // top center
|
||||
nb.put(0).put(0).put(1 * (inverted ? -1 : 1)); |
||||
tb.put(0.5f).put(1); |
||||
} |
||||
|
||||
IndexBuffer ib = getIndexBuffer(); |
||||
int index = 0; |
||||
// Connectivity
|
||||
for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) { |
||||
int i0 = axisStart; |
||||
int i1 = i0 + 1; |
||||
axisStart += radialSamples + 1; |
||||
int i2 = axisStart; |
||||
int i3 = i2 + 1; |
||||
for (int i = 0; i < radialSamples; i++) { |
||||
if (closed && axisCount == 0) { |
||||
if (!inverted) { |
||||
ib.put(index++, i0++); |
||||
ib.put(index++, vertCount - 2); |
||||
ib.put(index++, i1++); |
||||
} else { |
||||
ib.put(index++, i0++); |
||||
ib.put(index++, i1++); |
||||
ib.put(index++, vertCount - 2); |
||||
} |
||||
} else if (closed && axisCount == axisSamples - 2) { |
||||
ib.put(index++, i2++); |
||||
ib.put(index++, inverted ? vertCount - 1 : i3++); |
||||
ib.put(index++, inverted ? i3++ : vertCount - 1); |
||||
} else { |
||||
ib.put(index++, i0++); |
||||
ib.put(index++, inverted ? i2 : i1); |
||||
ib.put(index++, inverted ? i1 : i2); |
||||
ib.put(index++, i1++); |
||||
ib.put(index++, inverted ? i2++ : i3++); |
||||
ib.put(index++, inverted ? i3++ : i2++); |
||||
} |
||||
} |
||||
} |
||||
|
||||
updateBound(); |
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter e) throws IOException { |
||||
super.read(e); |
||||
InputCapsule capsule = e.getCapsule(this); |
||||
axisSamples = capsule.readInt("axisSamples", 0); |
||||
radialSamples = capsule.readInt("radialSamples", 0); |
||||
radius = capsule.readFloat("radius", 0); |
||||
radius2 = capsule.readFloat("radius2", 0); |
||||
height = capsule.readFloat("height", 0); |
||||
closed = capsule.readBoolean("closed", false); |
||||
inverted = capsule.readBoolean("inverted", false); |
||||
} |
||||
|
||||
@Override |
||||
public void write(JmeExporter e) throws IOException { |
||||
super.write(e); |
||||
OutputCapsule capsule = e.getCapsule(this); |
||||
capsule.write(axisSamples, "axisSamples", 0); |
||||
capsule.write(radialSamples, "radialSamples", 0); |
||||
capsule.write(radius, "radius", 0); |
||||
capsule.write(radius2, "radius2", 0); |
||||
capsule.write(height, "height", 0); |
||||
capsule.write(closed, "closed", false); |
||||
capsule.write(inverted, "inverted", false); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,238 @@ |
||||
package com.jme3.scene.debug.custom; |
||||
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
import java.util.Map; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.bounding.*; |
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.VertexBuffer; |
||||
import com.jme3.scene.shape.Sphere; |
||||
|
||||
import static com.jme3.util.BufferUtils.createFloatBuffer; |
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.util.HashMap; |
||||
|
||||
/** |
||||
* The class that displays either wires between the bones' heads if no length |
||||
* data is supplied and full bones' shapes otherwise. |
||||
*/ |
||||
public class SkeletonBone extends Node { |
||||
|
||||
/** |
||||
* The skeleton to be displayed. |
||||
*/ |
||||
private Skeleton skeleton; |
||||
/** |
||||
* The map between the bone index and its length. |
||||
*/ |
||||
private Map<Bone, Node> boneNodes = new HashMap<Bone, Node>(); |
||||
private Map<Node, Bone> nodeBones = new HashMap<Node, Bone>(); |
||||
private Node selectedNode = null; |
||||
private boolean guessBonesOrientation = false; |
||||
|
||||
/** |
||||
* Creates a wire with bone lengths data. If the data is supplied then the |
||||
* wires will show each full bone (from head to tail). |
||||
* |
||||
* @param skeleton the skeleton that will be shown |
||||
* @param boneLengths a map between the bone's index and the bone's length |
||||
*/ |
||||
public SkeletonBone(Skeleton skeleton, Map<Integer, Float> boneLengths, boolean guessBonesOrientation) { |
||||
this.skeleton = skeleton; |
||||
this.skeleton.reset(); |
||||
this.skeleton.updateWorldVectors(); |
||||
this.guessBonesOrientation = guessBonesOrientation; |
||||
|
||||
BoneShape boneShape = new BoneShape(5, 12, 0.02f, 0.07f, 1f, false, false); |
||||
Sphere jointShape = new Sphere(10, 10, 0.1f); |
||||
jointShape.setBuffer(VertexBuffer.Type.Color, 4, createFloatBuffer(jointShape.getVertexCount() * 4)); |
||||
FloatBuffer cb = jointShape.getFloatBuffer(VertexBuffer.Type.Color); |
||||
|
||||
cb.rewind(); |
||||
for (int i = 0; i < jointShape.getVertexCount(); i++) { |
||||
cb.put(0.05f).put(0.05f).put(0.05f).put(1f); |
||||
} |
||||
|
||||
for (Bone bone : skeleton.getRoots()) { |
||||
createSkeletonGeoms(bone, boneShape, jointShape, boneLengths, skeleton, this, guessBonesOrientation); |
||||
} |
||||
this.updateModelBound(); |
||||
|
||||
|
||||
Sphere originShape = new Sphere(10, 10, 0.02f); |
||||
originShape.setBuffer(VertexBuffer.Type.Color, 4, createFloatBuffer(originShape.getVertexCount() * 4)); |
||||
cb = originShape.getFloatBuffer(VertexBuffer.Type.Color); |
||||
cb.rewind(); |
||||
for (int i = 0; i < jointShape.getVertexCount(); i++) { |
||||
cb.put(0.4f).put(0.4f).put(0.05f).put(1f); |
||||
} |
||||
|
||||
Geometry origin = new Geometry("origin", originShape); |
||||
BoundingVolume bv = this.getWorldBound(); |
||||
float scale = 1; |
||||
if (bv.getType() == BoundingVolume.Type.AABB) { |
||||
BoundingBox bb = (BoundingBox) bv; |
||||
scale = (bb.getXExtent() + bb.getYExtent() + bb.getZExtent()) / 3f; |
||||
} else if (bv.getType() == BoundingVolume.Type.Sphere) { |
||||
BoundingSphere bs = (BoundingSphere) bv; |
||||
scale = bs.getRadius(); |
||||
} |
||||
origin.scale(scale); |
||||
attachChild(origin); |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
protected final void createSkeletonGeoms(Bone bone, Mesh boneShape, Mesh jointShape, Map<Integer, Float> boneLengths, Skeleton skeleton, Node parent, boolean guessBonesOrientation) { |
||||
|
||||
if (guessBonesOrientation && bone.getName().equalsIgnoreCase("Site")) { |
||||
//BVH skeleton have a useless end point bone named Site
|
||||
return; |
||||
} |
||||
Node n = new Node(bone.getName() + "Node"); |
||||
Geometry bGeom = new Geometry(bone.getName(), boneShape); |
||||
Geometry jGeom = new Geometry(bone.getName() + "Joint", jointShape); |
||||
n.setLocalTranslation(bone.getLocalPosition()); |
||||
n.setLocalRotation(bone.getLocalRotation()); |
||||
|
||||
float boneLength = boneLengths.get(skeleton.getBoneIndex(bone)); |
||||
n.setLocalScale(bone.getLocalScale()); |
||||
|
||||
bGeom.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X).normalizeLocal()); |
||||
|
||||
if (guessBonesOrientation) { |
||||
//One child only, the bone direction is from the parent joint to the child joint.
|
||||
if (bone.getChildren().size() == 1) { |
||||
Vector3f v = bone.getChildren().get(0).getLocalPosition(); |
||||
Quaternion q = new Quaternion(); |
||||
q.lookAt(v, Vector3f.UNIT_Z); |
||||
bGeom.setLocalRotation(q); |
||||
boneLength = v.length(); |
||||
} |
||||
//no child, the bone has the same direction as the parent bone.
|
||||
if (bone.getChildren().isEmpty()) { |
||||
if (parent.getChildren().size() > 0) { |
||||
bGeom.setLocalRotation(parent.getChild(0).getLocalRotation()); |
||||
} else { |
||||
//no parent, let's use the bind orientation of the bone
|
||||
bGeom.setLocalRotation(bone.getBindRotation()); |
||||
} |
||||
} |
||||
} |
||||
bGeom.setLocalScale(boneLength); |
||||
jGeom.setLocalScale(boneLength); |
||||
|
||||
n.attachChild(bGeom); |
||||
n.attachChild(jGeom); |
||||
|
||||
//tip
|
||||
if (bone.getChildren().size() != 1) { |
||||
Geometry gt = jGeom.clone(); |
||||
gt.scale(0.8f); |
||||
Vector3f v = new Vector3f(0, boneLength, 0); |
||||
if (guessBonesOrientation) { |
||||
if (bone.getChildren().isEmpty()) { |
||||
if (parent.getChildren().size() > 0) { |
||||
gt.setLocalTranslation(bGeom.getLocalRotation().mult(parent.getChild(0).getLocalRotation()).mult(v, v)); |
||||
} else { |
||||
gt.setLocalTranslation(bGeom.getLocalRotation().mult(bone.getBindRotation()).mult(v, v)); |
||||
} |
||||
} |
||||
} else { |
||||
gt.setLocalTranslation(v); |
||||
} |
||||
|
||||
n.attachChild(gt); |
||||
} |
||||
|
||||
|
||||
boneNodes.put(bone, n); |
||||
nodeBones.put(n, bone); |
||||
parent.attachChild(n); |
||||
for (Bone childBone : bone.getChildren()) { |
||||
createSkeletonGeoms(childBone, boneShape, jointShape, boneLengths, skeleton, n, guessBonesOrientation); |
||||
} |
||||
} |
||||
|
||||
protected Bone select(Geometry g) { |
||||
Node parentNode = g.getParent(); |
||||
|
||||
if (parent != null) { |
||||
Bone b = nodeBones.get(parentNode); |
||||
if (b != null) { |
||||
selectedNode = parentNode; |
||||
} |
||||
return b; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
protected Node getSelectedNode() { |
||||
return selectedNode; |
||||
} |
||||
|
||||
|
||||
protected final void updateSkeletonGeoms(Bone bone) { |
||||
if (guessBonesOrientation && bone.getName().equalsIgnoreCase("Site")) { |
||||
return; |
||||
} |
||||
Node n = boneNodes.get(bone); |
||||
n.setLocalTranslation(bone.getLocalPosition()); |
||||
n.setLocalRotation(bone.getLocalRotation()); |
||||
n.setLocalScale(bone.getLocalScale()); |
||||
|
||||
for (Bone childBone : bone.getChildren()) { |
||||
updateSkeletonGeoms(childBone); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* The method updates the geometry according to the positions of the bones. |
||||
*/ |
||||
public void updateGeometry() { |
||||
|
||||
for (Bone bone : skeleton.getRoots()) { |
||||
updateSkeletonGeoms(bone); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,156 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package com.jme3.scene.debug.custom; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.animation.SkeletonControl; |
||||
import com.jme3.app.Application; |
||||
import com.jme3.app.state.AbstractAppState; |
||||
import com.jme3.app.state.AppStateManager; |
||||
import com.jme3.collision.CollisionResults; |
||||
import com.jme3.input.MouseInput; |
||||
import com.jme3.input.controls.ActionListener; |
||||
import com.jme3.input.controls.MouseButtonTrigger; |
||||
import com.jme3.math.Ray; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author Nehon |
||||
*/ |
||||
public class SkeletonDebugAppState extends AbstractAppState { |
||||
|
||||
private Node debugNode = new Node("debugNode"); |
||||
private Map<Skeleton, SkeletonDebugger> skeletons = new HashMap<Skeleton, SkeletonDebugger>(); |
||||
private Map<Skeleton, Bone> selectedBones = new HashMap<Skeleton, Bone>(); |
||||
private Application app; |
||||
|
||||
@Override |
||||
public void initialize(AppStateManager stateManager, Application app) { |
||||
ViewPort vp = app.getRenderManager().createMainView("debug", app.getCamera()); |
||||
vp.attachScene(debugNode); |
||||
vp.setClearDepth(true); |
||||
this.app = app; |
||||
for (SkeletonDebugger skeletonDebugger : skeletons.values()) { |
||||
skeletonDebugger.initialize(app.getAssetManager()); |
||||
} |
||||
app.getInputManager().addListener(actionListener, "shoot"); |
||||
app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); |
||||
super.initialize(stateManager, app); |
||||
} |
||||
|
||||
@Override |
||||
public void update(float tpf) { |
||||
debugNode.updateLogicalState(tpf); |
||||
debugNode.updateGeometricState(); |
||||
} |
||||
|
||||
public SkeletonDebugger addSkeleton(SkeletonControl skeletonControl, boolean guessBonesOrientation) { |
||||
Skeleton skeleton = skeletonControl.getSkeleton(); |
||||
Spatial forSpatial = skeletonControl.getSpatial(); |
||||
return addSkeleton(skeleton, forSpatial, guessBonesOrientation); |
||||
} |
||||
|
||||
public SkeletonDebugger addSkeleton(Skeleton skeleton, Spatial forSpatial, boolean guessBonesOrientation) { |
||||
|
||||
SkeletonDebugger sd = new SkeletonDebugger(forSpatial.getName() + "_Skeleton", skeleton, guessBonesOrientation); |
||||
sd.setLocalTransform(forSpatial.getWorldTransform()); |
||||
if (forSpatial instanceof Node) { |
||||
List<Geometry> geoms = new ArrayList<>(); |
||||
findGeoms((Node) forSpatial, geoms); |
||||
if (geoms.size() == 1) { |
||||
sd.setLocalTransform(geoms.get(0).getWorldTransform()); |
||||
} |
||||
} |
||||
skeletons.put(skeleton, sd); |
||||
debugNode.attachChild(sd); |
||||
if (isInitialized()) { |
||||
sd.initialize(app.getAssetManager()); |
||||
} |
||||
return sd; |
||||
} |
||||
|
||||
private void findGeoms(Node node, List<Geometry> geoms) { |
||||
for (Spatial spatial : node.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
geoms.add((Geometry) spatial); |
||||
} else if (spatial instanceof Node) { |
||||
findGeoms((Node) spatial, geoms); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Pick a Target Using the Mouse Pointer. <ol><li>Map "pick target" action |
||||
* to a MouseButtonTrigger. <li>flyCam.setEnabled(false); |
||||
* <li>inputManager.setCursorVisible(true); <li>Implement action in |
||||
* AnalogListener (TODO).</ol> |
||||
*/ |
||||
private ActionListener actionListener = new ActionListener() { |
||||
public void onAction(String name, boolean isPressed, float tpf) { |
||||
if (name.equals("shoot") && isPressed) { |
||||
CollisionResults results = new CollisionResults(); |
||||
Vector2f click2d = app.getInputManager().getCursorPosition(); |
||||
Vector3f click3d = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone(); |
||||
Vector3f dir = app.getCamera().getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d); |
||||
Ray ray = new Ray(click3d, dir); |
||||
|
||||
debugNode.collideWith(ray, results); |
||||
|
||||
if (results.size() > 0) { |
||||
// The closest result is the target that the player picked:
|
||||
Geometry target = results.getClosestCollision().getGeometry(); |
||||
for (SkeletonDebugger skeleton : skeletons.values()) { |
||||
Bone selectedBone = skeleton.select(target); |
||||
if (selectedBone != null) { |
||||
selectedBones.put(skeleton.getSkeleton(), selectedBone); |
||||
System.err.println("-----------------------"); |
||||
System.err.println("Selected Bone : " + selectedBone.getName() + " in skeleton " + skeleton.getName()); |
||||
System.err.println("Root Bone : " + (selectedBone.getParent() == null)); |
||||
System.err.println("-----------------------"); |
||||
System.err.println("Bind translation: " + selectedBone.getBindPosition()); |
||||
System.err.println("Bind rotation: " + selectedBone.getBindRotation()); |
||||
System.err.println("Bind scale: " + selectedBone.getBindScale()); |
||||
System.err.println("---"); |
||||
System.err.println("Local translation: " + selectedBone.getLocalPosition()); |
||||
System.err.println("Local rotation: " + selectedBone.getLocalRotation()); |
||||
System.err.println("Local scale: " + selectedBone.getLocalScale()); |
||||
System.err.println("---"); |
||||
System.err.println("Model translation: " + selectedBone.getModelSpacePosition()); |
||||
System.err.println("Model rotation: " + selectedBone.getModelSpaceRotation()); |
||||
System.err.println("Model scale: " + selectedBone.getModelSpaceScale()); |
||||
System.err.println("---"); |
||||
System.err.println("Bind inverse Transform: "); |
||||
System.err.println(selectedBone.getBindInverseTransform()); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
public Map<Skeleton, Bone> getSelectedBones() { |
||||
return selectedBones; |
||||
} |
||||
|
||||
public Node getDebugNode() { |
||||
return debugNode; |
||||
} |
||||
|
||||
public void setDebugNode(Node debugNode) { |
||||
this.debugNode = debugNode; |
||||
} |
||||
} |
@ -0,0 +1,218 @@ |
||||
package com.jme3.scene.debug.custom; |
||||
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
import com.jme3.animation.Bone; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.scene.BatchNode; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer; |
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* The class that creates a mesh to display how bones behave. If it is supplied |
||||
* with the bones' lengths it will show exactly how the bones look like on the |
||||
* scene. If not then only connections between each bone heads will be shown. |
||||
*/ |
||||
public class SkeletonDebugger extends BatchNode { |
||||
|
||||
/** |
||||
* The lines of the bones or the wires between their heads. |
||||
*/ |
||||
private SkeletonBone bones; |
||||
|
||||
private Skeleton skeleton; |
||||
/** |
||||
* The dotted lines between a bone's tail and the had of its children. Not |
||||
* available if the length data was not provided. |
||||
*/ |
||||
private SkeletonInterBoneWire interBoneWires; |
||||
private List<Bone> selectedBones = new ArrayList<Bone>(); |
||||
|
||||
public SkeletonDebugger() { |
||||
} |
||||
|
||||
/** |
||||
* Creates a debugger with no length data. The wires will be a connection |
||||
* between the bones' heads only. The points will show the bones' heads only |
||||
* and no dotted line of inter bones connection will be visible. |
||||
* |
||||
* @param name the name of the debugger's node |
||||
* @param skeleton the skeleton that will be shown |
||||
*/ |
||||
public SkeletonDebugger(String name, Skeleton skeleton, boolean guessBonesOrientation) { |
||||
super(name); |
||||
this.skeleton = skeleton; |
||||
skeleton.reset(); |
||||
skeleton.updateWorldVectors(); |
||||
Map<Integer, Float> boneLengths = new HashMap<Integer, Float>(); |
||||
|
||||
for (Bone bone : skeleton.getRoots()) { |
||||
computeLength(bone, boneLengths, skeleton); |
||||
} |
||||
|
||||
bones = new SkeletonBone(skeleton, boneLengths, guessBonesOrientation); |
||||
|
||||
this.attachChild(bones); |
||||
|
||||
interBoneWires = new SkeletonInterBoneWire(skeleton, boneLengths, guessBonesOrientation); |
||||
Geometry g = new Geometry(name + "_interwires", interBoneWires); |
||||
g.setBatchHint(BatchHint.Never); |
||||
this.attachChild(g); |
||||
} |
||||
|
||||
protected void initialize(AssetManager assetManager) { |
||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||
mat.setColor("Color", new ColorRGBA(0.05f, 0.05f, 0.05f, 1.0f));//new ColorRGBA(0.1f, 0.1f, 0.1f, 1.0f)
|
||||
setMaterial(mat); |
||||
Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||
mat2.setBoolean("VertexColor", true); |
||||
bones.setMaterial(mat2); |
||||
batch(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public final void setMaterial(Material material) { |
||||
if (batches.isEmpty()) { |
||||
for (int i = 0; i < children.size(); i++) { |
||||
children.get(i).setMaterial(material); |
||||
} |
||||
} else { |
||||
super.setMaterial(material); |
||||
} |
||||
} |
||||
|
||||
public Skeleton getSkeleton() { |
||||
return skeleton; |
||||
} |
||||
|
||||
|
||||
private void computeLength(Bone b, Map<Integer, Float> boneLengths, Skeleton skeleton) { |
||||
if (b.getChildren().isEmpty()) { |
||||
if (b.getParent() != null) { |
||||
boneLengths.put(skeleton.getBoneIndex(b), boneLengths.get(skeleton.getBoneIndex(b.getParent())) * 0.75f); |
||||
} else { |
||||
boneLengths.put(skeleton.getBoneIndex(b), 0.1f); |
||||
} |
||||
} else { |
||||
float length = Float.MAX_VALUE; |
||||
for (Bone bone : b.getChildren()) { |
||||
float len = b.getModelSpacePosition().subtract(bone.getModelSpacePosition()).length(); |
||||
if (len < length) { |
||||
length = len; |
||||
} |
||||
} |
||||
boneLengths.put(skeleton.getBoneIndex(b), length); |
||||
for (Bone bone : b.getChildren()) { |
||||
computeLength(bone, boneLengths, skeleton); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void updateLogicalState(float tpf) { |
||||
super.updateLogicalState(tpf); |
||||
bones.updateGeometry(); |
||||
if (interBoneWires != null) { |
||||
interBoneWires.updateGeometry(); |
||||
} |
||||
} |
||||
|
||||
ColorRGBA selectedColor = ColorRGBA.Orange; |
||||
ColorRGBA baseColor = new ColorRGBA(0.05f, 0.05f, 0.05f, 1f); |
||||
|
||||
protected Bone select(Geometry g) { |
||||
Node oldNode = bones.getSelectedNode(); |
||||
Bone b = bones.select(g); |
||||
if (b == null) { |
||||
return null; |
||||
} |
||||
if (oldNode != null) { |
||||
markSelected(oldNode, false); |
||||
} |
||||
markSelected(bones.getSelectedNode(), true); |
||||
return b; |
||||
} |
||||
|
||||
/** |
||||
* @return the skeleton wires |
||||
*/ |
||||
public SkeletonBone getBoneShapes() { |
||||
return bones; |
||||
} |
||||
|
||||
/** |
||||
* @return the dotted line between bones (can be null) |
||||
*/ |
||||
public SkeletonInterBoneWire getInterBoneWires() { |
||||
return interBoneWires; |
||||
} |
||||
|
||||
protected void markSelected(Node n, boolean selected) { |
||||
ColorRGBA c = baseColor; |
||||
if (selected) { |
||||
c = selectedColor; |
||||
} |
||||
for (Spatial spatial : n.getChildren()) { |
||||
if (spatial instanceof Geometry) { |
||||
Geometry geom = (Geometry) spatial; |
||||
|
||||
Geometry batch = (Geometry) getChild(getName() + "-batch0"); |
||||
VertexBuffer vb = batch.getMesh().getBuffer(VertexBuffer.Type.Color); |
||||
FloatBuffer color = (FloatBuffer) vb.getData(); |
||||
// System.err.println(getName() + "." + geom.getName() + " index " + getGeometryStartIndex(geom) * 4 + "/" + color.limit());
|
||||
|
||||
color.position(getGeometryStartIndex(geom) * 4); |
||||
|
||||
for (int i = 0; i < geom.getVertexCount(); i++) { |
||||
color.put(c.r).put(c.g).put(c.b).put(c.a); |
||||
} |
||||
color.rewind(); |
||||
vb.updateData(color); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,141 @@ |
||||
package com.jme3.scene.debug.custom; |
||||
|
||||
/* |
||||
* 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. |
||||
*/ |
||||
|
||||
|
||||
import java.nio.FloatBuffer; |
||||
import java.util.Map; |
||||
|
||||
import com.jme3.animation.Bone; |
||||
import com.jme3.animation.Skeleton; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.VertexBuffer; |
||||
import com.jme3.scene.VertexBuffer.Format; |
||||
import com.jme3.scene.VertexBuffer.Type; |
||||
import com.jme3.scene.VertexBuffer.Usage; |
||||
import com.jme3.util.BufferUtils; |
||||
|
||||
/** |
||||
* A class that displays a dotted line between a bone tail and its childrens' heads. |
||||
* |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public class SkeletonInterBoneWire extends Mesh { |
||||
private static final int POINT_AMOUNT = 10; |
||||
/** |
||||
* The amount of connections between bones. |
||||
*/ |
||||
private int connectionsAmount; |
||||
/** |
||||
* The skeleton that will be showed. |
||||
*/ |
||||
private Skeleton skeleton; |
||||
/** |
||||
* The map between the bone index and its length. |
||||
*/ |
||||
private Map<Integer, Float> boneLengths; |
||||
|
||||
private boolean guessBonesOrientation = false; |
||||
|
||||
/** |
||||
* Creates buffers for points. Each line has POINT_AMOUNT of points. |
||||
* |
||||
* @param skeleton the skeleton that will be showed |
||||
* @param boneLengths the lengths of the bones |
||||
*/ |
||||
public SkeletonInterBoneWire(Skeleton skeleton, Map<Integer, Float> boneLengths, boolean guessBonesOrientation) { |
||||
this.skeleton = skeleton; |
||||
|
||||
for (Bone bone : skeleton.getRoots()) { |
||||
this.countConnections(bone); |
||||
} |
||||
|
||||
this.setMode(Mode.Points); |
||||
this.setPointSize(2); |
||||
this.boneLengths = boneLengths; |
||||
|
||||
VertexBuffer pb = new VertexBuffer(Type.Position); |
||||
FloatBuffer fpb = BufferUtils.createFloatBuffer(POINT_AMOUNT * connectionsAmount * 3); |
||||
pb.setupData(Usage.Stream, 3, Format.Float, fpb); |
||||
this.setBuffer(pb); |
||||
|
||||
this.guessBonesOrientation = guessBonesOrientation; |
||||
this.updateCounts(); |
||||
} |
||||
|
||||
/** |
||||
* The method updates the geometry according to the poitions of the bones. |
||||
*/ |
||||
public void updateGeometry() { |
||||
VertexBuffer vb = this.getBuffer(Type.Position); |
||||
FloatBuffer posBuf = this.getFloatBuffer(Type.Position); |
||||
posBuf.clear(); |
||||
for (int i = 0; i < skeleton.getBoneCount(); ++i) { |
||||
Bone bone = skeleton.getBone(i); |
||||
Vector3f parentTail = bone.getModelSpacePosition().add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); |
||||
|
||||
if (guessBonesOrientation) { |
||||
parentTail = bone.getModelSpacePosition(); |
||||
} |
||||
|
||||
for (Bone child : bone.getChildren()) { |
||||
Vector3f childHead = child.getModelSpacePosition(); |
||||
Vector3f v = childHead.subtract(parentTail); |
||||
float pointDelta = v.length() / POINT_AMOUNT; |
||||
v.normalizeLocal().multLocal(pointDelta); |
||||
Vector3f pointPosition = parentTail.clone(); |
||||
for (int j = 0; j < POINT_AMOUNT; ++j) { |
||||
posBuf.put(pointPosition.getX()).put(pointPosition.getY()).put(pointPosition.getZ()); |
||||
pointPosition.addLocal(v); |
||||
} |
||||
} |
||||
} |
||||
posBuf.flip(); |
||||
vb.updateData(posBuf); |
||||
|
||||
this.updateBound(); |
||||
} |
||||
|
||||
/** |
||||
* Th method couns the connections between bones. |
||||
* |
||||
* @param bone the bone where counting starts |
||||
*/ |
||||
private void countConnections(Bone bone) { |
||||
for (Bone child : bone.getChildren()) { |
||||
++connectionsAmount; |
||||
this.countConnections(child); |
||||
} |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue