Bone animation :
- Split of the AnimControl in two parts : - The AnimControl that handles skeleton transformation via tha animation data - The SkeletonControl that handles the skinning of the mesh using skeleton transformations - Ensured backward compatibility with old j3o files, and changed the ogre mesh loader to create the controls properly - Reverted change http://code.google.com/p/jmonkeyengine/source/detail?r=7142 transforms passed to the setUserTransform methods must be considered as increments to current transform - Fixed some issues in the ragdollControl and test case (still WIP don't use it) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7163 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
e4c8769be0
commit
dc030c897f
@ -29,31 +29,21 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.jme3.animation;
|
package com.jme3.animation;
|
||||||
|
|
||||||
import com.jme3.bullet.control.RagdollControl;
|
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Matrix4f;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.renderer.ViewPort;
|
import com.jme3.renderer.ViewPort;
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.VertexBuffer;
|
|
||||||
import com.jme3.scene.VertexBuffer.Type;
|
|
||||||
import com.jme3.scene.control.AbstractControl;
|
import com.jme3.scene.control.AbstractControl;
|
||||||
import com.jme3.scene.control.Control;
|
import com.jme3.scene.control.Control;
|
||||||
import com.jme3.util.TempVars;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -83,26 +73,23 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
/**
|
/**
|
||||||
* List of targets which this controller effects.
|
* List of targets which this controller effects.
|
||||||
*/
|
*/
|
||||||
Mesh[] targets;
|
// Mesh[] targets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skeleton object must contain corresponding data for the targets' weight buffers.
|
* Skeleton object must contain corresponding data for the targets' weight buffers.
|
||||||
*/
|
*/
|
||||||
Skeleton skeleton;
|
Skeleton skeleton;
|
||||||
|
/** only used for backward compatibility */
|
||||||
|
@Deprecated
|
||||||
|
private SkeletonControl skeletonControl;
|
||||||
/**
|
/**
|
||||||
* List of animations
|
* List of animations
|
||||||
*/
|
*/
|
||||||
HashMap<String, BoneAnimation> animationMap;
|
HashMap<String, BoneAnimation> animationMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animation channels
|
* Animation channels
|
||||||
*/
|
*/
|
||||||
transient ArrayList<AnimChannel> channels
|
transient ArrayList<AnimChannel> channels = new ArrayList<AnimChannel>();
|
||||||
= new ArrayList<AnimChannel>();
|
transient ArrayList<AnimEventListener> listeners = new ArrayList<AnimEventListener>();
|
||||||
|
|
||||||
transient ArrayList<AnimEventListener> listeners
|
|
||||||
= new ArrayList<AnimEventListener>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new <code>AnimControl</code> that will animate the given skins
|
* Create a new <code>AnimControl</code> that will animate the given skins
|
||||||
@ -114,12 +101,22 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* properly set BoneIndex and BoneWeight buffers.
|
* properly set BoneIndex and BoneWeight buffers.
|
||||||
* @param skeleton The skeleton structure represents a bone hierarchy
|
* @param skeleton The skeleton structure represents a bone hierarchy
|
||||||
* to be animated.
|
* to be animated.
|
||||||
|
* @deprecated AnimControl doesnt' hande the skinning anymore, use AnimControl(Skeleton skeleton);
|
||||||
|
* Then create a SkeletonControl(Node model, Mesh[] meshes, Skeleton skeleton);
|
||||||
|
* and add it to the spatial.
|
||||||
*/
|
*/
|
||||||
public AnimControl(Node model, Mesh[] meshes, Skeleton skeleton){
|
@Deprecated
|
||||||
|
public AnimControl(Node model, Mesh[] meshes, Skeleton skeleton) {
|
||||||
super(model);
|
super(model);
|
||||||
|
|
||||||
this.skeleton = skeleton;
|
this.skeleton = skeleton;
|
||||||
this.targets = meshes;
|
|
||||||
|
skeletonControl = new SkeletonControl(model, meshes, this.skeleton);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimControl(Skeleton skeleton) {
|
||||||
|
this.skeleton = skeleton;
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,32 +128,11 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
public AnimControl() {
|
public AnimControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Control cloneForSpatial(Spatial spatial){
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
try {
|
try {
|
||||||
Node clonedNode = (Node) spatial;
|
|
||||||
AnimControl clone = (AnimControl) super.clone();
|
AnimControl clone = (AnimControl) super.clone();
|
||||||
clone.spatial = spatial;
|
clone.spatial = spatial;
|
||||||
clone.skeleton = new Skeleton(skeleton);
|
clone.skeleton = new Skeleton(skeleton);
|
||||||
Mesh[] meshes = new Mesh[targets.length];
|
|
||||||
for (int i = 0; i < meshes.length; i++) {
|
|
||||||
meshes[i] = ((Geometry) clonedNode.getChild(i)).getMesh();
|
|
||||||
}
|
|
||||||
for (int i = meshes.length; i < clonedNode.getQuantity(); i++){
|
|
||||||
// go through attachment nodes, apply them to correct bone
|
|
||||||
Spatial child = clonedNode.getChild(i);
|
|
||||||
if (child instanceof Node){
|
|
||||||
Node clonedAttachNode = (Node) child;
|
|
||||||
Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
|
|
||||||
|
|
||||||
if (originalBone != null){
|
|
||||||
Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
|
|
||||||
|
|
||||||
clonedAttachNode.setUserData("AttachedBone", clonedBone);
|
|
||||||
clonedBone.setAttachmentsNode(clonedAttachNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clone.targets = meshes;
|
|
||||||
clone.channels = new ArrayList<AnimChannel>();
|
clone.channels = new ArrayList<AnimChannel>();
|
||||||
return clone;
|
return clone;
|
||||||
} catch (CloneNotSupportedException ex) {
|
} catch (CloneNotSupportedException ex) {
|
||||||
@ -169,7 +145,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* will be capable of playing. The animations should be compatible
|
* will be capable of playing. The animations should be compatible
|
||||||
* with the skeleton given in the constructor.
|
* with the skeleton given in the constructor.
|
||||||
*/
|
*/
|
||||||
public void setAnimations(HashMap<String, BoneAnimation> animations){
|
public void setAnimations(HashMap<String, BoneAnimation> animations) {
|
||||||
animationMap = animations;
|
animationMap = animations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +155,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* @return The animation corresponding to the given name, or null, if no
|
* @return The animation corresponding to the given name, or null, if no
|
||||||
* such named animation exists.
|
* such named animation exists.
|
||||||
*/
|
*/
|
||||||
public BoneAnimation getAnim(String name){
|
public BoneAnimation getAnim(String name) {
|
||||||
return animationMap.get(name);
|
return animationMap.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +164,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* <code>AnimControl</code>.
|
* <code>AnimControl</code>.
|
||||||
* @param anim The animation to add.
|
* @param anim The animation to add.
|
||||||
*/
|
*/
|
||||||
public void addAnim(BoneAnimation anim){
|
public void addAnim(BoneAnimation anim) {
|
||||||
animationMap.put(anim.getName(), anim);
|
animationMap.put(anim.getName(), anim);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,23 +172,34 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* Remove an animation so that it is no longer available for playing.
|
* Remove an animation so that it is no longer available for playing.
|
||||||
* @param anim The animation to remove.
|
* @param anim The animation to remove.
|
||||||
*/
|
*/
|
||||||
public void removeAnim(BoneAnimation anim){
|
public void removeAnim(BoneAnimation anim) {
|
||||||
if (!animationMap.containsKey(anim.getName()))
|
if (!animationMap.containsKey(anim.getName())) {
|
||||||
throw new IllegalArgumentException("Given animation does not exist " +
|
throw new IllegalArgumentException("Given animation does not exist "
|
||||||
"in this AnimControl");
|
+ "in this AnimControl");
|
||||||
|
}
|
||||||
|
|
||||||
animationMap.remove(anim.getName());
|
animationMap.remove(anim.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param boneName the name of the bone
|
||||||
|
* @return the node attached to this bone
|
||||||
|
* @deprecated use SkeletonControl.getAttachementNode instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public Node getAttachmentsNode(String boneName) {
|
public Node getAttachmentsNode(String boneName) {
|
||||||
Bone b = skeleton.getBone(boneName);
|
Bone b = skeleton.getBone(boneName);
|
||||||
if (b == null)
|
if (b == null) {
|
||||||
throw new IllegalArgumentException("Given bone name does not exist " +
|
throw new IllegalArgumentException("Given bone name does not exist "
|
||||||
"in the skeleton.");
|
+ "in the skeleton.");
|
||||||
|
}
|
||||||
|
|
||||||
Node n = b.getAttachmentsNode();
|
Node n = b.getAttachmentsNode();
|
||||||
Node model = (Node) spatial;
|
if (spatial != null) {
|
||||||
model.attachChild(n);
|
Node model = (Node) spatial;
|
||||||
|
model.attachChild(n);
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +209,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
*
|
*
|
||||||
* @return A new animation channel for this <code>AnimControl</code>.
|
* @return A new animation channel for this <code>AnimControl</code>.
|
||||||
*/
|
*/
|
||||||
public AnimChannel createChannel(){
|
public AnimChannel createChannel() {
|
||||||
AnimChannel channel = new AnimChannel(this);
|
AnimChannel channel = new AnimChannel(this);
|
||||||
channels.add(channel);
|
channels.add(channel);
|
||||||
return channel;
|
return channel;
|
||||||
@ -236,7 +223,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
*
|
*
|
||||||
* @throws IndexOutOfBoundsException If no channel exists at the given index.
|
* @throws IndexOutOfBoundsException If no channel exists at the given index.
|
||||||
*/
|
*/
|
||||||
public AnimChannel getChannel(int index){
|
public AnimChannel getChannel(int index) {
|
||||||
return channels.get(index);
|
return channels.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +233,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
*
|
*
|
||||||
* @see AnimControl#createChannel()
|
* @see AnimControl#createChannel()
|
||||||
*/
|
*/
|
||||||
public int getNumChannels(){
|
public int getNumChannels() {
|
||||||
return channels.size();
|
return channels.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +242,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
*
|
*
|
||||||
* @see AnimControl#createChannel()
|
* @see AnimControl#createChannel()
|
||||||
*/
|
*/
|
||||||
public void clearChannels(){
|
public void clearChannels() {
|
||||||
channels.clear();
|
channels.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,19 +256,23 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
/**
|
/**
|
||||||
* @return The targets, or skins, being influenced by this
|
* @return The targets, or skins, being influenced by this
|
||||||
* <code>AnimControl</code>.
|
* <code>AnimControl</code>.
|
||||||
|
* @deprecated use SkeletonControl.getTargets() instead
|
||||||
|
* get the SkeletonControl doing spatial.getControl(SkeletonControl.class);
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Mesh[] getTargets() {
|
public Mesh[] getTargets() {
|
||||||
return targets;
|
return skeletonControl.getTargets();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new listener to receive animation related events.
|
* Adds a new listener to receive animation related events.
|
||||||
* @param listener The listener to add.
|
* @param listener The listener to add.
|
||||||
*/
|
*/
|
||||||
public void addListener(AnimEventListener listener){
|
public void addListener(AnimEventListener listener) {
|
||||||
if (listeners.contains(listener))
|
if (listeners.contains(listener)) {
|
||||||
throw new IllegalArgumentException("The given listener is already " +
|
throw new IllegalArgumentException("The given listener is already "
|
||||||
"registed at this AnimControl");
|
+ "registed at this AnimControl");
|
||||||
|
}
|
||||||
|
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
@ -291,10 +282,11 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* @param listener
|
* @param listener
|
||||||
* @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
|
* @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
|
||||||
*/
|
*/
|
||||||
public void removeListener(AnimEventListener listener){
|
public void removeListener(AnimEventListener listener) {
|
||||||
if (!listeners.remove(listener))
|
if (!listeners.remove(listener)) {
|
||||||
throw new IllegalArgumentException("The given listener is not " +
|
throw new IllegalArgumentException("The given listener is not "
|
||||||
"registed at this AnimControl");
|
+ "registed at this AnimControl");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,53 +294,36 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
*
|
*
|
||||||
* @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
|
* @see AnimControl#addListener(com.jme3.animation.AnimEventListener)
|
||||||
*/
|
*/
|
||||||
public void clearListeners(){
|
public void clearListeners() {
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyAnimChange(AnimChannel channel, String name){
|
void notifyAnimChange(AnimChannel channel, String name) {
|
||||||
for (int i = 0; i < listeners.size(); i++){
|
for (int i = 0; i < listeners.size(); i++) {
|
||||||
listeners.get(i).onAnimChange(this, channel, name);
|
listeners.get(i).onAnimChange(this, channel, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyAnimCycleDone(AnimChannel channel, String name){
|
void notifyAnimCycleDone(AnimChannel channel, String name) {
|
||||||
for (int i = 0; i < listeners.size(); i++){
|
for (int i = 0; i < listeners.size(); i++) {
|
||||||
listeners.get(i).onAnimCycleDone(this, channel, name);
|
listeners.get(i).onAnimCycleDone(this, channel, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final void reset(){
|
@Override
|
||||||
resetToBind();
|
public void setSpatial(Spatial spatial) {
|
||||||
if (skeleton != null){
|
super.setSpatial(spatial);
|
||||||
skeleton.resetAndUpdate();
|
|
||||||
|
//Backward compatibility.
|
||||||
|
if (skeletonControl != null) {
|
||||||
|
spatial.addControl(skeletonControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetToBind(){
|
final void reset() {
|
||||||
for (int i = 0; i < targets.length; i++){
|
if (skeleton != null) {
|
||||||
Mesh mesh = targets[i];
|
skeleton.resetAndUpdate();
|
||||||
if (targets[i].getBuffer(Type.BindPosePosition) != null){
|
|
||||||
VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
|
|
||||||
ByteBuffer bib = (ByteBuffer) bi.getData();
|
|
||||||
if (!bib.hasArray())
|
|
||||||
mesh.prepareForAnim(true); // prepare for software animation
|
|
||||||
|
|
||||||
VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
|
|
||||||
VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
|
|
||||||
VertexBuffer pos = mesh.getBuffer(Type.Position);
|
|
||||||
VertexBuffer norm = mesh.getBuffer(Type.Normal);
|
|
||||||
FloatBuffer pb = (FloatBuffer) pos.getData();
|
|
||||||
FloatBuffer nb = (FloatBuffer) norm.getData();
|
|
||||||
FloatBuffer bpb = (FloatBuffer) bindPos.getData();
|
|
||||||
FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
|
|
||||||
pb.clear();
|
|
||||||
nb.clear();
|
|
||||||
bpb.clear();
|
|
||||||
bnb.clear();
|
|
||||||
pb.put(bpb).clear();
|
|
||||||
nb.put(bnb).clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +331,7 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* @return The names of all animations that this <code>AnimControl</code>
|
* @return The names of all animations that this <code>AnimControl</code>
|
||||||
* can play.
|
* can play.
|
||||||
*/
|
*/
|
||||||
public Collection<String> getAnimationNames(){
|
public Collection<String> getAnimationNames() {
|
||||||
return animationMap.keySet();
|
return animationMap.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,160 +340,59 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
|
|||||||
* @param name The name of the animation
|
* @param name The name of the animation
|
||||||
* @return The length of time, in seconds, of the named animation.
|
* @return The length of time, in seconds, of the named animation.
|
||||||
*/
|
*/
|
||||||
public float getAnimationLength(String name){
|
public float getAnimationLength(String name) {
|
||||||
BoneAnimation a = animationMap.get(name);
|
BoneAnimation a = animationMap.get(name);
|
||||||
if (a == null)
|
if (a == null) {
|
||||||
throw new IllegalArgumentException("The animation " + name +
|
throw new IllegalArgumentException("The animation " + name
|
||||||
" does not exist in this AnimControl");
|
+ " does not exist in this AnimControl");
|
||||||
|
}
|
||||||
|
|
||||||
return a.getLength();
|
return a.getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RagdollControl ragdoll=null;
|
|
||||||
|
|
||||||
public void setRagdoll(RagdollControl ragdoll) {
|
|
||||||
this.ragdoll = ragdoll;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlUpdate(float tpf) {
|
protected void controlUpdate(float tpf) {
|
||||||
resetToBind(); // reset morph meshes to bind pose
|
|
||||||
skeleton.reset(); // reset skeleton to bind pose
|
skeleton.reset(); // reset skeleton to bind pose
|
||||||
|
|
||||||
for (int i = 0; i < channels.size(); i++){
|
for (int i = 0; i < channels.size(); i++) {
|
||||||
channels.get(i).update(tpf);
|
channels.get(i).update(tpf);
|
||||||
}
|
}
|
||||||
|
|
||||||
skeleton.updateWorldVectors();
|
skeleton.updateWorldVectors();
|
||||||
// here update the targets vertices if no hardware skinning supported
|
|
||||||
|
|
||||||
if(ragdoll!=null){
|
|
||||||
ragdoll.update(tpf);
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
|
|
||||||
|
|
||||||
// if hardware skinning is supported, the matrices and weight buffer
|
|
||||||
// will be sent by the SkinningShaderLogic object assigned to the shader
|
|
||||||
for (int i = 0; i < targets.length; i++){
|
|
||||||
// only update targets with bone-vertex assignments
|
|
||||||
if (targets[i].getBuffer(Type.BoneIndex) != null)
|
|
||||||
softwareSkinUpdate(targets[i], offsetMatrices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices){
|
|
||||||
int maxWeightsPerVert = mesh.getMaxNumWeights();
|
|
||||||
int fourMinusMaxWeights = 4 - maxWeightsPerVert;
|
|
||||||
|
|
||||||
// NOTE: This code assumes the vertex buffer is in bind pose
|
|
||||||
// resetToBind() has been called this frame
|
|
||||||
VertexBuffer vb = mesh.getBuffer(Type.Position);
|
|
||||||
FloatBuffer fvb = (FloatBuffer) vb.getData();
|
|
||||||
fvb.rewind();
|
|
||||||
|
|
||||||
VertexBuffer nb = mesh.getBuffer(Type.Normal);
|
|
||||||
FloatBuffer fnb = (FloatBuffer) nb.getData();
|
|
||||||
fnb.rewind();
|
|
||||||
|
|
||||||
// get boneIndexes and weights for mesh
|
|
||||||
ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
|
|
||||||
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
|
||||||
|
|
||||||
ib.rewind();
|
|
||||||
wb.rewind();
|
|
||||||
|
|
||||||
float[] weights = wb.array();
|
|
||||||
byte[] indices = ib.array();
|
|
||||||
int idxWeights = 0;
|
|
||||||
|
|
||||||
TempVars vars = TempVars.get();
|
|
||||||
float[] posBuf = vars.skinPositions;
|
|
||||||
float[] normBuf = vars.skinNormals;
|
|
||||||
|
|
||||||
int iterations = (int) FastMath.ceil(fvb.capacity() / ((float)posBuf.length));
|
|
||||||
int bufLength = posBuf.length * 3;
|
|
||||||
for (int i = iterations-1; i >= 0; i--){
|
|
||||||
// read next set of positions and normals from native buffer
|
|
||||||
bufLength = Math.min(posBuf.length, fvb.remaining());
|
|
||||||
fvb.get(posBuf, 0, bufLength);
|
|
||||||
fnb.get(normBuf, 0, bufLength);
|
|
||||||
int verts = bufLength / 3;
|
|
||||||
int idxPositions = 0;
|
|
||||||
|
|
||||||
// iterate vertices and apply skinning transform for each effecting bone
|
|
||||||
for (int vert = verts - 1; vert >= 0; vert--){
|
|
||||||
float nmx = normBuf[idxPositions];
|
|
||||||
float vtx = posBuf[idxPositions++];
|
|
||||||
float nmy = normBuf[idxPositions];
|
|
||||||
float vty = posBuf[idxPositions++];
|
|
||||||
float nmz = normBuf[idxPositions];
|
|
||||||
float vtz = posBuf[idxPositions++];
|
|
||||||
|
|
||||||
float rx=0, ry=0, rz=0, rnx=0, rny=0, rnz=0;
|
|
||||||
|
|
||||||
for (int w = maxWeightsPerVert - 1; w >= 0; w--){
|
|
||||||
float weight = weights[idxWeights];
|
|
||||||
Matrix4f mat = offsetMatrices[indices[idxWeights++]];
|
|
||||||
|
|
||||||
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
|
||||||
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
|
||||||
rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
|
|
||||||
|
|
||||||
rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
|
|
||||||
rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
|
|
||||||
rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
idxWeights += fourMinusMaxWeights;
|
|
||||||
|
|
||||||
idxPositions -= 3;
|
|
||||||
normBuf[idxPositions] = rnx;
|
|
||||||
posBuf[idxPositions++] = rx;
|
|
||||||
normBuf[idxPositions] = rny;
|
|
||||||
posBuf[idxPositions++] = ry;
|
|
||||||
normBuf[idxPositions] = rnz;
|
|
||||||
posBuf[idxPositions++] = rz;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fvb.position(fvb.position()-bufLength);
|
|
||||||
fvb.put(posBuf, 0, bufLength);
|
|
||||||
fnb.position(fnb.position()-bufLength);
|
|
||||||
fnb.put(normBuf, 0, bufLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
vb.updateData(fvb);
|
|
||||||
nb.updateData(fnb);
|
|
||||||
|
|
||||||
// mesh.updateBound();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException{
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(targets, "targets", null);
|
|
||||||
oc.write(skeleton, "skeleton", null);
|
oc.write(skeleton, "skeleton", null);
|
||||||
oc.writeStringSavableMap(animationMap, "animations", null);
|
oc.writeStringSavableMap(animationMap, "animations", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException{
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
InputCapsule in = im.getCapsule(this);
|
InputCapsule in = im.getCapsule(this);
|
||||||
Savable[] sav = in.readSavableArray("targets", null);
|
|
||||||
if (sav != null){
|
|
||||||
targets = new Mesh[sav.length];
|
|
||||||
System.arraycopy(sav, 0, targets, 0, sav.length);
|
|
||||||
}
|
|
||||||
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
||||||
animationMap = (HashMap<String, BoneAnimation>) in.readStringSavableMap("animations", null);
|
animationMap = (HashMap<String, BoneAnimation>) in.readStringSavableMap("animations", null);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
||||||
|
//if we find a target mesh array the AnimControl creates the SkeletonControl for old files and add it to the spatial.
|
||||||
|
//When backward compatibility won't be needed anymore this can deleted
|
||||||
|
Savable[] sav = in.readSavableArray("targets", null);
|
||||||
|
if (sav != null) {
|
||||||
|
Mesh[] tg = null;
|
||||||
|
tg = new Mesh[sav.length];
|
||||||
|
System.arraycopy(sav, 0, tg, 0, sav.length);
|
||||||
|
skeletonControl = new SkeletonControl((Node) spatial, tg, skeleton);
|
||||||
|
spatial.addControl(skeletonControl);
|
||||||
|
}
|
||||||
|
//------
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +347,7 @@ public final class Bone implements Savable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set user transform.
|
* Set user transform.
|
||||||
|
* The transforms are used as increments to current translations
|
||||||
* @see setUserControl
|
* @see setUserControl
|
||||||
*/
|
*/
|
||||||
public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
|
public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
|
||||||
@ -360,7 +361,7 @@ public final class Bone implements Savable {
|
|||||||
|
|
||||||
localPos.addLocal(translation);
|
localPos.addLocal(translation);
|
||||||
localRot = localRot.mult(rotation);
|
localRot = localRot.mult(rotation);
|
||||||
localScale.multLocal(scale);
|
localScale.addLocal(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
274
engine/src/core/com/jme3/animation/SkeletonControl.java
Normal file
274
engine/src/core/com/jme3/animation/SkeletonControl.java
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.jme3.animation;
|
||||||
|
|
||||||
|
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.math.FastMath;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.scene.VertexBuffer.Type;
|
||||||
|
import com.jme3.scene.control.AbstractControl;
|
||||||
|
import com.jme3.scene.control.Control;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Nehon
|
||||||
|
*/
|
||||||
|
public class SkeletonControl extends AbstractControl implements Savable, Cloneable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The skelrton of the model
|
||||||
|
*/
|
||||||
|
private Skeleton skeleton;
|
||||||
|
/**
|
||||||
|
* List of targets which this controller effects.
|
||||||
|
*/
|
||||||
|
Mesh[] targets;
|
||||||
|
|
||||||
|
public SkeletonControl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonControl(Node model, Mesh[] targets, Skeleton skeleton) {
|
||||||
|
super(model);
|
||||||
|
this.skeleton = skeleton;
|
||||||
|
this.targets = targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
resetToBind(); // reset morph meshes to bind pose
|
||||||
|
|
||||||
|
Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
|
||||||
|
|
||||||
|
// if hardware skinning is supported, the matrices and weight buffer
|
||||||
|
// will be sent by the SkinningShaderLogic object assigned to the shader
|
||||||
|
for (int i = 0; i < targets.length; i++) {
|
||||||
|
// only update targets with bone-vertex assignments
|
||||||
|
if (targets[i].getBuffer(Type.BoneIndex) != null) {
|
||||||
|
softwareSkinUpdate(targets[i], offsetMatrices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetToBind() {
|
||||||
|
for (int i = 0; i < targets.length; i++) {
|
||||||
|
Mesh mesh = targets[i];
|
||||||
|
if (targets[i].getBuffer(Type.BindPosePosition) != null) {
|
||||||
|
VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
|
||||||
|
ByteBuffer bib = (ByteBuffer) bi.getData();
|
||||||
|
if (!bib.hasArray()) {
|
||||||
|
mesh.prepareForAnim(true); // prepare for software animation
|
||||||
|
}
|
||||||
|
VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
|
||||||
|
VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
|
||||||
|
VertexBuffer pos = mesh.getBuffer(Type.Position);
|
||||||
|
VertexBuffer norm = mesh.getBuffer(Type.Normal);
|
||||||
|
FloatBuffer pb = (FloatBuffer) pos.getData();
|
||||||
|
FloatBuffer nb = (FloatBuffer) norm.getData();
|
||||||
|
FloatBuffer bpb = (FloatBuffer) bindPos.getData();
|
||||||
|
FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
|
||||||
|
pb.clear();
|
||||||
|
nb.clear();
|
||||||
|
bpb.clear();
|
||||||
|
bnb.clear();
|
||||||
|
pb.put(bpb).clear();
|
||||||
|
nb.put(bnb).clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
|
||||||
|
int maxWeightsPerVert = mesh.getMaxNumWeights();
|
||||||
|
int fourMinusMaxWeights = 4 - maxWeightsPerVert;
|
||||||
|
|
||||||
|
// NOTE: This code assumes the vertex buffer is in bind pose
|
||||||
|
// resetToBind() has been called this frame
|
||||||
|
VertexBuffer vb = mesh.getBuffer(Type.Position);
|
||||||
|
FloatBuffer fvb = (FloatBuffer) vb.getData();
|
||||||
|
fvb.rewind();
|
||||||
|
|
||||||
|
VertexBuffer nb = mesh.getBuffer(Type.Normal);
|
||||||
|
FloatBuffer fnb = (FloatBuffer) nb.getData();
|
||||||
|
fnb.rewind();
|
||||||
|
|
||||||
|
// get boneIndexes and weights for mesh
|
||||||
|
ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
|
||||||
|
FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
|
||||||
|
|
||||||
|
ib.rewind();
|
||||||
|
wb.rewind();
|
||||||
|
|
||||||
|
float[] weights = wb.array();
|
||||||
|
byte[] indices = ib.array();
|
||||||
|
int idxWeights = 0;
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
float[] posBuf = vars.skinPositions;
|
||||||
|
float[] normBuf = vars.skinNormals;
|
||||||
|
|
||||||
|
int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
|
||||||
|
int bufLength = posBuf.length * 3;
|
||||||
|
for (int i = iterations - 1; i >= 0; i--) {
|
||||||
|
// read next set of positions and normals from native buffer
|
||||||
|
bufLength = Math.min(posBuf.length, fvb.remaining());
|
||||||
|
fvb.get(posBuf, 0, bufLength);
|
||||||
|
fnb.get(normBuf, 0, bufLength);
|
||||||
|
int verts = bufLength / 3;
|
||||||
|
int idxPositions = 0;
|
||||||
|
|
||||||
|
// iterate vertices and apply skinning transform for each effecting bone
|
||||||
|
for (int vert = verts - 1; vert >= 0; vert--) {
|
||||||
|
float nmx = normBuf[idxPositions];
|
||||||
|
float vtx = posBuf[idxPositions++];
|
||||||
|
float nmy = normBuf[idxPositions];
|
||||||
|
float vty = posBuf[idxPositions++];
|
||||||
|
float nmz = normBuf[idxPositions];
|
||||||
|
float vtz = posBuf[idxPositions++];
|
||||||
|
|
||||||
|
float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0;
|
||||||
|
|
||||||
|
for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
|
||||||
|
float weight = weights[idxWeights];
|
||||||
|
Matrix4f mat = offsetMatrices[indices[idxWeights++]];
|
||||||
|
|
||||||
|
rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
|
||||||
|
ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
|
||||||
|
rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
|
||||||
|
|
||||||
|
rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
|
||||||
|
rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
|
||||||
|
rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
idxWeights += fourMinusMaxWeights;
|
||||||
|
|
||||||
|
idxPositions -= 3;
|
||||||
|
normBuf[idxPositions] = rnx;
|
||||||
|
posBuf[idxPositions++] = rx;
|
||||||
|
normBuf[idxPositions] = rny;
|
||||||
|
posBuf[idxPositions++] = ry;
|
||||||
|
normBuf[idxPositions] = rnz;
|
||||||
|
posBuf[idxPositions++] = rz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fvb.position(fvb.position() - bufLength);
|
||||||
|
fvb.put(posBuf, 0, bufLength);
|
||||||
|
fnb.position(fnb.position() - bufLength);
|
||||||
|
fnb.put(normBuf, 0, bufLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
vb.updateData(fvb);
|
||||||
|
nb.updateData(fnb);
|
||||||
|
|
||||||
|
// mesh.updateBound();
|
||||||
|
}
|
||||||
|
|
||||||
|
final void reset() {
|
||||||
|
resetToBind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
|
Node clonedNode = (Node) spatial;
|
||||||
|
AnimControl ctrl = spatial.getControl(AnimControl.class);
|
||||||
|
SkeletonControl clone = new SkeletonControl();
|
||||||
|
|
||||||
|
clone.skeleton = ctrl.getSkeleton();
|
||||||
|
Mesh[] meshes = new Mesh[targets.length];
|
||||||
|
for (int i = 0; i < meshes.length; i++) {
|
||||||
|
meshes[i] = ((Geometry) clonedNode.getChild(i)).getMesh();
|
||||||
|
}
|
||||||
|
for (int i = meshes.length; i < clonedNode.getQuantity(); i++) {
|
||||||
|
// go through attachment nodes, apply them to correct bone
|
||||||
|
Spatial child = clonedNode.getChild(i);
|
||||||
|
if (child instanceof Node) {
|
||||||
|
Node clonedAttachNode = (Node) child;
|
||||||
|
Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
|
||||||
|
|
||||||
|
if (originalBone != null) {
|
||||||
|
Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
|
||||||
|
|
||||||
|
clonedAttachNode.setUserData("AttachedBone", clonedBone);
|
||||||
|
clonedBone.setAttachmentsNode(clonedAttachNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clone.targets = meshes;
|
||||||
|
return clone;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param boneName the name of the bone
|
||||||
|
* @return the node attached to this bone
|
||||||
|
*/
|
||||||
|
public Node getAttachmentsNode(String boneName) {
|
||||||
|
Bone b = skeleton.getBone(boneName);
|
||||||
|
if (b == null) {
|
||||||
|
throw new IllegalArgumentException("Given bone name does not exist "
|
||||||
|
+ "in the skeleton.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Node n = b.getAttachmentsNode();
|
||||||
|
Node model = (Node) spatial;
|
||||||
|
model.attachChild(n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Skeleton getSkeleton() {
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkeleton(Skeleton skeleton) {
|
||||||
|
this.skeleton = skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mesh[] getTargets() {
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargets(Mesh[] targets) {
|
||||||
|
this.targets = targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
super.write(ex);
|
||||||
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
oc.write(targets, "targets", null);
|
||||||
|
oc.write(skeleton, "skeleton", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
super.read(im);
|
||||||
|
InputCapsule in = im.getCapsule(this);
|
||||||
|
Savable[] sav = in.readSavableArray("targets", null);
|
||||||
|
if (sav != null) {
|
||||||
|
targets = new Mesh[sav.length];
|
||||||
|
System.arraycopy(sav, 0, targets, 0, sav.length);
|
||||||
|
}
|
||||||
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,6 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.jme3.scene;
|
package com.jme3.scene;
|
||||||
|
|
||||||
import com.jme3.bounding.BoundingVolume;
|
import com.jme3.bounding.BoundingVolume;
|
||||||
@ -64,7 +63,6 @@ import java.util.LinkedList;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>Spatial</code> defines the base class for scene graph nodes. It
|
* <code>Spatial</code> defines the base class for scene graph nodes. It
|
||||||
* maintains a link to a parent, it's local transforms and the world's
|
* maintains a link to a parent, it's local transforms and the world's
|
||||||
@ -80,77 +78,59 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
|
||||||
|
|
||||||
public enum CullHint {
|
public enum CullHint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do whatever our parent does. If no parent, we'll default to dynamic.
|
* Do whatever our parent does. If no parent, we'll default to dynamic.
|
||||||
*/
|
*/
|
||||||
Inherit,
|
Inherit,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not draw if we are not at least partially within the view frustum
|
* Do not draw if we are not at least partially within the view frustum
|
||||||
* of the renderer's camera.
|
* of the renderer's camera.
|
||||||
*/
|
*/
|
||||||
Dynamic,
|
Dynamic,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always cull this from view.
|
* Always cull this from view.
|
||||||
*/
|
*/
|
||||||
Always,
|
Always,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Never cull this from view. Note that we will still get culled if our
|
* Never cull this from view. Note that we will still get culled if our
|
||||||
* parent is culled.
|
* parent is culled.
|
||||||
*/
|
*/
|
||||||
Never;
|
Never;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh flag types
|
* Refresh flag types
|
||||||
*/
|
*/
|
||||||
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
||||||
RF_BOUND = 0x02,
|
RF_BOUND = 0x02,
|
||||||
RF_LIGHTLIST = 0x04; // changes in light lists
|
RF_LIGHTLIST = 0x04; // changes in light lists
|
||||||
|
|
||||||
protected CullHint cullHint = CullHint.Inherit;
|
protected CullHint cullHint = CullHint.Inherit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spatial's bounding volume relative to the world.
|
* Spatial's bounding volume relative to the world.
|
||||||
*/
|
*/
|
||||||
protected BoundingVolume worldBound;
|
protected BoundingVolume worldBound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LightList
|
* LightList
|
||||||
*/
|
*/
|
||||||
protected LightList localLights;
|
protected LightList localLights;
|
||||||
|
|
||||||
protected transient LightList worldLights;
|
protected transient LightList worldLights;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This spatial's name.
|
* This spatial's name.
|
||||||
*/
|
*/
|
||||||
protected String name;
|
protected String name;
|
||||||
|
|
||||||
// scale values
|
// scale values
|
||||||
protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects;
|
protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects;
|
||||||
|
|
||||||
protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit;
|
protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit;
|
||||||
|
|
||||||
protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit;
|
protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit;
|
||||||
|
|
||||||
public transient float queueDistance = Float.NEGATIVE_INFINITY;
|
public transient float queueDistance = Float.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
protected Transform localTransform;
|
protected Transform localTransform;
|
||||||
|
|
||||||
protected Transform worldTransform;
|
protected Transform worldTransform;
|
||||||
|
|
||||||
protected ArrayList<Control> controls = new ArrayList<Control>(1);
|
protected ArrayList<Control> controls = new ArrayList<Control>(1);
|
||||||
|
|
||||||
protected HashMap<String, Savable> userData = null;
|
protected HashMap<String, Savable> userData = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spatial's parent, or null if it has none.
|
* Spatial's parent, or null if it has none.
|
||||||
*/
|
*/
|
||||||
protected transient Node parent;
|
protected transient Node parent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh flags. Indicate what data of the spatial need to be
|
* Refresh flags. Indicate what data of the spatial need to be
|
||||||
* updated to reflect the correct state.
|
* updated to reflect the correct state.
|
||||||
@ -187,12 +167,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* Indicate that the transform of this spatial has changed and that
|
* Indicate that the transform of this spatial has changed and that
|
||||||
* a refresh is required.
|
* a refresh is required.
|
||||||
*/
|
*/
|
||||||
protected void setTransformRefresh(){
|
protected void setTransformRefresh() {
|
||||||
refreshFlags |= RF_TRANSFORM;
|
refreshFlags |= RF_TRANSFORM;
|
||||||
setBoundRefresh();
|
setBoundRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setLightListRefresh(){
|
protected void setLightListRefresh() {
|
||||||
refreshFlags |= RF_LIGHTLIST;
|
refreshFlags |= RF_LIGHTLIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,14 +180,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* Indicate that the bounding of this spatial has changed and that
|
* Indicate that the bounding of this spatial has changed and that
|
||||||
* a refresh is required.
|
* a refresh is required.
|
||||||
*/
|
*/
|
||||||
protected void setBoundRefresh(){
|
protected void setBoundRefresh() {
|
||||||
refreshFlags |= RF_BOUND;
|
refreshFlags |= RF_BOUND;
|
||||||
|
|
||||||
// XXX: Replace with a recursive call?
|
// XXX: Replace with a recursive call?
|
||||||
Spatial p = parent;
|
Spatial p = parent;
|
||||||
while (p != null){
|
while (p != null) {
|
||||||
if ((p.refreshFlags & RF_BOUND) != 0)
|
if ((p.refreshFlags & RF_BOUND) != 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
p.refreshFlags |= RF_BOUND;
|
p.refreshFlags |= RF_BOUND;
|
||||||
p = p.parent;
|
p = p.parent;
|
||||||
@ -225,20 +206,20 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @return true if inside or intersecting camera frustum
|
* @return true if inside or intersecting camera frustum
|
||||||
* (should be rendered), false if outside.
|
* (should be rendered), false if outside.
|
||||||
*/
|
*/
|
||||||
public boolean checkCulling(Camera cam){
|
public boolean checkCulling(Camera cam) {
|
||||||
if (refreshFlags != 0){
|
if (refreshFlags != 0) {
|
||||||
throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
|
throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
|
||||||
+ "Make sure scene graph state was not changed after\n"
|
+ "Make sure scene graph state was not changed after\n"
|
||||||
+ " rootNode.updateGeometricState() call. \n"
|
+ " rootNode.updateGeometricState() call. \n"
|
||||||
+ "Problem spatial name: "+getName());
|
+ "Problem spatial name: " + getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
CullHint cm = getCullHint();
|
CullHint cm = getCullHint();
|
||||||
assert cm != CullHint.Inherit;
|
assert cm != CullHint.Inherit;
|
||||||
if (cm == Spatial.CullHint.Always){
|
if (cm == Spatial.CullHint.Always) {
|
||||||
setLastFrustumIntersection(Camera.FrustumIntersect.Outside);
|
setLastFrustumIntersection(Camera.FrustumIntersect.Outside);
|
||||||
return false;
|
return false;
|
||||||
} else if (cm == Spatial.CullHint.Never){
|
} else if (cm == Spatial.CullHint.Never) {
|
||||||
setLastFrustumIntersection(Camera.FrustumIntersect.Intersects);
|
setLastFrustumIntersection(Camera.FrustumIntersect.Intersects);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -252,9 +233,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
return cam.containsGui(getWorldBound());
|
return cam.containsGui(getWorldBound());
|
||||||
} else {
|
} else {
|
||||||
int state = cam.getPlaneState();
|
int state = cam.getPlaneState();
|
||||||
|
|
||||||
frustrumIntersects = cam.contains(getWorldBound());
|
frustrumIntersects = cam.contains(getWorldBound());
|
||||||
|
|
||||||
cam.setPlaneState(state);
|
cam.setPlaneState(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,7 +262,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LightList getLocalLightList(){
|
public LightList getLocalLightList() {
|
||||||
return localLights;
|
return localLights;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +309,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return the world transform.
|
* @return the world transform.
|
||||||
*/
|
*/
|
||||||
public Transform getWorldTransform(){
|
public Transform getWorldTransform() {
|
||||||
checkDoTransformUpdate();
|
checkDoTransformUpdate();
|
||||||
return worldTransform;
|
return worldTransform;
|
||||||
}
|
}
|
||||||
@ -387,7 +368,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
assert vars.lock();
|
assert vars.lock();
|
||||||
Vector3f compVecA = vars.vect4;
|
Vector3f compVecA = vars.vect4;
|
||||||
assert vars.unlock();
|
assert vars.unlock();
|
||||||
|
|
||||||
compVecA.set(position).subtractLocal(worldTranslation);
|
compVecA.set(position).subtractLocal(worldTranslation);
|
||||||
getLocalRotation().lookAt(compVecA, upVector);
|
getLocalRotation().lookAt(compVecA, upVector);
|
||||||
|
|
||||||
@ -397,7 +378,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
/**
|
/**
|
||||||
* Should be overriden by Node and Geometry.
|
* Should be overriden by Node and Geometry.
|
||||||
*/
|
*/
|
||||||
protected void updateWorldBound(){
|
protected void updateWorldBound() {
|
||||||
// the world bound of a leaf is the same as it's model bound
|
// the world bound of a leaf is the same as it's model bound
|
||||||
// for a node, the world bound is a combination of all it's children
|
// for a node, the world bound is a combination of all it's children
|
||||||
// bounds
|
// bounds
|
||||||
@ -405,15 +386,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
refreshFlags &= ~RF_BOUND;
|
refreshFlags &= ~RF_BOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateWorldLightList(){
|
protected void updateWorldLightList() {
|
||||||
if (parent == null){
|
if (parent == null) {
|
||||||
worldLights.update(localLights, null);
|
worldLights.update(localLights, null);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
}else{
|
} else {
|
||||||
if ((parent.refreshFlags & RF_LIGHTLIST) == 0){
|
if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
|
||||||
worldLights.update(localLights, parent.worldLights);
|
worldLights.update(localLights, parent.worldLights);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
}else{
|
} else {
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -423,11 +404,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* Should only be called from updateGeometricState().
|
* Should only be called from updateGeometricState().
|
||||||
* In most cases should not be subclassed.
|
* In most cases should not be subclassed.
|
||||||
*/
|
*/
|
||||||
protected void updateWorldTransforms(){
|
protected void updateWorldTransforms() {
|
||||||
if (parent == null){
|
if (parent == null) {
|
||||||
worldTransform.set(localTransform);
|
worldTransform.set(localTransform);
|
||||||
refreshFlags &= ~RF_TRANSFORM;
|
refreshFlags &= ~RF_TRANSFORM;
|
||||||
}else{
|
} else {
|
||||||
// check if transform for parent is updated
|
// check if transform for parent is updated
|
||||||
assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
|
assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
|
||||||
worldTransform.set(localTransform);
|
worldTransform.set(localTransform);
|
||||||
@ -436,23 +417,24 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkDoTransformUpdate(){
|
void checkDoTransformUpdate() {
|
||||||
if ( (refreshFlags & RF_TRANSFORM) == 0 )
|
if ((refreshFlags & RF_TRANSFORM) == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (parent == null){
|
if (parent == null) {
|
||||||
worldTransform.set(localTransform);
|
worldTransform.set(localTransform);
|
||||||
refreshFlags &= ~RF_TRANSFORM;
|
refreshFlags &= ~RF_TRANSFORM;
|
||||||
}else{
|
} else {
|
||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
assert vars.lock();
|
assert vars.lock();
|
||||||
|
|
||||||
Spatial[] stack = vars.spatialStack;
|
Spatial[] stack = vars.spatialStack;
|
||||||
Spatial rootNode = this;
|
Spatial rootNode = this;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (true){
|
while (true) {
|
||||||
Spatial hisParent = rootNode.parent;
|
Spatial hisParent = rootNode.parent;
|
||||||
if (hisParent == null){
|
if (hisParent == null) {
|
||||||
rootNode.worldTransform.set(rootNode.localTransform);
|
rootNode.worldTransform.set(rootNode.localTransform);
|
||||||
rootNode.refreshFlags &= ~RF_TRANSFORM;
|
rootNode.refreshFlags &= ~RF_TRANSFORM;
|
||||||
i--;
|
i--;
|
||||||
@ -461,7 +443,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
|
|
||||||
stack[i] = rootNode;
|
stack[i] = rootNode;
|
||||||
|
|
||||||
if ( (hisParent.refreshFlags & RF_TRANSFORM) == 0 ){
|
if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +453,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
|
|
||||||
assert vars.unlock();
|
assert vars.unlock();
|
||||||
|
|
||||||
for (int j = i; j >= 0; j--){
|
for (int j = i; j >= 0; j--) {
|
||||||
rootNode = stack[j];
|
rootNode = stack[j];
|
||||||
//rootNode.worldTransform.set(rootNode.localTransform);
|
//rootNode.worldTransform.set(rootNode.localTransform);
|
||||||
//rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform);
|
//rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform);
|
||||||
@ -481,17 +463,18 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkDoBoundUpdate(){
|
void checkDoBoundUpdate() {
|
||||||
if ( (refreshFlags & RF_BOUND) == 0 )
|
if ((refreshFlags & RF_BOUND) == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
checkDoTransformUpdate();
|
checkDoTransformUpdate();
|
||||||
|
|
||||||
// Go to children recursively and update their bound
|
// Go to children recursively and update their bound
|
||||||
if (this instanceof Node){
|
if (this instanceof Node) {
|
||||||
Node node = (Node) this;
|
Node node = (Node) this;
|
||||||
int len = node.getQuantity();
|
int len = node.getQuantity();
|
||||||
for (int i = 0; i < len; i++){
|
for (int i = 0; i < len; i++) {
|
||||||
Spatial child = node.getChild(i);
|
Spatial child = node.getChild(i);
|
||||||
child.checkDoBoundUpdate();
|
child.checkDoBoundUpdate();
|
||||||
}
|
}
|
||||||
@ -501,11 +484,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
updateWorldBound();
|
updateWorldBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runControlUpdate(float tpf){
|
private void runControlUpdate(float tpf) {
|
||||||
if (controls.size() == 0)
|
if (controls.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < controls.size(); i++){
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
controls.get(i).update(tpf);
|
controls.get(i).update(tpf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -520,11 +504,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
* @see Spatial#getControl(java.lang.Class)
|
* @see Spatial#getControl(java.lang.Class)
|
||||||
*/
|
*/
|
||||||
public void runControlRender(RenderManager rm, ViewPort vp){
|
public void runControlRender(RenderManager rm, ViewPort vp) {
|
||||||
if (controls.size() == 0)
|
if (controls.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < controls.size(); i++){
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
controls.get(i).render(rm, vp);
|
controls.get(i).render(rm, vp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,7 +520,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#removeControl(java.lang.Class)
|
* @see Spatial#removeControl(java.lang.Class)
|
||||||
*/
|
*/
|
||||||
public void addControl(Control control){
|
public void addControl(Control control) {
|
||||||
controls.add(control);
|
controls.add(control);
|
||||||
control.setSpatial(this);
|
control.setSpatial(this);
|
||||||
}
|
}
|
||||||
@ -545,9 +530,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
*/
|
*/
|
||||||
public void removeControl(Class<? extends Control> controlType){
|
public void removeControl(Class<? extends Control> controlType) {
|
||||||
for (int i = 0; i < controls.size(); i++){
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
if (controlType.isAssignableFrom(controls.get(i).getClass())){
|
if (controlType.isAssignableFrom(controls.get(i).getClass())) {
|
||||||
Control control = controls.remove(i);
|
Control control = controls.remove(i);
|
||||||
control.setSpatial(null);
|
control.setSpatial(null);
|
||||||
}
|
}
|
||||||
@ -563,11 +548,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
*/
|
*/
|
||||||
public boolean removeControl(Control control){
|
public boolean removeControl(Control control) {
|
||||||
boolean result = controls.remove(control);
|
boolean result = controls.remove(control);
|
||||||
if (result)
|
if (result) {
|
||||||
control.setSpatial(null);
|
control.setSpatial(null);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,9 +566,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
*/
|
*/
|
||||||
public <T extends Control> T getControl(Class<T> controlType){
|
public <T extends Control> T getControl(Class<T> controlType) {
|
||||||
for (int i = 0; i < controls.size(); i++){
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
if (controlType.isAssignableFrom(controls.get(i).getClass())){
|
if (controlType.isAssignableFrom(controls.get(i).getClass())) {
|
||||||
return (T) controls.get(i);
|
return (T) controls.get(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,7 +586,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
*/
|
*/
|
||||||
public Control getControl(int index){
|
public Control getControl(int index) {
|
||||||
return controls.get(index);
|
return controls.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,11 +595,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
* @see Spatial#removeControl(java.lang.Class)
|
* @see Spatial#removeControl(java.lang.Class)
|
||||||
*/
|
*/
|
||||||
public int getNumControls(){
|
public int getNumControls() {
|
||||||
return controls.size();
|
return controls.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>updateLogicalState</code> calls the <code>update()</code> method
|
* <code>updateLogicalState</code> calls the <code>update()</code> method
|
||||||
* for all controls attached to this Spatial.
|
* for all controls attached to this Spatial.
|
||||||
@ -622,7 +607,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
* @see Spatial#addControl(com.jme3.scene.control.Control)
|
||||||
*/
|
*/
|
||||||
public void updateLogicalState(float tpf){
|
public void updateLogicalState(float tpf) {
|
||||||
runControlUpdate(tpf);
|
runControlUpdate(tpf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,19 +623,19 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see Spatial#getWorldTransform()
|
* @see Spatial#getWorldTransform()
|
||||||
* @see Spatial#getWorldBound()
|
* @see Spatial#getWorldBound()
|
||||||
*/
|
*/
|
||||||
public void updateGeometricState(){
|
public void updateGeometricState() {
|
||||||
// assume that this Spatial is a leaf, a proper implementation
|
// assume that this Spatial is a leaf, a proper implementation
|
||||||
// for this method should be provided by Node.
|
// for this method should be provided by Node.
|
||||||
|
|
||||||
// NOTE: Update world transforms first because
|
// NOTE: Update world transforms first because
|
||||||
// bound transform depends on them.
|
// bound transform depends on them.
|
||||||
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
if ((refreshFlags & RF_LIGHTLIST) != 0) {
|
||||||
updateWorldLightList();
|
updateWorldLightList();
|
||||||
}
|
}
|
||||||
if ((refreshFlags & RF_TRANSFORM) != 0){
|
if ((refreshFlags & RF_TRANSFORM) != 0) {
|
||||||
updateWorldTransforms();
|
updateWorldTransforms();
|
||||||
}
|
}
|
||||||
if ((refreshFlags & RF_BOUND) != 0){
|
if ((refreshFlags & RF_BOUND) != 0) {
|
||||||
updateWorldBound();
|
updateWorldBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,7 +831,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* spatial.
|
* spatial.
|
||||||
*/
|
*/
|
||||||
public void setLocalTranslation(float x, float y, float z) {
|
public void setLocalTranslation(float x, float y, float z) {
|
||||||
this.localTransform.setTranslation(x,y,z);
|
this.localTransform.setTranslation(x, y, z);
|
||||||
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
||||||
setTransformRefresh();
|
setTransformRefresh();
|
||||||
}
|
}
|
||||||
@ -866,7 +851,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return the local transform of this spatial.
|
* @return the local transform of this spatial.
|
||||||
*/
|
*/
|
||||||
public Transform getLocalTransform(){
|
public Transform getLocalTransform() {
|
||||||
return localTransform;
|
return localTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,7 +861,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @param material The material to set.
|
* @param material The material to set.
|
||||||
*/
|
*/
|
||||||
public void setMaterial(Material material){
|
public void setMaterial(Material material) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -885,7 +870,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @param light The light to add.
|
* @param light The light to add.
|
||||||
*/
|
*/
|
||||||
public void addLight(Light light){
|
public void addLight(Light light) {
|
||||||
localLights.add(light);
|
localLights.add(light);
|
||||||
setLightListRefresh();
|
setLightListRefresh();
|
||||||
}
|
}
|
||||||
@ -896,7 +881,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @param light The light to remove.
|
* @param light The light to remove.
|
||||||
* @see Spatial#addLight(com.jme3.light.Light)
|
* @see Spatial#addLight(com.jme3.light.Light)
|
||||||
*/
|
*/
|
||||||
public void removeLight(Light light){
|
public void removeLight(Light light) {
|
||||||
localLights.remove(light);
|
localLights.remove(light);
|
||||||
setLightListRefresh();
|
setLightListRefresh();
|
||||||
}
|
}
|
||||||
@ -906,7 +891,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial move(float x, float y, float z){
|
public Spatial move(float x, float y, float z) {
|
||||||
this.localTransform.getTranslation().addLocal(x, y, z);
|
this.localTransform.getTranslation().addLocal(x, y, z);
|
||||||
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
||||||
setTransformRefresh();
|
setTransformRefresh();
|
||||||
@ -919,7 +904,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial move(Vector3f offset){
|
public Spatial move(Vector3f offset) {
|
||||||
this.localTransform.getTranslation().addLocal(offset);
|
this.localTransform.getTranslation().addLocal(offset);
|
||||||
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
this.worldTransform.setTranslation(this.localTransform.getTranslation());
|
||||||
setTransformRefresh();
|
setTransformRefresh();
|
||||||
@ -932,8 +917,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial scale(float s){
|
public Spatial scale(float s) {
|
||||||
return scale(s,s,s);
|
return scale(s, s, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -941,8 +926,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial scale(float x, float y, float z){
|
public Spatial scale(float x, float y, float z) {
|
||||||
this.localTransform.getScale().multLocal(x,y,z);
|
this.localTransform.getScale().multLocal(x, y, z);
|
||||||
this.worldTransform.setScale(this.localTransform.getScale());
|
this.worldTransform.setScale(this.localTransform.getScale());
|
||||||
setTransformRefresh();
|
setTransformRefresh();
|
||||||
|
|
||||||
@ -954,7 +939,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial rotate(Quaternion rot){
|
public Spatial rotate(Quaternion rot) {
|
||||||
this.localTransform.getRotation().multLocal(rot);
|
this.localTransform.getRotation().multLocal(rot);
|
||||||
this.worldTransform.setRotation(this.localTransform.getRotation());
|
this.worldTransform.setRotation(this.localTransform.getRotation());
|
||||||
setTransformRefresh();
|
setTransformRefresh();
|
||||||
@ -968,7 +953,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial rotate(float yaw, float roll, float pitch){
|
public Spatial rotate(float yaw, float roll, float pitch) {
|
||||||
assert TempVars.get().lock();
|
assert TempVars.get().lock();
|
||||||
Quaternion q = TempVars.get().quat1;
|
Quaternion q = TempVars.get().quat1;
|
||||||
q.fromAngles(yaw, roll, pitch);
|
q.fromAngles(yaw, roll, pitch);
|
||||||
@ -982,7 +967,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* Centers the spatial in the origin of the world bound.
|
* Centers the spatial in the origin of the world bound.
|
||||||
* @return The spatial on which this method is called, e.g <code>this</code>.
|
* @return The spatial on which this method is called, e.g <code>this</code>.
|
||||||
*/
|
*/
|
||||||
public Spatial center(){
|
public Spatial center() {
|
||||||
Vector3f worldTrans = getWorldTranslation();
|
Vector3f worldTrans = getWorldTranslation();
|
||||||
Vector3f worldCenter = getWorldBound().getCenter();
|
Vector3f worldCenter = getWorldBound().getCenter();
|
||||||
|
|
||||||
@ -998,15 +983,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* the cullmode of it's parent.
|
* the cullmode of it's parent.
|
||||||
*/
|
*/
|
||||||
public CullHint getCullHint() {
|
public CullHint getCullHint() {
|
||||||
if (cullHint != CullHint.Inherit)
|
if (cullHint != CullHint.Inherit) {
|
||||||
return cullHint;
|
return cullHint;
|
||||||
else if (parent != null)
|
} else if (parent != null) {
|
||||||
return parent.getCullHint();
|
return parent.getCullHint();
|
||||||
else
|
} else {
|
||||||
return CullHint.Dynamic;
|
return CullHint.Dynamic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this spatial's renderqueue bucket. If the mode is set to inherit,
|
* Returns this spatial's renderqueue bucket. If the mode is set to inherit,
|
||||||
* then the spatial gets its renderqueue bucket from its parent.
|
* then the spatial gets its renderqueue bucket from its parent.
|
||||||
@ -1014,12 +999,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @return The spatial's current renderqueue mode.
|
* @return The spatial's current renderqueue mode.
|
||||||
*/
|
*/
|
||||||
public RenderQueue.Bucket getQueueBucket() {
|
public RenderQueue.Bucket getQueueBucket() {
|
||||||
if (queueBucket != RenderQueue.Bucket.Inherit)
|
if (queueBucket != RenderQueue.Bucket.Inherit) {
|
||||||
return queueBucket;
|
return queueBucket;
|
||||||
else if (parent != null)
|
} else if (parent != null) {
|
||||||
return parent.getQueueBucket();
|
return parent.getQueueBucket();
|
||||||
else
|
} else {
|
||||||
return RenderQueue.Bucket.Opaque;
|
return RenderQueue.Bucket.Opaque;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1030,12 +1016,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see ShadowMode
|
* @see ShadowMode
|
||||||
*/
|
*/
|
||||||
public RenderQueue.ShadowMode getShadowMode() {
|
public RenderQueue.ShadowMode getShadowMode() {
|
||||||
if (shadowMode != RenderQueue.ShadowMode.Inherit)
|
if (shadowMode != RenderQueue.ShadowMode.Inherit) {
|
||||||
return shadowMode;
|
return shadowMode;
|
||||||
else if (parent != null)
|
} else if (parent != null) {
|
||||||
return parent.getShadowMode();
|
return parent.getShadowMode();
|
||||||
else
|
} else {
|
||||||
return ShadowMode.Off;
|
return ShadowMode.Off;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1044,7 +1031,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @param lod The lod level to set.
|
* @param lod The lod level to set.
|
||||||
*/
|
*/
|
||||||
public void setLodLevel(int lod){
|
public void setLodLevel(int lod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1083,11 +1070,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @see Mesh#cloneForAnim()
|
* @see Mesh#cloneForAnim()
|
||||||
*/
|
*/
|
||||||
public Spatial clone(boolean cloneMaterial){
|
public Spatial clone(boolean cloneMaterial) {
|
||||||
try{
|
try {
|
||||||
Spatial clone = (Spatial) super.clone();
|
Spatial clone = (Spatial) super.clone();
|
||||||
if (worldBound != null)
|
if (worldBound != null) {
|
||||||
clone.worldBound = worldBound.clone();
|
clone.worldBound = worldBound.clone();
|
||||||
|
}
|
||||||
clone.worldLights = worldLights.clone();
|
clone.worldLights = worldLights.clone();
|
||||||
clone.localLights = localLights.clone();
|
clone.localLights = localLights.clone();
|
||||||
|
|
||||||
@ -1098,11 +1086,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
clone.worldTransform = worldTransform.clone();
|
clone.worldTransform = worldTransform.clone();
|
||||||
clone.localTransform = localTransform.clone();
|
clone.localTransform = localTransform.clone();
|
||||||
|
|
||||||
if (clone instanceof Node){
|
if (clone instanceof Node) {
|
||||||
Node node = (Node) this;
|
Node node = (Node) this;
|
||||||
Node nodeClone = (Node) clone;
|
Node nodeClone = (Node) clone;
|
||||||
nodeClone.children = new ArrayList<Spatial>();
|
nodeClone.children = new ArrayList<Spatial>();
|
||||||
for (Spatial child : node.children){
|
for (Spatial child : node.children) {
|
||||||
Spatial childClone = child.clone(cloneMaterial);
|
Spatial childClone = child.clone(cloneMaterial);
|
||||||
childClone.parent = nodeClone;
|
childClone.parent = nodeClone;
|
||||||
nodeClone.children.add(childClone);
|
nodeClone.children.add(childClone);
|
||||||
@ -1115,16 +1103,16 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
clone.setLightListRefresh();
|
clone.setLightListRefresh();
|
||||||
|
|
||||||
clone.controls = new ArrayList<Control>();
|
clone.controls = new ArrayList<Control>();
|
||||||
for (int i = 0; i < controls.size(); i++){
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
clone.controls.add( controls.get(i).cloneForSpatial(clone) );
|
clone.controls.add(controls.get(i).cloneForSpatial(clone));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userData != null){
|
if (userData != null) {
|
||||||
clone.userData = (HashMap<String, Savable>) userData.clone();
|
clone.userData = (HashMap<String, Savable>) userData.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}catch (CloneNotSupportedException ex){
|
} catch (CloneNotSupportedException ex) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1142,7 +1130,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see Mesh#cloneForAnim()
|
* @see Mesh#cloneForAnim()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Spatial clone(){
|
public Spatial clone() {
|
||||||
return clone(true);
|
return clone(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1155,33 +1143,36 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*/
|
*/
|
||||||
public abstract Spatial deepClone();
|
public abstract Spatial deepClone();
|
||||||
|
|
||||||
public void setUserData(String key, Object data){
|
public void setUserData(String key, Object data) {
|
||||||
if (userData == null)
|
if (userData == null) {
|
||||||
userData = new HashMap<String, Savable>();
|
userData = new HashMap<String, Savable>();
|
||||||
|
}
|
||||||
|
|
||||||
if (data instanceof Savable){
|
if (data instanceof Savable) {
|
||||||
userData.put(key, (Savable) data);
|
userData.put(key, (Savable) data);
|
||||||
}else{
|
} else {
|
||||||
userData.put(key, new UserData(UserData.getObjectType(data), data));
|
userData.put(key, new UserData(UserData.getObjectType(data), data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getUserData(String key){
|
public Object getUserData(String key) {
|
||||||
if (userData == null)
|
if (userData == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Savable s = userData.get(key);
|
Savable s = userData.get(key);
|
||||||
if (s instanceof UserData){
|
if (s instanceof UserData) {
|
||||||
return ((UserData)s).getValue();
|
return ((UserData) s).getValue();
|
||||||
}else{
|
} else {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getUserDataKeys(){
|
public Collection<String> getUserDataKeys() {
|
||||||
if (userData != null)
|
if (userData != null) {
|
||||||
return userData.keySet();
|
return userData.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
return Collections.EMPTY_SET;
|
return Collections.EMPTY_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1202,12 +1193,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
* @see java.util.regex.Pattern
|
* @see java.util.regex.Pattern
|
||||||
*/
|
*/
|
||||||
public boolean matches(Class<? extends Spatial> spatialSubclass,
|
public boolean matches(Class<? extends Spatial> spatialSubclass,
|
||||||
String nameRegex) {
|
String nameRegex) {
|
||||||
if (spatialSubclass != null && !spatialSubclass.isInstance(this))
|
if (spatialSubclass != null && !spatialSubclass.isInstance(this)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
if (nameRegex != null && (name == null || !name.matches(nameRegex)))
|
|
||||||
|
if (nameRegex != null && (name == null || !name.matches(nameRegex))) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1232,16 +1225,22 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
|
worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
|
||||||
cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit);
|
cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit);
|
||||||
queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
|
queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
|
||||||
RenderQueue.Bucket.Inherit);
|
RenderQueue.Bucket.Inherit);
|
||||||
shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
|
shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
|
||||||
ShadowMode.Inherit);
|
ShadowMode.Inherit);
|
||||||
|
|
||||||
localTransform = (Transform) ic.readSavable("transform", Transform.Identity);
|
localTransform = (Transform) ic.readSavable("transform", Transform.Identity);
|
||||||
|
|
||||||
localLights = (LightList) ic.readSavable("lights", null);
|
localLights = (LightList) ic.readSavable("lights", null);
|
||||||
localLights.setOwner(this);
|
localLights.setOwner(this);
|
||||||
|
|
||||||
controls = ic.readSavableArrayList("controlsList", null);
|
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
||||||
|
//the AnimControl creates the SkeletonControl for old files and add it to the spatial.
|
||||||
|
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
|
||||||
|
//When backward compatibility won't be needed anymore this can be replaced by :
|
||||||
|
//controls = ic.readSavableArrayList("controlsList", null));
|
||||||
|
controls.addAll(0, ic.readSavableArrayList("controlsList", null));
|
||||||
|
|
||||||
userData = (HashMap<String, Savable>) ic.readStringSavableMap("user_data", null);
|
userData = (HashMap<String, Savable>) ic.readStringSavableMap("user_data", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,7 +1314,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
*
|
*
|
||||||
* @param shadowMode The local shadow mode to set.
|
* @param shadowMode The local shadow mode to set.
|
||||||
*/
|
*/
|
||||||
public void setShadowMode(RenderQueue.ShadowMode shadowMode){
|
public void setShadowMode(RenderQueue.ShadowMode shadowMode) {
|
||||||
this.shadowMode = shadowMode;
|
this.shadowMode = shadowMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1398,13 +1397,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
store.setTranslation(getWorldTranslation());
|
store.setTranslation(getWorldTranslation());
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit each scene graph element ordered by DFS
|
* Visit each scene graph element ordered by DFS
|
||||||
* @param visitor
|
* @param visitor
|
||||||
*/
|
*/
|
||||||
public abstract void depthFirstTraversal(SceneGraphVisitor visitor);
|
public abstract void depthFirstTraversal(SceneGraphVisitor visitor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit each scene graph element ordered by BFS
|
* Visit each scene graph element ordered by BFS
|
||||||
* @param visitor
|
* @param visitor
|
||||||
@ -1412,14 +1411,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable {
|
|||||||
public void breadthFirstTraversal(SceneGraphVisitor visitor) {
|
public void breadthFirstTraversal(SceneGraphVisitor visitor) {
|
||||||
Queue<Spatial> queue = new LinkedList<Spatial>();
|
Queue<Spatial> queue = new LinkedList<Spatial>();
|
||||||
queue.add(this);
|
queue.add(this);
|
||||||
|
|
||||||
while (!queue.isEmpty()) {
|
while (!queue.isEmpty()) {
|
||||||
Spatial s = queue.poll();
|
Spatial s = queue.poll();
|
||||||
visitor.visit(s);
|
visitor.visit(s);
|
||||||
s.breadthFirstTraversal(visitor, queue);
|
s.breadthFirstTraversal(visitor, queue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue);
|
protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.jme3.bullet.control;
|
|||||||
import com.jme3.animation.AnimControl;
|
import com.jme3.animation.AnimControl;
|
||||||
import com.jme3.animation.Bone;
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.animation.Skeleton;
|
import com.jme3.animation.Skeleton;
|
||||||
|
import com.jme3.animation.SkeletonControl;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.bullet.PhysicsSpace;
|
import com.jme3.bullet.PhysicsSpace;
|
||||||
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
||||||
@ -107,6 +108,15 @@ public class RagdollControl implements PhysicsControl {
|
|||||||
|
|
||||||
public void setSpatial(Spatial model) {
|
public void setSpatial(Spatial model) {
|
||||||
targetModel = model;
|
targetModel = model;
|
||||||
|
|
||||||
|
//HACK ALERT change this
|
||||||
|
//I remove the skeletonControl and readd it to the spatial to make sure it's after the ragdollControl in the stack
|
||||||
|
//Find a proper way to order the controls.
|
||||||
|
SkeletonControl sc = model.getControl(SkeletonControl.class);
|
||||||
|
model.removeControl(sc);
|
||||||
|
model.addControl(sc);
|
||||||
|
//----
|
||||||
|
|
||||||
removeFromPhysicsSpace();
|
removeFromPhysicsSpace();
|
||||||
clearData();
|
clearData();
|
||||||
// put into bind pose and compute bone transforms in model space
|
// put into bind pose and compute bone transforms in model space
|
||||||
|
@ -29,11 +29,11 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.jme3.scene.plugins.ogre;
|
package com.jme3.scene.plugins.ogre;
|
||||||
|
|
||||||
import com.jme3.animation.AnimControl;
|
import com.jme3.animation.AnimControl;
|
||||||
import com.jme3.animation.BoneAnimation;
|
import com.jme3.animation.BoneAnimation;
|
||||||
|
import com.jme3.animation.SkeletonControl;
|
||||||
import com.jme3.asset.AssetInfo;
|
import com.jme3.asset.AssetInfo;
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
@ -81,15 +81,12 @@ import static com.jme3.util.xml.SAXUtil.*;
|
|||||||
public class MeshLoader extends DefaultHandler implements AssetLoader {
|
public class MeshLoader extends DefaultHandler implements AssetLoader {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
|
private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
|
||||||
|
|
||||||
public static boolean AUTO_INTERLEAVE = true;
|
public static boolean AUTO_INTERLEAVE = true;
|
||||||
public static boolean HARDWARE_SKINNING = false;
|
public static boolean HARDWARE_SKINNING = false;
|
||||||
|
|
||||||
private String meshName;
|
private String meshName;
|
||||||
private String folderName;
|
private String folderName;
|
||||||
private AssetManager assetManager;
|
private AssetManager assetManager;
|
||||||
private MaterialList materialList;
|
private MaterialList materialList;
|
||||||
|
|
||||||
private ShortBuffer sb;
|
private ShortBuffer sb;
|
||||||
private IntBuffer ib;
|
private IntBuffer ib;
|
||||||
private FloatBuffer fb;
|
private FloatBuffer fb;
|
||||||
@ -105,16 +102,14 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
private boolean bigindices = false;
|
private boolean bigindices = false;
|
||||||
private int vertCount;
|
private int vertCount;
|
||||||
private int triCount;
|
private int triCount;
|
||||||
|
|
||||||
private List<Geometry> geoms = new ArrayList<Geometry>();
|
private List<Geometry> geoms = new ArrayList<Geometry>();
|
||||||
private List<Boolean> usesSharedGeom = new ArrayList<Boolean>();
|
private List<Boolean> usesSharedGeom = new ArrayList<Boolean>();
|
||||||
private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
|
private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
|
||||||
private AnimData animData;
|
private AnimData animData;
|
||||||
|
|
||||||
private ByteBuffer indicesData;
|
private ByteBuffer indicesData;
|
||||||
private FloatBuffer weightsFloatData;
|
private FloatBuffer weightsFloatData;
|
||||||
|
|
||||||
public MeshLoader(){
|
public MeshLoader() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,44 +145,44 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
public void endDocument() {
|
public void endDocument() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushFace(String v1, String v2, String v3) throws SAXException{
|
private void pushFace(String v1, String v2, String v3) throws SAXException {
|
||||||
int i1 = parseInt(v1);
|
int i1 = parseInt(v1);
|
||||||
|
|
||||||
// TODO: fan/strip support
|
// TODO: fan/strip support
|
||||||
int i2 = parseInt(v2);
|
int i2 = parseInt(v2);
|
||||||
int i3 = parseInt(v3);
|
int i3 = parseInt(v3);
|
||||||
if (ib != null){
|
if (ib != null) {
|
||||||
ib.put(i1).put(i2).put(i3);
|
ib.put(i1).put(i2).put(i3);
|
||||||
}else{
|
} else {
|
||||||
sb.put((short)i1).put((short)i2).put((short)i3);
|
sb.put((short) i1).put((short) i2).put((short) i3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startFaces(String count) throws SAXException{
|
private void startFaces(String count) throws SAXException {
|
||||||
int numFaces = parseInt(count);
|
int numFaces = parseInt(count);
|
||||||
int numIndices;
|
int numIndices;
|
||||||
|
|
||||||
if (mesh.getMode() == Mesh.Mode.Triangles){
|
if (mesh.getMode() == Mesh.Mode.Triangles) {
|
||||||
//mesh.setTriangleCount(numFaces);
|
//mesh.setTriangleCount(numFaces);
|
||||||
numIndices = numFaces * 3;
|
numIndices = numFaces * 3;
|
||||||
}else{
|
} else {
|
||||||
throw new SAXException("Triangle strip or fan not supported!");
|
throw new SAXException("Triangle strip or fan not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int numVerts;
|
int numVerts;
|
||||||
if (usesSharedGeom.size() > 0 && usesSharedGeom.get(geoms.size()-1)){
|
if (usesSharedGeom.size() > 0 && usesSharedGeom.get(geoms.size() - 1)) {
|
||||||
// sharedgeom.getMesh().updateCounts();
|
// sharedgeom.getMesh().updateCounts();
|
||||||
numVerts = sharedmesh.getVertexCount();
|
numVerts = sharedmesh.getVertexCount();
|
||||||
}else{
|
} else {
|
||||||
// mesh.updateCounts();
|
// mesh.updateCounts();
|
||||||
numVerts = mesh.getVertexCount();
|
numVerts = mesh.getVertexCount();
|
||||||
}
|
}
|
||||||
vb = new VertexBuffer(VertexBuffer.Type.Index);
|
vb = new VertexBuffer(VertexBuffer.Type.Index);
|
||||||
if (!bigindices){
|
if (!bigindices) {
|
||||||
sb = BufferUtils.createShortBuffer(numIndices);
|
sb = BufferUtils.createShortBuffer(numIndices);
|
||||||
ib = null;
|
ib = null;
|
||||||
vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
|
vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
|
||||||
}else{
|
} else {
|
||||||
ib = BufferUtils.createIntBuffer(numIndices);
|
ib = BufferUtils.createIntBuffer(numIndices);
|
||||||
sb = null;
|
sb = null;
|
||||||
vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
|
vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
|
||||||
@ -195,77 +190,81 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
mesh.setBuffer(vb);
|
mesh.setBuffer(vb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyMaterial(Geometry geom, String matName){
|
private void applyMaterial(Geometry geom, String matName) {
|
||||||
Material mat = null;
|
Material mat = null;
|
||||||
if (matName.endsWith(".j3m")){
|
if (matName.endsWith(".j3m")) {
|
||||||
// load as native jme3 material instance
|
// load as native jme3 material instance
|
||||||
mat = assetManager.loadMaterial(matName);
|
mat = assetManager.loadMaterial(matName);
|
||||||
}else{
|
} else {
|
||||||
if (materialList != null){
|
if (materialList != null) {
|
||||||
mat = materialList.get(matName);
|
mat = materialList.get(matName);
|
||||||
}
|
}
|
||||||
if (mat == null){
|
if (mat == null) {
|
||||||
logger.log(Level.WARNING, "Material {0} not found. Applying default material", matName);
|
logger.log(Level.WARNING, "Material {0} not found. Applying default material", matName);
|
||||||
mat = (Material) assetManager.loadAsset(new AssetKey("Common/Materials/RedColor.j3m"));
|
mat = (Material) assetManager.loadAsset(new AssetKey("Common/Materials/RedColor.j3m"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mat == null)
|
|
||||||
throw new RuntimeException("Cannot locate material named " + matName);
|
|
||||||
|
|
||||||
if (mat.isTransparent())
|
if (mat == null) {
|
||||||
|
throw new RuntimeException("Cannot locate material named " + matName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mat.isTransparent()) {
|
||||||
geom.setQueueBucket(Bucket.Transparent);
|
geom.setQueueBucket(Bucket.Transparent);
|
||||||
|
}
|
||||||
// else
|
// else
|
||||||
// geom.setShadowMode(ShadowMode.CastAndReceive);
|
// geom.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
|
||||||
// if (mat.isReceivesShadows())
|
// if (mat.isReceivesShadows())
|
||||||
|
|
||||||
|
|
||||||
geom.setMaterial(mat);
|
geom.setMaterial(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException{
|
private void startMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
|
||||||
mesh = new Mesh();
|
mesh = new Mesh();
|
||||||
if (opType == null || opType.equals("triangle_list")){
|
if (opType == null || opType.equals("triangle_list")) {
|
||||||
mesh.setMode(Mesh.Mode.Triangles);
|
mesh.setMode(Mesh.Mode.Triangles);
|
||||||
}else if (opType.equals("triangle_strip")){
|
} else if (opType.equals("triangle_strip")) {
|
||||||
mesh.setMode(Mesh.Mode.TriangleStrip);
|
mesh.setMode(Mesh.Mode.TriangleStrip);
|
||||||
}else if (opType.equals("triangle_fan")){
|
} else if (opType.equals("triangle_fan")) {
|
||||||
mesh.setMode(Mesh.Mode.TriangleFan);
|
mesh.setMode(Mesh.Mode.TriangleFan);
|
||||||
}
|
}
|
||||||
|
|
||||||
bigindices = parseBool(use32bitIndices, false);
|
bigindices = parseBool(use32bitIndices, false);
|
||||||
boolean sharedverts = parseBool(usesharedvertices, false);
|
boolean sharedverts = parseBool(usesharedvertices, false);
|
||||||
if (sharedverts){
|
if (sharedverts) {
|
||||||
usesSharedGeom.add(true);
|
usesSharedGeom.add(true);
|
||||||
// import vertexbuffers from shared geom
|
// import vertexbuffers from shared geom
|
||||||
IntMap<VertexBuffer> sharedBufs = sharedmesh.getBuffers();
|
IntMap<VertexBuffer> sharedBufs = sharedmesh.getBuffers();
|
||||||
for (Entry<VertexBuffer> entry : sharedBufs){
|
for (Entry<VertexBuffer> entry : sharedBufs) {
|
||||||
mesh.setBuffer(entry.getValue());
|
mesh.setBuffer(entry.getValue());
|
||||||
}
|
}
|
||||||
// this mesh is shared!
|
// this mesh is shared!
|
||||||
}else{
|
} else {
|
||||||
usesSharedGeom.add(false);
|
usesSharedGeom.add(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meshName == null)
|
if (meshName == null) {
|
||||||
geom = new Geometry("OgreSubmesh-"+(++geomIdx), mesh);
|
geom = new Geometry("OgreSubmesh-" + (++geomIdx), mesh);
|
||||||
else
|
} else {
|
||||||
geom = new Geometry(meshName+"-geom-"+(++geomIdx), mesh);
|
geom = new Geometry(meshName + "-geom-" + (++geomIdx), mesh);
|
||||||
|
}
|
||||||
|
|
||||||
applyMaterial(geom, matName);
|
applyMaterial(geom, matName);
|
||||||
geoms.add(geom);
|
geoms.add(geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startSharedGeom(String vertexcount) throws SAXException{
|
private void startSharedGeom(String vertexcount) throws SAXException {
|
||||||
sharedmesh = new Mesh();
|
sharedmesh = new Mesh();
|
||||||
vertCount = parseInt(vertexcount);
|
vertCount = parseInt(vertexcount);
|
||||||
// sharedmesh.setVertexCount(vertCount);
|
// sharedmesh.setVertexCount(vertCount);
|
||||||
|
|
||||||
if (meshName == null)
|
if (meshName == null) {
|
||||||
sharedgeom = new Geometry("Ogre-SharedGeom", sharedmesh);
|
sharedgeom = new Geometry("Ogre-SharedGeom", sharedmesh);
|
||||||
else
|
} else {
|
||||||
sharedgeom = new Geometry(meshName+"-sharedgeom", sharedmesh);
|
sharedgeom = new Geometry(meshName + "-sharedgeom", sharedmesh);
|
||||||
|
}
|
||||||
|
|
||||||
sharedgeom.setCullHint(CullHint.Always);
|
sharedgeom.setCullHint(CullHint.Always);
|
||||||
geoms.add(sharedgeom);
|
geoms.add(sharedgeom);
|
||||||
@ -275,7 +274,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
mesh = sharedmesh;
|
mesh = sharedmesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startGeometry(String vertexcount) throws SAXException{
|
private void startGeometry(String vertexcount) throws SAXException {
|
||||||
vertCount = parseInt(vertexcount);
|
vertCount = parseInt(vertexcount);
|
||||||
// mesh.setVertexCount(vertCount);
|
// mesh.setVertexCount(vertCount);
|
||||||
}
|
}
|
||||||
@ -284,51 +283,51 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
* Normalizes weights if needed and finds largest amount of weights used
|
* Normalizes weights if needed and finds largest amount of weights used
|
||||||
* for all vertices in the buffer.
|
* for all vertices in the buffer.
|
||||||
*/
|
*/
|
||||||
private void endBoneAssigns(){
|
private void endBoneAssigns() {
|
||||||
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)){
|
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//int vertCount = mesh.getVertexCount();
|
//int vertCount = mesh.getVertexCount();
|
||||||
int maxWeightsPerVert = 0;
|
int maxWeightsPerVert = 0;
|
||||||
weightsFloatData.rewind();
|
weightsFloatData.rewind();
|
||||||
for (int v = 0; v < vertCount; v++){
|
for (int v = 0; v < vertCount; v++) {
|
||||||
float w0 = weightsFloatData.get(),
|
float w0 = weightsFloatData.get(),
|
||||||
w1 = weightsFloatData.get(),
|
w1 = weightsFloatData.get(),
|
||||||
w2 = weightsFloatData.get(),
|
w2 = weightsFloatData.get(),
|
||||||
w3 = weightsFloatData.get();
|
w3 = weightsFloatData.get();
|
||||||
|
|
||||||
if (w3 != 0){
|
if (w3 != 0) {
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
|
||||||
}else if (w2 != 0){
|
} else if (w2 != 0) {
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
|
||||||
}else if (w1 != 0){
|
} else if (w1 != 0) {
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
|
||||||
}else if (w0 != 0){
|
} else if (w0 != 0) {
|
||||||
maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
|
maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float sum = w0 + w1 + w2 + w3;
|
float sum = w0 + w1 + w2 + w3;
|
||||||
if (sum != 1f){
|
if (sum != 1f) {
|
||||||
weightsFloatData.position(weightsFloatData.position()-4);
|
weightsFloatData.position(weightsFloatData.position() - 4);
|
||||||
// compute new vals based on sum
|
// compute new vals based on sum
|
||||||
float sumToB = 1f / sum;
|
float sumToB = 1f / sum;
|
||||||
weightsFloatData.put( w0 * sumToB );
|
weightsFloatData.put(w0 * sumToB);
|
||||||
weightsFloatData.put( w1 * sumToB );
|
weightsFloatData.put(w1 * sumToB);
|
||||||
weightsFloatData.put( w2 * sumToB );
|
weightsFloatData.put(w2 * sumToB);
|
||||||
weightsFloatData.put( w3 * sumToB );
|
weightsFloatData.put(w3 * sumToB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
weightsFloatData.rewind();
|
weightsFloatData.rewind();
|
||||||
|
|
||||||
weightsFloatData = null;
|
weightsFloatData = null;
|
||||||
indicesData = null;
|
indicesData = null;
|
||||||
|
|
||||||
mesh.setMaxNumWeights(maxWeightsPerVert);
|
mesh.setMaxNumWeights(maxWeightsPerVert);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startBoneAssigns(){
|
private void startBoneAssigns() {
|
||||||
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)){
|
if (mesh != sharedmesh && usesSharedGeom.get(geoms.size() - 1)) {
|
||||||
// will use bone assignments from shared mesh (?)
|
// will use bone assignments from shared mesh (?)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -338,53 +337,53 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
// each vertex has
|
// each vertex has
|
||||||
// - 4 bone weights
|
// - 4 bone weights
|
||||||
// - 4 bone indices
|
// - 4 bone indices
|
||||||
if (HARDWARE_SKINNING){
|
if (HARDWARE_SKINNING) {
|
||||||
weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
|
weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
|
||||||
indicesData = BufferUtils.createByteBuffer(vertCount * 4);
|
indicesData = BufferUtils.createByteBuffer(vertCount * 4);
|
||||||
}else{
|
} else {
|
||||||
// create array-backed buffers if software skinning for access speed
|
// create array-backed buffers if software skinning for access speed
|
||||||
weightsFloatData = FloatBuffer.allocate(vertCount * 4);
|
weightsFloatData = FloatBuffer.allocate(vertCount * 4);
|
||||||
indicesData = ByteBuffer.allocate(vertCount * 4);
|
indicesData = ByteBuffer.allocate(vertCount * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
|
VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
|
||||||
VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
|
VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
|
||||||
|
|
||||||
Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly;
|
Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly;
|
||||||
weights.setupData(usage, 4, Format.Float, weightsFloatData);
|
weights.setupData(usage, 4, Format.Float, weightsFloatData);
|
||||||
indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
|
indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
|
||||||
|
|
||||||
mesh.setBuffer(weights);
|
mesh.setBuffer(weights);
|
||||||
mesh.setBuffer(indices);
|
mesh.setBuffer(indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startVertexBuffer(Attributes attribs) throws SAXException{
|
private void startVertexBuffer(Attributes attribs) throws SAXException {
|
||||||
if (parseBool(attribs.getValue("positions"), false)){
|
if (parseBool(attribs.getValue("positions"), false)) {
|
||||||
vb = new VertexBuffer(Type.Position);
|
vb = new VertexBuffer(Type.Position);
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
||||||
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
||||||
mesh.setBuffer(vb);
|
mesh.setBuffer(vb);
|
||||||
}
|
}
|
||||||
if (parseBool(attribs.getValue("normals"), false)){
|
if (parseBool(attribs.getValue("normals"), false)) {
|
||||||
vb = new VertexBuffer(Type.Normal);
|
vb = new VertexBuffer(Type.Normal);
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
||||||
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
||||||
mesh.setBuffer(vb);
|
mesh.setBuffer(vb);
|
||||||
}
|
}
|
||||||
if (parseBool(attribs.getValue("colours_diffuse"), false)){
|
if (parseBool(attribs.getValue("colours_diffuse"), false)) {
|
||||||
vb = new VertexBuffer(Type.Color);
|
vb = new VertexBuffer(Type.Color);
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * 4);
|
fb = BufferUtils.createFloatBuffer(vertCount * 4);
|
||||||
vb.setupData(Usage.Static, 4, Format.Float, fb);
|
vb.setupData(Usage.Static, 4, Format.Float, fb);
|
||||||
mesh.setBuffer(vb);
|
mesh.setBuffer(vb);
|
||||||
}
|
}
|
||||||
if (parseBool(attribs.getValue("tangents"), false)){
|
if (parseBool(attribs.getValue("tangents"), false)) {
|
||||||
int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
|
int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
|
||||||
vb = new VertexBuffer(Type.Tangent);
|
vb = new VertexBuffer(Type.Tangent);
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
|
fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
|
||||||
vb.setupData(Usage.Static, dimensions, Format.Float, fb);
|
vb.setupData(Usage.Static, dimensions, Format.Float, fb);
|
||||||
mesh.setBuffer(vb);
|
mesh.setBuffer(vb);
|
||||||
}
|
}
|
||||||
if (parseBool(attribs.getValue("binormals"), false)){
|
if (parseBool(attribs.getValue("binormals"), false)) {
|
||||||
vb = new VertexBuffer(Type.Binormal);
|
vb = new VertexBuffer(Type.Binormal);
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
fb = BufferUtils.createFloatBuffer(vertCount * 3);
|
||||||
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
vb.setupData(Usage.Static, 3, Format.Float, fb);
|
||||||
@ -392,17 +391,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
|
int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
|
||||||
for (int i = 0; i < texCoords; i++){
|
for (int i = 0; i < texCoords; i++) {
|
||||||
int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
|
int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
|
||||||
if (dims < 1 || dims > 4)
|
if (dims < 1 || dims > 4) {
|
||||||
throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
|
throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
|
||||||
|
}
|
||||||
|
|
||||||
if (i >= 2)
|
if (i >= 2) {
|
||||||
throw new SAXException("More than 2 texture coordinates not supported");
|
throw new SAXException("More than 2 texture coordinates not supported");
|
||||||
|
}
|
||||||
|
|
||||||
if (i == 0){
|
if (i == 0) {
|
||||||
vb = new VertexBuffer(Type.TexCoord);
|
vb = new VertexBuffer(Type.TexCoord);
|
||||||
}else{
|
} else {
|
||||||
vb = new VertexBuffer(Type.TexCoord2);
|
vb = new VertexBuffer(Type.TexCoord2);
|
||||||
}
|
}
|
||||||
fb = BufferUtils.createFloatBuffer(vertCount * dims);
|
fb = BufferUtils.createFloatBuffer(vertCount * dims);
|
||||||
@ -411,51 +412,47 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startVertex(){
|
private void startVertex() {
|
||||||
texCoordIdx = 0;
|
texCoordIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushAttrib(Type type, Attributes attribs) throws SAXException{
|
private void pushAttrib(Type type, Attributes attribs) throws SAXException {
|
||||||
try {
|
try {
|
||||||
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
|
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
|
||||||
buf.put(parseFloat(attribs.getValue("x")))
|
buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
|
||||||
.put(parseFloat(attribs.getValue("y")))
|
} catch (Exception ex) {
|
||||||
.put(parseFloat(attribs.getValue("z")));
|
throw new SAXException("Failed to push attrib", ex);
|
||||||
} catch (Exception ex){
|
|
||||||
throw new SAXException("Failed to push attrib", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushTangent(Attributes attribs) throws SAXException{
|
private void pushTangent(Attributes attribs) throws SAXException {
|
||||||
try {
|
try {
|
||||||
VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
|
VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
|
||||||
FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
|
FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
|
||||||
buf.put(parseFloat(attribs.getValue("x")))
|
buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
|
||||||
.put(parseFloat(attribs.getValue("y")))
|
if (tangentBuf.getNumComponents() == 4) {
|
||||||
.put(parseFloat(attribs.getValue("z")));
|
|
||||||
if (tangentBuf.getNumComponents() == 4){
|
|
||||||
buf.put(parseFloat(attribs.getValue("w")));
|
buf.put(parseFloat(attribs.getValue("w")));
|
||||||
}
|
}
|
||||||
} catch (Exception ex){
|
} catch (Exception ex) {
|
||||||
throw new SAXException("Failed to push attrib", ex);
|
throw new SAXException("Failed to push attrib", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushTexCoord(Attributes attribs) throws SAXException{
|
private void pushTexCoord(Attributes attribs) throws SAXException {
|
||||||
if (texCoordIdx >= 2)
|
if (texCoordIdx >= 2) {
|
||||||
return; // TODO: More than 2 texcoords
|
return; // TODO: More than 2 texcoords
|
||||||
|
}
|
||||||
Type type = texCoordIdx == 0 ? Type.TexCoord : Type.TexCoord2;
|
Type type = texCoordIdx == 0 ? Type.TexCoord : Type.TexCoord2;
|
||||||
|
|
||||||
VertexBuffer tcvb = mesh.getBuffer(type);
|
VertexBuffer tcvb = mesh.getBuffer(type);
|
||||||
FloatBuffer buf = (FloatBuffer) tcvb.getData();
|
FloatBuffer buf = (FloatBuffer) tcvb.getData();
|
||||||
|
|
||||||
buf.put(parseFloat(attribs.getValue("u")));
|
buf.put(parseFloat(attribs.getValue("u")));
|
||||||
if (tcvb.getNumComponents() >= 2){
|
if (tcvb.getNumComponents() >= 2) {
|
||||||
buf.put(parseFloat(attribs.getValue("v")));
|
buf.put(parseFloat(attribs.getValue("v")));
|
||||||
if (tcvb.getNumComponents() >= 3){
|
if (tcvb.getNumComponents() >= 3) {
|
||||||
buf.put(parseFloat(attribs.getValue("w")));
|
buf.put(parseFloat(attribs.getValue("w")));
|
||||||
if (tcvb.getNumComponents() == 4){
|
if (tcvb.getNumComponents() == 4) {
|
||||||
buf.put(parseFloat(attribs.getValue("x")));
|
buf.put(parseFloat(attribs.getValue("x")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,36 +461,38 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
texCoordIdx++;
|
texCoordIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushColor(Attributes attribs) throws SAXException{
|
private void pushColor(Attributes attribs) throws SAXException {
|
||||||
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
|
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
|
||||||
String value = parseString(attribs.getValue("value"));
|
String value = parseString(attribs.getValue("value"));
|
||||||
String[] vals = value.split(" ");
|
String[] vals = value.split(" ");
|
||||||
if (vals.length != 3 && vals.length != 4)
|
if (vals.length != 3 && vals.length != 4) {
|
||||||
throw new SAXException("Color value must contain 3 or 4 components");
|
throw new SAXException("Color value must contain 3 or 4 components");
|
||||||
|
}
|
||||||
|
|
||||||
ColorRGBA color = new ColorRGBA();
|
ColorRGBA color = new ColorRGBA();
|
||||||
color.r = parseFloat(vals[0]);
|
color.r = parseFloat(vals[0]);
|
||||||
color.g = parseFloat(vals[1]);
|
color.g = parseFloat(vals[1]);
|
||||||
color.b = parseFloat(vals[2]);
|
color.b = parseFloat(vals[2]);
|
||||||
if (vals.length == 3)
|
if (vals.length == 3) {
|
||||||
color.a = 1f;
|
color.a = 1f;
|
||||||
else
|
} else {
|
||||||
color.a = parseFloat(vals[3]);
|
color.a = parseFloat(vals[3]);
|
||||||
|
}
|
||||||
|
|
||||||
buf.put(color.r).put(color.g).put(color.b).put(color.a);
|
buf.put(color.r).put(color.g).put(color.b).put(color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLodFaceList(String submeshindex, String numfaces){
|
private void startLodFaceList(String submeshindex, String numfaces) {
|
||||||
int index = Integer.parseInt(submeshindex);
|
int index = Integer.parseInt(submeshindex);
|
||||||
int faceCount = Integer.parseInt(numfaces);
|
int faceCount = Integer.parseInt(numfaces);
|
||||||
|
|
||||||
vb = new VertexBuffer(VertexBuffer.Type.Index);
|
vb = new VertexBuffer(VertexBuffer.Type.Index);
|
||||||
sb = BufferUtils.createShortBuffer(faceCount * 3);
|
sb = BufferUtils.createShortBuffer(faceCount * 3);
|
||||||
ib = null;
|
ib = null;
|
||||||
vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
|
vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
|
||||||
|
|
||||||
List<VertexBuffer> levels = lodLevels.get(index);
|
List<VertexBuffer> levels = lodLevels.get(index);
|
||||||
if (levels == null){
|
if (levels == null) {
|
||||||
levels = new ArrayList<VertexBuffer>();
|
levels = new ArrayList<VertexBuffer>();
|
||||||
Mesh submesh = geoms.get(index).getMesh();
|
Mesh submesh = geoms.get(index).getMesh();
|
||||||
levels.add(submesh.getBuffer(Type.Index));
|
levels.add(submesh.getBuffer(Type.Index));
|
||||||
@ -503,13 +502,13 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
levels.add(vb);
|
levels.add(vb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLevelOfDetail(String numlevels){
|
private void startLevelOfDetail(String numlevels) {
|
||||||
// numLevels = Integer.parseInt(numlevels);
|
// numLevels = Integer.parseInt(numlevels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endLevelOfDetail(){
|
private void endLevelOfDetail() {
|
||||||
// set the lod data for each mesh
|
// set the lod data for each mesh
|
||||||
for (Entry<List<VertexBuffer>> entry : lodLevels){
|
for (Entry<List<VertexBuffer>> entry : lodLevels) {
|
||||||
Mesh m = geoms.get(entry.getKey()).getMesh();
|
Mesh m = geoms.get(entry.getKey()).getMesh();
|
||||||
List<VertexBuffer> levels = entry.getValue();
|
List<VertexBuffer> levels = entry.getValue();
|
||||||
VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
|
VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
|
||||||
@ -518,11 +517,11 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLodGenerated(String depthsqr){
|
private void startLodGenerated(String depthsqr) {
|
||||||
// dist = Float.parseFloat(depthsqr);
|
// dist = Float.parseFloat(depthsqr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException{
|
private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException {
|
||||||
int vert = parseInt(vertIndex);
|
int vert = parseInt(vertIndex);
|
||||||
float w = parseFloat(weight);
|
float w = parseFloat(weight);
|
||||||
byte bone = (byte) parseInt(boneIndex);
|
byte bone = (byte) parseInt(boneIndex);
|
||||||
@ -532,95 +531,101 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
|
|
||||||
int i;
|
int i;
|
||||||
// see which weights are unused for a given bone
|
// see which weights are unused for a given bone
|
||||||
for (i = vert * 4; i < vert * 4 + 4; i++){
|
for (i = vert * 4; i < vert * 4 + 4; i++) {
|
||||||
float v = weightsFloatData.get(i);
|
float v = weightsFloatData.get(i);
|
||||||
if (v == 0)
|
if (v == 0) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
weightsFloatData.put(i, w);
|
weightsFloatData.put(i, w);
|
||||||
indicesData.put(i, bone);
|
indicesData.put(i, bone);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startSkeleton(String name){
|
private void startSkeleton(String name) {
|
||||||
animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml");
|
animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml");
|
||||||
//TODO:workaround for meshxml / mesh.xml
|
//TODO:workaround for meshxml / mesh.xml
|
||||||
if(animData==null)
|
if (animData == null) {
|
||||||
animData = (AnimData) assetManager.loadAsset(folderName + name + "xml");
|
animData = (AnimData) assetManager.loadAsset(folderName + name + "xml");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startSubmeshName(String indexStr, String nameStr){
|
private void startSubmeshName(String indexStr, String nameStr) {
|
||||||
int index = Integer.parseInt(indexStr);
|
int index = Integer.parseInt(indexStr);
|
||||||
geoms.get(index).setName(nameStr);
|
geoms.get(index).setName(nameStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{
|
public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
|
||||||
if (ignoreUntilEnd != null)
|
if (ignoreUntilEnd != null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (qName.equals("texcoord")){
|
if (qName.equals("texcoord")) {
|
||||||
pushTexCoord(attribs);
|
pushTexCoord(attribs);
|
||||||
}else if (qName.equals("vertexboneassignment")){
|
} else if (qName.equals("vertexboneassignment")) {
|
||||||
pushBoneAssign(attribs.getValue("vertexindex"),
|
pushBoneAssign(attribs.getValue("vertexindex"),
|
||||||
attribs.getValue("boneindex"),
|
attribs.getValue("boneindex"),
|
||||||
attribs.getValue("weight"));
|
attribs.getValue("weight"));
|
||||||
}else if (qName.equals("face")){
|
} else if (qName.equals("face")) {
|
||||||
pushFace(attribs.getValue("v1"),
|
pushFace(attribs.getValue("v1"),
|
||||||
attribs.getValue("v2"),
|
attribs.getValue("v2"),
|
||||||
attribs.getValue("v3"));
|
attribs.getValue("v3"));
|
||||||
}else if (qName.equals("position")){
|
} else if (qName.equals("position")) {
|
||||||
pushAttrib(Type.Position, attribs);
|
pushAttrib(Type.Position, attribs);
|
||||||
}else if (qName.equals("normal")){
|
} else if (qName.equals("normal")) {
|
||||||
pushAttrib(Type.Normal, attribs);
|
pushAttrib(Type.Normal, attribs);
|
||||||
}else if (qName.equals("tangent")){
|
} else if (qName.equals("tangent")) {
|
||||||
pushTangent(attribs);
|
pushTangent(attribs);
|
||||||
}else if (qName.equals("binormal")){
|
} else if (qName.equals("binormal")) {
|
||||||
pushAttrib(Type.Binormal, attribs);
|
pushAttrib(Type.Binormal, attribs);
|
||||||
}else if (qName.equals("colour_diffuse")){
|
} else if (qName.equals("colour_diffuse")) {
|
||||||
pushColor(attribs);
|
pushColor(attribs);
|
||||||
}else if (qName.equals("vertex")){
|
} else if (qName.equals("vertex")) {
|
||||||
startVertex();
|
startVertex();
|
||||||
}else if (qName.equals("faces")){
|
} else if (qName.equals("faces")) {
|
||||||
startFaces(attribs.getValue("count"));
|
startFaces(attribs.getValue("count"));
|
||||||
}else if (qName.equals("geometry")){
|
} else if (qName.equals("geometry")) {
|
||||||
String count = attribs.getValue("vertexcount");
|
String count = attribs.getValue("vertexcount");
|
||||||
if (count == null)
|
if (count == null) {
|
||||||
count = attribs.getValue("count");
|
count = attribs.getValue("count");
|
||||||
|
}
|
||||||
|
|
||||||
startGeometry(count);
|
startGeometry(count);
|
||||||
}else if (qName.equals("vertexbuffer")){
|
} else if (qName.equals("vertexbuffer")) {
|
||||||
startVertexBuffer(attribs);
|
startVertexBuffer(attribs);
|
||||||
}else if (qName.equals("lodfacelist")){
|
} else if (qName.equals("lodfacelist")) {
|
||||||
startLodFaceList(attribs.getValue("submeshindex"),
|
startLodFaceList(attribs.getValue("submeshindex"),
|
||||||
attribs.getValue("numfaces"));
|
attribs.getValue("numfaces"));
|
||||||
}else if (qName.equals("lodgenerated")){
|
} else if (qName.equals("lodgenerated")) {
|
||||||
startLodGenerated(attribs.getValue("fromdepthsquared"));
|
startLodGenerated(attribs.getValue("fromdepthsquared"));
|
||||||
}else if (qName.equals("levelofdetail")){
|
} else if (qName.equals("levelofdetail")) {
|
||||||
startLevelOfDetail(attribs.getValue("numlevels"));
|
startLevelOfDetail(attribs.getValue("numlevels"));
|
||||||
}else if (qName.equals("boneassignments")){
|
} else if (qName.equals("boneassignments")) {
|
||||||
startBoneAssigns();
|
startBoneAssigns();
|
||||||
}else if (qName.equals("submesh")){
|
} else if (qName.equals("submesh")) {
|
||||||
startMesh(attribs.getValue("material"),
|
startMesh(attribs.getValue("material"),
|
||||||
attribs.getValue("usesharedvertices"),
|
attribs.getValue("usesharedvertices"),
|
||||||
attribs.getValue("use32bitindexes"),
|
attribs.getValue("use32bitindexes"),
|
||||||
attribs.getValue("operationtype"));
|
attribs.getValue("operationtype"));
|
||||||
}else if (qName.equals("sharedgeometry")){
|
} else if (qName.equals("sharedgeometry")) {
|
||||||
String count = attribs.getValue("vertexcount");
|
String count = attribs.getValue("vertexcount");
|
||||||
if (count == null)
|
if (count == null) {
|
||||||
count = attribs.getValue("count");
|
count = attribs.getValue("count");
|
||||||
|
}
|
||||||
|
|
||||||
if (count != null && !count.equals("0"))
|
if (count != null && !count.equals("0")) {
|
||||||
startSharedGeom(count);
|
startSharedGeom(count);
|
||||||
}else if (qName.equals("submeshes")){
|
}
|
||||||
|
} else if (qName.equals("submeshes")) {
|
||||||
// ok
|
// ok
|
||||||
}else if (qName.equals("skeletonlink")){
|
} else if (qName.equals("skeletonlink")) {
|
||||||
startSkeleton(attribs.getValue("name"));
|
startSkeleton(attribs.getValue("name"));
|
||||||
}else if (qName.equals("submeshname")){
|
} else if (qName.equals("submeshname")) {
|
||||||
startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
|
startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
|
||||||
}else if (qName.equals("mesh")){
|
} else if (qName.equals("mesh")) {
|
||||||
// ok
|
// ok
|
||||||
}else{
|
} else {
|
||||||
logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
|
logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
|
||||||
ignoreUntilEnd = qName;
|
ignoreUntilEnd = qName;
|
||||||
}
|
}
|
||||||
@ -628,56 +633,59 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String name, String qName) {
|
public void endElement(String uri, String name, String qName) {
|
||||||
if (ignoreUntilEnd != null){
|
if (ignoreUntilEnd != null) {
|
||||||
if (ignoreUntilEnd.equals(qName))
|
if (ignoreUntilEnd.equals(qName)) {
|
||||||
ignoreUntilEnd = null;
|
ignoreUntilEnd = null;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qName.equals("submesh")){
|
if (qName.equals("submesh")) {
|
||||||
bigindices = false;
|
bigindices = false;
|
||||||
geom = null;
|
geom = null;
|
||||||
mesh = null;
|
mesh = null;
|
||||||
}else if (qName.equals("submeshes")){
|
} else if (qName.equals("submeshes")) {
|
||||||
// IMPORTANT: restore sharedgeoemtry, for use with shared boneweights
|
// IMPORTANT: restore sharedgeoemtry, for use with shared boneweights
|
||||||
geom = sharedgeom;
|
geom = sharedgeom;
|
||||||
mesh = sharedmesh;
|
mesh = sharedmesh;
|
||||||
}else if (qName.equals("faces")){
|
} else if (qName.equals("faces")) {
|
||||||
if (ib != null)
|
if (ib != null) {
|
||||||
ib.flip();
|
ib.flip();
|
||||||
else
|
} else {
|
||||||
sb.flip();
|
sb.flip();
|
||||||
|
}
|
||||||
|
|
||||||
vb = null;
|
vb = null;
|
||||||
ib = null;
|
ib = null;
|
||||||
sb = null;
|
sb = null;
|
||||||
}else if (qName.equals("vertexbuffer")){
|
} else if (qName.equals("vertexbuffer")) {
|
||||||
fb = null;
|
fb = null;
|
||||||
vb = null;
|
vb = null;
|
||||||
}else if (qName.equals("geometry")
|
} else if (qName.equals("geometry")
|
||||||
|| qName.equals("sharedgeometry")){
|
|| qName.equals("sharedgeometry")) {
|
||||||
// finish writing to buffers
|
// finish writing to buffers
|
||||||
IntMap<VertexBuffer> bufs = mesh.getBuffers();
|
IntMap<VertexBuffer> bufs = mesh.getBuffers();
|
||||||
for (Entry<VertexBuffer> entry : bufs){
|
for (Entry<VertexBuffer> entry : bufs) {
|
||||||
Buffer data = entry.getValue().getData();
|
Buffer data = entry.getValue().getData();
|
||||||
if (data.position() != 0)
|
if (data.position() != 0) {
|
||||||
data.flip();
|
data.flip();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mesh.updateBound();
|
mesh.updateBound();
|
||||||
mesh.setStatic();
|
mesh.setStatic();
|
||||||
|
|
||||||
if (qName.equals("sharedgeometry")){
|
if (qName.equals("sharedgeometry")) {
|
||||||
geom = null;
|
geom = null;
|
||||||
mesh = null;
|
mesh = null;
|
||||||
}
|
}
|
||||||
}else if (qName.equals("lodfacelist")){
|
} else if (qName.equals("lodfacelist")) {
|
||||||
sb.flip();
|
sb.flip();
|
||||||
vb = null;
|
vb = null;
|
||||||
sb = null;
|
sb = null;
|
||||||
}else if (qName.equals("levelofdetail")){
|
} else if (qName.equals("levelofdetail")) {
|
||||||
endLevelOfDetail();
|
endLevelOfDetail();
|
||||||
}else if (qName.equals("boneassignments")){
|
} else if (qName.equals("boneassignments")) {
|
||||||
endBoneAssigns();
|
endBoneAssigns();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -686,9 +694,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
public void characters(char ch[], int start, int length) {
|
public void characters(char ch[], int start, int length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBindPose(Mesh mesh){
|
private void createBindPose(Mesh mesh) {
|
||||||
VertexBuffer pos = mesh.getBuffer(Type.Position);
|
VertexBuffer pos = mesh.getBuffer(Type.Position);
|
||||||
if (pos == null || mesh.getBuffer(Type.BoneIndex) == null){
|
if (pos == null || mesh.getBuffer(Type.BoneIndex) == null) {
|
||||||
// ignore, this mesh doesn't have positional data
|
// ignore, this mesh doesn't have positional data
|
||||||
// or it doesn't have bone-vertex assignments, so its not animated
|
// or it doesn't have bone-vertex assignments, so its not animated
|
||||||
return;
|
return;
|
||||||
@ -696,9 +704,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
|
|
||||||
VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
|
VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
|
||||||
bindPos.setupData(Usage.CpuOnly,
|
bindPos.setupData(Usage.CpuOnly,
|
||||||
3,
|
3,
|
||||||
Format.Float,
|
Format.Float,
|
||||||
BufferUtils.clone(pos.getData()));
|
BufferUtils.clone(pos.getData()));
|
||||||
mesh.setBuffer(bindPos);
|
mesh.setBuffer(bindPos);
|
||||||
|
|
||||||
// XXX: note that this method also sets stream mode
|
// XXX: note that this method also sets stream mode
|
||||||
@ -706,111 +714,118 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
pos.setUsage(Usage.Stream);
|
pos.setUsage(Usage.Stream);
|
||||||
|
|
||||||
VertexBuffer norm = mesh.getBuffer(Type.Normal);
|
VertexBuffer norm = mesh.getBuffer(Type.Normal);
|
||||||
if (norm != null){
|
if (norm != null) {
|
||||||
VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
|
VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
|
||||||
bindNorm.setupData(Usage.CpuOnly,
|
bindNorm.setupData(Usage.CpuOnly,
|
||||||
3,
|
3,
|
||||||
Format.Float,
|
Format.Float,
|
||||||
BufferUtils.clone(norm.getData()));
|
BufferUtils.clone(norm.getData()));
|
||||||
mesh.setBuffer(bindNorm);
|
mesh.setBuffer(bindNorm);
|
||||||
norm.setUsage(Usage.Stream);
|
norm.setUsage(Usage.Stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node compileModel(){
|
private Node compileModel() {
|
||||||
String nodeName;
|
String nodeName;
|
||||||
if (meshName == null)
|
if (meshName == null) {
|
||||||
nodeName = "OgreMesh"+(++nodeIdx);
|
nodeName = "OgreMesh" + (++nodeIdx);
|
||||||
else
|
} else {
|
||||||
nodeName = meshName+"-ogremesh";
|
nodeName = meshName + "-ogremesh";
|
||||||
|
}
|
||||||
|
|
||||||
Node model = new Node(nodeName);
|
Node model = new Node(nodeName);
|
||||||
if (animData != null){
|
if (animData != null) {
|
||||||
ArrayList<Mesh> newMeshes = new ArrayList<Mesh>(geoms.size());
|
ArrayList<Mesh> newMeshes = new ArrayList<Mesh>(geoms.size());
|
||||||
|
|
||||||
// generate bind pose for mesh and add to skin-list
|
// generate bind pose for mesh and add to skin-list
|
||||||
// ONLY if not using shared geometry
|
// ONLY if not using shared geometry
|
||||||
// This includes the shared geoemtry itself actually
|
// This includes the shared geoemtry itself actually
|
||||||
for (int i = 0; i < geoms.size(); i++){
|
for (int i = 0; i < geoms.size(); i++) {
|
||||||
Geometry g = geoms.get(i);
|
Geometry g = geoms.get(i);
|
||||||
Mesh m = geoms.get(i).getMesh();
|
Mesh m = geoms.get(i).getMesh();
|
||||||
boolean useShared = usesSharedGeom.get(i);
|
boolean useShared = usesSharedGeom.get(i);
|
||||||
// create bind pose
|
// create bind pose
|
||||||
if (!useShared){
|
if (!useShared) {
|
||||||
createBindPose(m);
|
createBindPose(m);
|
||||||
newMeshes.add(m);
|
newMeshes.add(m);
|
||||||
}else{
|
} else {
|
||||||
VertexBuffer bindPos = sharedmesh.getBuffer(Type.BindPosePosition);
|
VertexBuffer bindPos = sharedmesh.getBuffer(Type.BindPosePosition);
|
||||||
VertexBuffer bindNorm = sharedmesh.getBuffer(Type.BindPoseNormal);
|
VertexBuffer bindNorm = sharedmesh.getBuffer(Type.BindPoseNormal);
|
||||||
VertexBuffer boneIndex = sharedmesh.getBuffer(Type.BoneIndex);
|
VertexBuffer boneIndex = sharedmesh.getBuffer(Type.BoneIndex);
|
||||||
VertexBuffer boneWeight = sharedmesh.getBuffer(Type.BoneWeight);
|
VertexBuffer boneWeight = sharedmesh.getBuffer(Type.BoneWeight);
|
||||||
|
|
||||||
if (bindPos != null)
|
if (bindPos != null) {
|
||||||
m.setBuffer(bindPos);
|
m.setBuffer(bindPos);
|
||||||
|
}
|
||||||
|
|
||||||
if (bindNorm != null)
|
if (bindNorm != null) {
|
||||||
m.setBuffer(bindNorm);
|
m.setBuffer(bindNorm);
|
||||||
|
}
|
||||||
|
|
||||||
if (boneIndex != null)
|
if (boneIndex != null) {
|
||||||
m.setBuffer(boneIndex);
|
m.setBuffer(boneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (boneWeight != null)
|
if (boneWeight != null) {
|
||||||
m.setBuffer(boneWeight);
|
m.setBuffer(boneWeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mesh[] meshes = new Mesh[newMeshes.size()];
|
Mesh[] meshes = new Mesh[newMeshes.size()];
|
||||||
for (int i = 0; i < meshes.length; i++)
|
for (int i = 0; i < meshes.length; i++) {
|
||||||
meshes[i] = newMeshes.get(i);
|
meshes[i] = newMeshes.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
|
HashMap<String, BoneAnimation> anims = new HashMap<String, BoneAnimation>();
|
||||||
ArrayList<BoneAnimation> animList = animData.anims;
|
ArrayList<BoneAnimation> animList = animData.anims;
|
||||||
for (int i = 0; i < animList.size(); i++){
|
for (int i = 0; i < animList.size(); i++) {
|
||||||
BoneAnimation anim = animList.get(i);
|
BoneAnimation anim = animList.get(i);
|
||||||
anims.put(anim.getName(), anim);
|
anims.put(anim.getName(), anim);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimControl ctrl = new AnimControl(model,
|
//AnimControl ctrl = new AnimControl(model, meshes, animData.skeleton);
|
||||||
meshes,
|
AnimControl ctrl = new AnimControl(animData.skeleton);
|
||||||
animData.skeleton);
|
|
||||||
ctrl.setAnimations(anims);
|
ctrl.setAnimations(anims);
|
||||||
model.addControl(ctrl);
|
model.addControl(ctrl);
|
||||||
|
SkeletonControl skeletonControl = new SkeletonControl(model, meshes, animData.skeleton);
|
||||||
|
model.addControl(skeletonControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < geoms.size(); i++){
|
for (int i = 0; i < geoms.size(); i++) {
|
||||||
Geometry g = geoms.get(i);
|
Geometry g = geoms.get(i);
|
||||||
Mesh m = g.getMesh();
|
Mesh m = g.getMesh();
|
||||||
if (sharedmesh != null && usesSharedGeom.get(i)){
|
if (sharedmesh != null && usesSharedGeom.get(i)) {
|
||||||
m.setBound(sharedmesh.getBound().clone());
|
m.setBound(sharedmesh.getBound().clone());
|
||||||
}
|
}
|
||||||
model.attachChild(geoms.get(i));
|
model.attachChild(geoms.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object load(AssetInfo info) throws IOException {
|
public Object load(AssetInfo info) throws IOException {
|
||||||
try{
|
try {
|
||||||
AssetKey key = info.getKey();
|
AssetKey key = info.getKey();
|
||||||
meshName = key.getName();
|
meshName = key.getName();
|
||||||
folderName = key.getFolder();
|
folderName = key.getFolder();
|
||||||
String ext = key.getExtension();
|
String ext = key.getExtension();
|
||||||
meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
|
meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
|
||||||
if (folderName != null && folderName.length() > 0){
|
if (folderName != null && folderName.length() > 0) {
|
||||||
meshName = meshName.substring(folderName.length());
|
meshName = meshName.substring(folderName.length());
|
||||||
}
|
}
|
||||||
assetManager = info.getManager();
|
assetManager = info.getManager();
|
||||||
|
|
||||||
OgreMeshKey meshKey = null;
|
OgreMeshKey meshKey = null;
|
||||||
if (key instanceof OgreMeshKey){
|
if (key instanceof OgreMeshKey) {
|
||||||
meshKey = (OgreMeshKey) key;
|
meshKey = (OgreMeshKey) key;
|
||||||
materialList = meshKey.getMaterialList();
|
materialList = meshKey.getMaterialList();
|
||||||
}else{
|
} else {
|
||||||
try {
|
try {
|
||||||
materialList = (MaterialList) assetManager.loadAsset(folderName + meshName + ".material");
|
materialList = (MaterialList) assetManager.loadAsset(folderName + meshName + ".material");
|
||||||
} catch (AssetNotFoundException e) {
|
} catch (AssetNotFoundException e) {
|
||||||
logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext});
|
logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLReader xr = XMLReaderFactory.createXMLReader();
|
XMLReader xr = XMLReaderFactory.createXMLReader();
|
||||||
@ -821,12 +836,11 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
|
|||||||
r.close();
|
r.close();
|
||||||
|
|
||||||
return compileModel();
|
return compileModel();
|
||||||
}catch (SAXException ex){
|
} catch (SAXException ex) {
|
||||||
IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
|
IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
|
||||||
ioEx.initCause(ex);
|
ioEx.initCause(ex);
|
||||||
throw ioEx;
|
throw ioEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ public class TestBoneRagdoll extends SimpleApplication {
|
|||||||
setupLight();
|
setupLight();
|
||||||
|
|
||||||
model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
|
model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
|
||||||
// model.setLocalTranslation(5,5,5);
|
// model.setLocalTranslation(5,5,5);
|
||||||
// model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
|
// model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
|
||||||
|
|
||||||
//debug view
|
//debug view
|
||||||
AnimControl control= model.getControl(AnimControl.class);
|
AnimControl control= model.getControl(AnimControl.class);
|
||||||
@ -105,12 +105,12 @@ public class TestBoneRagdoll extends SimpleApplication {
|
|||||||
// ragdoll.setEnabled(true);
|
// ragdoll.setEnabled(true);
|
||||||
// ragdoll.attachDebugShape(assetManager);
|
// ragdoll.attachDebugShape(assetManager);
|
||||||
|
|
||||||
ragdoll.setSpatial(model);
|
// ragdoll.setSpatial(model);
|
||||||
ragdoll.setPhysicsSpace(getPhysicsSpace());
|
// ragdoll.setPhysicsSpace(getPhysicsSpace());
|
||||||
control.setRagdoll(ragdoll);
|
// control.setRagdoll(ragdoll);
|
||||||
|
|
||||||
// model.addControl(ragdoll);
|
model.addControl(ragdoll);
|
||||||
// getPhysicsSpace().add(ragdoll);
|
getPhysicsSpace().add(ragdoll);
|
||||||
speed = 1f;
|
speed = 1f;
|
||||||
|
|
||||||
rootNode.attachChild(model);
|
rootNode.attachChild(model);
|
||||||
|
@ -131,11 +131,11 @@ public class TestOgreComplexAnim extends SimpleApplication {
|
|||||||
q.fromAngles(0, angle, 0);
|
q.fromAngles(0, angle, 0);
|
||||||
|
|
||||||
b.setUserControl(true);
|
b.setUserControl(true);
|
||||||
b.setUserTransforms(Vector3f.ZERO, q, new Vector3f(angle, angle, angle));
|
b.setUserTransforms(Vector3f.ZERO, q, Vector3f.ZERO);
|
||||||
|
|
||||||
// b2.setUserControl(true);
|
b2.setUserControl(true);
|
||||||
// b2.setUserTransforms(Vector3f.ZERO, Quaternion.IDENTITY, new Vector3f(angle, angle, angle));
|
b2.setUserTransforms(Vector3f.ZERO, Quaternion.IDENTITY, new Vector3f(angle, angle, angle));
|
||||||
//
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user