Merge pull request #637 from stephengold/master
fix SkeletonControl.getAttachmentsNode() for odd models (such as Jaime)
This commit is contained in:
commit
3d07e165af
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -33,7 +33,11 @@ package com.jme3.animation;
|
|||||||
|
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.util.SafeArrayList;
|
||||||
import com.jme3.util.TempVars;
|
import com.jme3.util.TempVars;
|
||||||
import com.jme3.util.clone.JmeCloneable;
|
import com.jme3.util.clone.JmeCloneable;
|
||||||
import com.jme3.util.clone.Cloner;
|
import com.jme3.util.clone.Cloner;
|
||||||
@ -80,7 +84,10 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
* The attachment node.
|
* The attachment node.
|
||||||
*/
|
*/
|
||||||
private Node attachNode;
|
private Node attachNode;
|
||||||
|
/**
|
||||||
|
* A geometry animated by this node, used when updating the attachments node.
|
||||||
|
*/
|
||||||
|
private Geometry targetGeometry = null;
|
||||||
/**
|
/**
|
||||||
* Bind transform is the local bind transform of this bone. (local space)
|
* Bind transform is the local bind transform of this bone. (local space)
|
||||||
*/
|
*/
|
||||||
@ -187,6 +194,7 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
this.children = cloner.clone(children);
|
this.children = cloner.clone(children);
|
||||||
|
|
||||||
this.attachNode = cloner.clone(attachNode);
|
this.attachNode = cloner.clone(attachNode);
|
||||||
|
this.targetGeometry = cloner.clone(targetGeometry);
|
||||||
|
|
||||||
this.bindPos = cloner.clone(bindPos);
|
this.bindPos = cloner.clone(bindPos);
|
||||||
this.bindRot = cloner.clone(bindRot);
|
this.bindRot = cloner.clone(bindRot);
|
||||||
@ -505,9 +513,39 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attachNode != null) {
|
if (attachNode != null) {
|
||||||
|
updateAttachNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the local transform of the attachments node.
|
||||||
|
*/
|
||||||
|
private void updateAttachNode() {
|
||||||
|
Node attachParent = attachNode.getParent();
|
||||||
|
if (attachParent == null || targetGeometry == null
|
||||||
|
|| targetGeometry.getParent() == attachParent
|
||||||
|
&& targetGeometry.getLocalTransform().isIdentity()) {
|
||||||
|
/*
|
||||||
|
* The animated meshes are in the same coordinate system as the
|
||||||
|
* attachments node: no further transforms are needed.
|
||||||
|
*/
|
||||||
attachNode.setLocalTranslation(modelPos);
|
attachNode.setLocalTranslation(modelPos);
|
||||||
attachNode.setLocalRotation(modelRot);
|
attachNode.setLocalRotation(modelRot);
|
||||||
attachNode.setLocalScale(modelScale);
|
attachNode.setLocalScale(modelScale);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Spatial loopSpatial = targetGeometry;
|
||||||
|
Transform combined = new Transform(modelPos, modelRot, modelScale);
|
||||||
|
/*
|
||||||
|
* Climb the scene graph applying local transforms until the
|
||||||
|
* attachments node's parent is reached.
|
||||||
|
*/
|
||||||
|
while (loopSpatial != attachParent && loopSpatial != null) {
|
||||||
|
Transform localTransform = loopSpatial.getLocalTransform();
|
||||||
|
combined.combineWithParent(localTransform);
|
||||||
|
loopSpatial = loopSpatial.getParent();
|
||||||
|
}
|
||||||
|
attachNode.setLocalTransform(combined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,15 +699,32 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the attachment node.
|
* Access the attachments node of this bone. If this bone doesn't already
|
||||||
* Attach models and effects to this node to make
|
* have an attachments node, create one. Models and effects attached to the
|
||||||
* them follow this bone's motions.
|
* attachments node will follow this bone's motions.
|
||||||
|
*
|
||||||
|
* @param boneIndex this bone's index in its skeleton (≥0)
|
||||||
|
* @param targets a list of geometries animated by this bone's skeleton (not
|
||||||
|
* null, unaffected)
|
||||||
*/
|
*/
|
||||||
Node getAttachmentsNode() {
|
Node getAttachmentsNode(int boneIndex, SafeArrayList<Geometry> targets) {
|
||||||
|
targetGeometry = null;
|
||||||
|
/*
|
||||||
|
* Search for a geometry animated by this particular bone.
|
||||||
|
*/
|
||||||
|
for (Geometry geometry : targets) {
|
||||||
|
Mesh mesh = geometry.getMesh();
|
||||||
|
if (mesh != null && mesh.isAnimatedByBone(boneIndex)) {
|
||||||
|
targetGeometry = geometry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attachNode == null) {
|
if (attachNode == null) {
|
||||||
attachNode = new Node(name + "_attachnode");
|
attachNode = new Node(name + "_attachnode");
|
||||||
attachNode.setUserData("AttachedBone", this);
|
attachNode.setUserData("AttachedBone", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attachNode;
|
return attachNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,6 +878,7 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachNode = (Node) input.readSavable("attachNode", null);
|
attachNode = (Node) input.readSavable("attachNode", null);
|
||||||
|
targetGeometry = (Geometry) input.readSavable("targetGeometry", null);
|
||||||
|
|
||||||
localPos.set(bindPos);
|
localPos.set(bindPos);
|
||||||
localRot.set(bindRot);
|
localRot.set(bindRot);
|
||||||
@ -845,6 +901,7 @@ public final class Bone implements Savable, JmeCloneable {
|
|||||||
|
|
||||||
output.write(name, "name", null);
|
output.write(name, "name", null);
|
||||||
output.write(attachNode, "attachNode", null);
|
output.write(attachNode, "attachNode", null);
|
||||||
|
output.write(targetGeometry, "targetGeometry", null);
|
||||||
output.write(bindPos, "bindPos", null);
|
output.write(bindPos, "bindPos", null);
|
||||||
output.write(bindRot, "bindRot", null);
|
output.write(bindRot, "bindRot", null);
|
||||||
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -52,7 +52,6 @@ import java.io.IOException;
|
|||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -71,10 +70,12 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
* The skeleton of the model.
|
* The skeleton of the model.
|
||||||
*/
|
*/
|
||||||
private Skeleton skeleton;
|
private Skeleton skeleton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of targets which this controller effects.
|
* List of geometries affected by this control.
|
||||||
*/
|
*/
|
||||||
private SafeArrayList<Mesh> targets = new SafeArrayList<Mesh>(Mesh.class);
|
private SafeArrayList<Geometry> targets = new SafeArrayList<Geometry>(Geometry.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to track when a mesh was updated. Meshes are only updated if they
|
* Used to track when a mesh was updated. Meshes are only updated if they
|
||||||
* are visible in at least one camera.
|
* are visible in at least one camera.
|
||||||
@ -124,8 +125,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
for (Material m : materials) {
|
for (Material m : materials) {
|
||||||
m.setInt("NumberOfBones", numBones);
|
m.setInt("NumberOfBones", numBones);
|
||||||
}
|
}
|
||||||
for (Mesh mesh : targets) {
|
for (Geometry geometry : targets) {
|
||||||
if (mesh.isAnimated()) {
|
Mesh mesh = geometry.getMesh();
|
||||||
|
if (mesh != null && mesh.isAnimated()) {
|
||||||
mesh.prepareForAnim(false);
|
mesh.prepareForAnim(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,8 +139,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
m.clearParam("NumberOfBones");
|
m.clearParam("NumberOfBones");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Mesh mesh : targets) {
|
for (Geometry geometry : targets) {
|
||||||
if (mesh.isAnimated()) {
|
Mesh mesh = geometry.getMesh();
|
||||||
|
if (mesh != null && mesh.isAnimated()) {
|
||||||
mesh.prepareForAnim(true);
|
mesh.prepareForAnim(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,15 +213,22 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
this.skeleton = skeleton;
|
this.skeleton = skeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If specified the geometry has an animated mesh, add its mesh and material
|
||||||
|
* to the lists of animation targets.
|
||||||
|
*/
|
||||||
|
private void findTargets(Geometry geometry) {
|
||||||
|
Mesh mesh = geometry.getMesh();
|
||||||
|
if (mesh != null && mesh.isAnimated()) {
|
||||||
|
targets.add(geometry);
|
||||||
|
materials.add(geometry.getMaterial());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void findTargets(Node node) {
|
private void findTargets(Node node) {
|
||||||
for (Spatial child : node.getChildren()) {
|
for (Spatial child : node.getChildren()) {
|
||||||
if (child instanceof Geometry) {
|
if (child instanceof Geometry) {
|
||||||
Geometry geom = (Geometry) child;
|
findTargets((Geometry) child);
|
||||||
Mesh mesh = geom.getMesh();
|
|
||||||
if (mesh.isAnimated()) {
|
|
||||||
targets.add(mesh);
|
|
||||||
materials.add(geom.getMaterial());
|
|
||||||
}
|
|
||||||
} else if (child instanceof Node) {
|
} else if (child instanceof Node) {
|
||||||
findTargets((Node) child);
|
findTargets((Node) child);
|
||||||
}
|
}
|
||||||
@ -236,10 +246,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
|
|
||||||
offsetMatrices = skeleton.computeSkinningMatrices();
|
offsetMatrices = skeleton.computeSkinningMatrices();
|
||||||
|
|
||||||
for (Mesh mesh : targets) {
|
for (Geometry geometry : targets) {
|
||||||
// NOTE: This assumes that code higher up
|
Mesh mesh = geometry.getMesh();
|
||||||
// Already ensured those targets are animated
|
// NOTE: This assumes code higher up has
|
||||||
// otherwise a crash will happen in skin update
|
// already ensured this mesh is animated.
|
||||||
|
// Otherwise a crash will happen in skin update.
|
||||||
softwareSkinUpdate(mesh, offsetMatrices);
|
softwareSkinUpdate(mesh, offsetMatrices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,8 +324,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
|
|
||||||
//only do this for software updates
|
//only do this for software updates
|
||||||
void resetToBind() {
|
void resetToBind() {
|
||||||
for (Mesh mesh : targets) {
|
for (Geometry geometry : targets) {
|
||||||
if (mesh.isAnimated()) {
|
Mesh mesh = geometry.getMesh();
|
||||||
|
if (mesh != null && mesh.isAnimated()) {
|
||||||
Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData();
|
Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData();
|
||||||
Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData();
|
Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData();
|
||||||
if (!biBuff.hasArray() || !bwBuff.hasArray()) {
|
if (!biBuff.hasArray() || !bwBuff.hasArray()) {
|
||||||
@ -432,9 +444,13 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Access the attachments node of the named bone. If the bone doesn't
|
||||||
|
* already have an attachments node, create one and attach it to the scene
|
||||||
|
* graph. Models and effects attached to the attachments node will follow
|
||||||
|
* the bone's motions.
|
||||||
*
|
*
|
||||||
* @param boneName the name of the bone
|
* @param boneName the name of the bone
|
||||||
* @return the node attached to this bone
|
* @return the attachments node of the bone
|
||||||
*/
|
*/
|
||||||
public Node getAttachmentsNode(String boneName) {
|
public Node getAttachmentsNode(String boneName) {
|
||||||
Bone b = skeleton.getBone(boneName);
|
Bone b = skeleton.getBone(boneName);
|
||||||
@ -443,9 +459,20 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
+ "in the skeleton.");
|
+ "in the skeleton.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Node n = b.getAttachmentsNode();
|
updateTargetsAndMaterials(spatial);
|
||||||
Node model = (Node) spatial;
|
int boneIndex = skeleton.getBoneIndex(b);
|
||||||
model.attachChild(n);
|
Node n = b.getAttachmentsNode(boneIndex, targets);
|
||||||
|
/*
|
||||||
|
* Select a node to parent the attachments node.
|
||||||
|
*/
|
||||||
|
Node parent;
|
||||||
|
if (spatial instanceof Node) {
|
||||||
|
parent = (Node) spatial; // the usual case
|
||||||
|
} else {
|
||||||
|
parent = spatial.getParent();
|
||||||
|
}
|
||||||
|
parent.attachChild(n);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,12 +486,20 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a copy of array of the targets meshes of this control
|
* Enumerate the target meshes of this control.
|
||||||
*
|
*
|
||||||
* @return
|
* @return a new array
|
||||||
*/
|
*/
|
||||||
public Mesh[] getTargets() {
|
public Mesh[] getTargets() {
|
||||||
return targets.toArray(new Mesh[targets.size()]);
|
Mesh[] result = new Mesh[targets.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Geometry geometry : targets) {
|
||||||
|
Mesh mesh = geometry.getMesh();
|
||||||
|
result[i] = mesh;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -758,12 +793,19 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the lists of animation targets.
|
||||||
|
*
|
||||||
|
* @param spatial the controlled spatial
|
||||||
|
*/
|
||||||
private void updateTargetsAndMaterials(Spatial spatial) {
|
private void updateTargetsAndMaterials(Spatial spatial) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
materials.clear();
|
materials.clear();
|
||||||
if (spatial != null && spatial instanceof Node) {
|
|
||||||
Node node = (Node) spatial;
|
if (spatial instanceof Node) {
|
||||||
findTargets(node);
|
findTargets((Node) spatial);
|
||||||
|
} else if (spatial instanceof Geometry) {
|
||||||
|
findTargets((Geometry) spatial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -288,6 +288,17 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
|
|||||||
rot.set(0, 0, 0, 1);
|
rot.set(0, 0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for exact identity.
|
||||||
|
*
|
||||||
|
* @return true if exactly equal to {@link #IDENTITY}, otherwise false
|
||||||
|
*/
|
||||||
|
public boolean isIdentity() {
|
||||||
|
return translation.x == 0f && translation.y == 0f && translation.z == 0f
|
||||||
|
&& scale.x == 1f && scale.y == 1f && scale.z == 1f
|
||||||
|
&& rot.w == 1f && rot.x == 0f && rot.y == 0f && rot.z == 0f;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -1408,6 +1408,45 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
getBuffer(Type.HWBoneIndex) != null;
|
getBuffer(Type.HWBoneIndex) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the specified bone animates this mesh.
|
||||||
|
*
|
||||||
|
* @param boneIndex the bone's index in its skeleton
|
||||||
|
* @return true if the specified bone animates this mesh, otherwise false
|
||||||
|
*/
|
||||||
|
public boolean isAnimatedByBone(int boneIndex) {
|
||||||
|
VertexBuffer biBuf = getBuffer(VertexBuffer.Type.BoneIndex);
|
||||||
|
VertexBuffer wBuf = getBuffer(VertexBuffer.Type.BoneWeight);
|
||||||
|
if (biBuf == null || wBuf == null) {
|
||||||
|
return false; // no bone animation data
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer boneIndexBuffer = (ByteBuffer) biBuf.getData();
|
||||||
|
boneIndexBuffer.rewind();
|
||||||
|
int numBoneIndices = boneIndexBuffer.remaining();
|
||||||
|
assert numBoneIndices % 4 == 0 : numBoneIndices;
|
||||||
|
int numVertices = boneIndexBuffer.remaining() / 4;
|
||||||
|
|
||||||
|
FloatBuffer weightBuffer = (FloatBuffer) wBuf.getData();
|
||||||
|
weightBuffer.rewind();
|
||||||
|
int numWeights = weightBuffer.remaining();
|
||||||
|
assert numWeights == numVertices * 4 : numWeights;
|
||||||
|
/*
|
||||||
|
* Test each vertex to determine whether the bone affects it.
|
||||||
|
*/
|
||||||
|
byte biByte = (byte) boneIndex; // bone indices wrap after 127
|
||||||
|
for (int vIndex = 0; vIndex < numVertices; vIndex++) {
|
||||||
|
for (int wIndex = 0; wIndex < 4; wIndex++) {
|
||||||
|
byte bIndex = boneIndexBuffer.get();
|
||||||
|
float weight = weightBuffer.get();
|
||||||
|
if (wIndex < maxNumWeights && bIndex == biByte && weight != 0f) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the count of vertices used for each tessellation patch
|
* Sets the count of vertices used for each tessellation patch
|
||||||
* @param patchVertexCount
|
* @param patchVertexCount
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package jme3test.model.anim;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimChannel;
|
||||||
|
import com.jme3.animation.AnimControl;
|
||||||
|
import com.jme3.animation.AnimEventListener;
|
||||||
|
import com.jme3.animation.LoopMode;
|
||||||
|
import com.jme3.animation.SkeletonControl;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.input.KeyInput;
|
||||||
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple application to an test attachments node on the Jaime model.
|
||||||
|
*
|
||||||
|
* Derived from {@link jme3test.model.anim.TestOgreAnim}.
|
||||||
|
*/
|
||||||
|
public class TestAttachmentsNode extends SimpleApplication
|
||||||
|
implements AnimEventListener, ActionListener {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestAttachmentsNode app = new TestAttachmentsNode();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimChannel channel;
|
||||||
|
private AnimControl control;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
flyCam.setMoveSpeed(10f);
|
||||||
|
cam.setLocation(new Vector3f(6.4f, 7.5f, 12.8f));
|
||||||
|
cam.setRotation(new Quaternion(-0.060740203f, 0.93925786f, -0.2398315f, -0.2378785f));
|
||||||
|
|
||||||
|
DirectionalLight dl = new DirectionalLight();
|
||||||
|
dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
|
||||||
|
dl.setColor(ColorRGBA.White);
|
||||||
|
rootNode.addLight(dl);
|
||||||
|
|
||||||
|
Spatial model = assetManager.loadModel("Models/Jaime/Jaime.j3o");
|
||||||
|
control = model.getControl(AnimControl.class);
|
||||||
|
SkeletonControl skeletonControl = model.getControl(SkeletonControl.class);
|
||||||
|
|
||||||
|
model.center();
|
||||||
|
model.setLocalScale(5f);
|
||||||
|
|
||||||
|
control.addListener(this);
|
||||||
|
channel = control.createChannel();
|
||||||
|
channel.setAnim("Idle");
|
||||||
|
|
||||||
|
Box box = new Box(0.3f, 0.02f, 0.02f);
|
||||||
|
Geometry saber = new Geometry("saber", box);
|
||||||
|
saber.move(0.4f, 0.05f, 0.01f);
|
||||||
|
Material red = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
|
||||||
|
saber.setMaterial(red);
|
||||||
|
Node n = skeletonControl.getAttachmentsNode("hand.R");
|
||||||
|
n.attachChild(saber);
|
||||||
|
rootNode.attachChild(model);
|
||||||
|
|
||||||
|
inputManager.addListener(this, "Attack");
|
||||||
|
inputManager.addMapping("Attack", new KeyTrigger(KeyInput.KEY_SPACE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
|
||||||
|
if (animName.equals("Punches")) {
|
||||||
|
channel.setAnim("Idle", 0.5f);
|
||||||
|
channel.setLoopMode(LoopMode.DontLoop);
|
||||||
|
channel.setSpeed(1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAction(String binding, boolean value, float tpf) {
|
||||||
|
if (binding.equals("Attack") && value) {
|
||||||
|
if (!channel.getAnimationName().equals("Punches")) {
|
||||||
|
channel.setAnim("Punches", 0.5f);
|
||||||
|
channel.setLoopMode(LoopMode.Cycle);
|
||||||
|
channel.setSpeed(0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user