From 51b12e1b9e4a5a22708db8b36500d854fc76f7d9 Mon Sep 17 00:00:00 2001 From: Stephen Gold Date: Wed, 9 Jan 2019 10:31:23 -0800 Subject: [PATCH] test&fix for issue #1004 (RagdollUtils can't handle 16-bit bone indices) --- .../bullet/control/ragdoll/RagdollUtils.java | 42 +++++++-- .../java/jme3test/bullet/TestIssue1004.java | 92 +++++++++++++++++++ 2 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 jme3-examples/src/main/java/jme3test/bullet/TestIssue1004.java diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java index 419c337bc..9e354a1fa 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2018 jMonkeyEngine + * Copyright (c) 2009-2019 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,9 +41,12 @@ import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; import com.jme3.scene.Spatial; +import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Type; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.nio.ShortBuffer; import java.util.*; /** @@ -254,7 +257,8 @@ public class RagdollUtils { private static List getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) { FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); - ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); + VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + Buffer boneIndices = biBuf.getDataReadOnly(); FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); vertices.rewind(); @@ -270,7 +274,8 @@ public class RagdollUtils { boolean add = false; int start = i / 3 * 4; for (k = start; k < start + 4; k++) { - if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) { + if (readIndex(boneIndices, k) == boneIndex + && boneWeight.get(k) >= weightThreshold) { add = true; break; } @@ -349,8 +354,8 @@ public class RagdollUtils { public static boolean hasVertices(int boneIndex, Mesh[] targets, float weightThreshold) { for (Mesh mesh : targets) { - ByteBuffer boneIndices - = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); + VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + Buffer boneIndices = biBuf.getDataReadOnly(); FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); @@ -361,7 +366,7 @@ public class RagdollUtils { for (int i = 0; i < vertexComponents; i += 3) { int start = i / 3 * 4; for (int k = start; k < start + 4; k++) { - if (boneIndices.get(k) == boneIndex + if (readIndex(boneIndices, k) == boneIndex && boneWeight.get(k) >= weightThreshold) { return true; } @@ -371,4 +376,29 @@ public class RagdollUtils { return false; } + + /** + * Read an index from a buffer. + * + * @param buffer a buffer of bytes or shorts (not null) + * @param k the position from which the index will be read + * @return the index value (≥0) + */ + public static int readIndex(Buffer buffer, int k) { + int result; + if (buffer instanceof ByteBuffer) { + ByteBuffer byteBuffer = (ByteBuffer) buffer; + byte b = byteBuffer.get(k); + result = 0xff & b; + } else if (buffer instanceof ShortBuffer) { + ShortBuffer shortBuffer = (ShortBuffer) buffer; + short s = shortBuffer.get(k); + result = 0xffff & s; + } else { + throw new IllegalArgumentException(buffer.getClass().getName()); + } + + assert result >= 0 : result; + return result; + } } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestIssue1004.java b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1004.java new file mode 100644 index 000000000..0379fadab --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/bullet/TestIssue1004.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 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.bullet; + +import com.jme3.app.SimpleApplication; +import com.jme3.bullet.BulletAppState; +import com.jme3.bullet.control.KinematicRagdollControl; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.VertexBuffer; +import java.nio.ByteBuffer; + +/** + * Test case for JME issue #1004: RagdollUtils can't handle 16-bit bone indices. + *

+ * If successful, no exception will be thrown. + */ +public class TestIssue1004 extends SimpleApplication { + // ************************************************************************* + // new methods exposed + + public static void main(String[] args) { + TestIssue1004 app = new TestIssue1004(); + app.start(); + } + // ************************************************************************* + // SimpleApplication methods + + @Override + public void simpleInitApp() { + BulletAppState bulletAppState = new BulletAppState(); + stateManager.attach(bulletAppState); + String sinbadPath = "Models/Sinbad/SinbadOldAnim.j3o"; + Node sinbad = (Node) assetManager.loadModel(sinbadPath); + + Geometry geometry = (Geometry) sinbad.getChild(0); + Mesh mesh = geometry.getMesh(); + VertexBuffer.Type bufferType = VertexBuffer.Type.BoneIndex; + VertexBuffer vertexBuffer = mesh.getBuffer(bufferType); + + // Remove the existing bone-index buffer. + mesh.getBufferList().remove(vertexBuffer); + mesh.getBuffers().remove(bufferType.ordinal()); + + // Copy the 8-bit bone indices to 16-bit indices. + ByteBuffer oldBuffer = (ByteBuffer) vertexBuffer.getDataReadOnly(); + int numComponents = oldBuffer.limit(); + oldBuffer.rewind(); + short[] shortArray = new short[numComponents]; + for (int index = 0; oldBuffer.hasRemaining(); ++index) { + shortArray[index] = oldBuffer.get(); + } + + // Add the 16-bit bone indices to the mesh. + mesh.setBuffer(bufferType, 4, shortArray); + + KinematicRagdollControl ragdoll = new KinematicRagdollControl(0.5f); + sinbad.addControl(ragdoll); + + stop(); + } +}