getAttachmentNode(): move target selection from SkeletonControl to Bone

fix-456
Stephen Gold 8 years ago
parent b2aa1ff9f1
commit 9905c4f011
  1. 25
      jme3-core/src/main/java/com/jme3/animation/Bone.java
  2. 60
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  3. 41
      jme3-core/src/main/java/com/jme3/scene/Mesh.java

@ -34,8 +34,10 @@ package com.jme3.animation;
import com.jme3.export.*;
import com.jme3.math.*;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.clone.Cloner;
@ -701,16 +703,27 @@ public final class Bone implements Savable, JmeCloneable {
* have an attachments node, create one. Models and effects attached to the
* attachments node will follow this bone's motions.
*
* @param target a geometry animated by this bone, or null to indicate that
* all geometries affected by this bone have the same global transform as
* the attachment node's parent
*/
Node getAttachmentsNode(Geometry target) {
* @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(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) {
attachNode = new Node(name + "_attachnode");
attachNode.setUserData("AttachedBone", this);
}
targetGeometry = target;
return attachNode;
}

@ -70,15 +70,12 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
* The skeleton of the model.
*/
private Skeleton skeleton;
/**
* List of targets which this controller effects.
*/
private SafeArrayList<Mesh> targets = new SafeArrayList<Mesh>(Mesh.class);
/**
* Geometry with an animated mesh, for calculating attachments node
* transforms. A null means no geometry is subject to this control.
* List of geometries affected by this control.
*/
private Geometry targetGeometry = null;
private SafeArrayList<Geometry> targets = new SafeArrayList<Geometry>(Geometry.class);
/**
* Used to track when a mesh was updated. Meshes are only updated if they
* are visible in at least one camera.
@ -128,8 +125,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
for (Material m : materials) {
m.setInt("NumberOfBones", numBones);
}
for (Mesh mesh : targets) {
if (mesh.isAnimated()) {
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
if (mesh != null && mesh.isAnimated()) {
mesh.prepareForAnim(false);
}
}
@ -141,8 +139,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
m.clearParam("NumberOfBones");
}
}
for (Mesh mesh : targets) {
if (mesh.isAnimated()) {
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
if (mesh != null && mesh.isAnimated()) {
mesh.prepareForAnim(true);
}
}
@ -220,9 +219,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
*/
private void findTargets(Geometry geometry) {
Mesh mesh = geometry.getMesh();
if (mesh.isAnimated()) {
targets.add(mesh);
targetGeometry = geometry;
if (mesh != null && mesh.isAnimated()) {
targets.add(geometry);
materials.add(geometry.getMaterial());
}
}
@ -248,10 +246,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
offsetMatrices = skeleton.computeSkinningMatrices();
for (Mesh mesh : targets) {
// NOTE: This assumes that code higher up
// Already ensured those targets are animated
// otherwise a crash will happen in skin update
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
// NOTE: This assumes code higher up has
// already ensured this mesh is animated.
// Otherwise a crash will happen in skin update.
softwareSkinUpdate(mesh, offsetMatrices);
}
}
@ -325,8 +324,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
//only do this for software updates
void resetToBind() {
for (Mesh mesh : targets) {
if (mesh.isAnimated()) {
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
if (mesh != null && mesh.isAnimated()) {
Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData();
Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData();
if (!biBuff.hasArray() || !bwBuff.hasArray()) {
@ -460,7 +460,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
}
updateTargetsAndMaterials(spatial);
Node n = b.getAttachmentsNode(targetGeometry);
int boneIndex = skeleton.getBoneIndex(b);
Node n = b.getAttachmentsNode(boneIndex, targets);
/*
* Select a node to parent the attachments node.
*/
@ -485,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() {
return targets.toArray(new Mesh[targets.size()]);
public Mesh[] getTargets() {
Mesh[] result = new Mesh[targets.size()];
int i = 0;
for (Geometry geometry : targets) {
Mesh mesh = geometry.getMesh();
result[i] = mesh;
i++;
}
return result;
}
/**
@ -791,7 +800,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
*/
private void updateTargetsAndMaterials(Spatial spatial) {
targets.clear();
targetGeometry = null;
materials.clear();
if (spatial instanceof Node) {

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* Copyright (c) 2009-2017 jMonkeyEngine
* All rights reserved.
*
* 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;
}
/**
* 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
* @param patchVertexCount

Loading…
Cancel
Save