diff options
Diffstat (limited to 'engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java')
-rw-r--r-- | engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java new file mode 100644 index 0000000..02bab98 --- /dev/null +++ b/engine/src/bullet-common/com/jme3/bullet/control/ragdoll/RagdollUtils.java @@ -0,0 +1,268 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.bullet.control.ragdoll; + +import com.jme3.animation.Bone; +import com.jme3.animation.Skeleton; +import com.jme3.bullet.collision.shapes.HullCollisionShape; +import com.jme3.bullet.joints.SixDofJoint; +import com.jme3.math.Quaternion; +import com.jme3.math.Transform; +import com.jme3.math.Vector3f; +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.Type; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.*; + +/** + * + * @author Nehon + */ +public class RagdollUtils { + + public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { + + joint.getRotationalLimitMotor(0).setHiLimit(maxX); + joint.getRotationalLimitMotor(0).setLoLimit(minX); + joint.getRotationalLimitMotor(1).setHiLimit(maxY); + joint.getRotationalLimitMotor(1).setLoLimit(minY); + joint.getRotationalLimitMotor(2).setHiLimit(maxZ); + joint.getRotationalLimitMotor(2).setLoLimit(minZ); + } + + public static Map<Integer, List<Float>> buildPointMap(Spatial model) { + + + Map<Integer, List<Float>> map = new HashMap<Integer, List<Float>>(); + if (model instanceof Geometry) { + Geometry g = (Geometry) model; + buildPointMapForMesh(g.getMesh(), map); + } else if (model instanceof Node) { + Node node = (Node) model; + for (Spatial s : node.getChildren()) { + if (s instanceof Geometry) { + Geometry g = (Geometry) s; + buildPointMapForMesh(g.getMesh(), map); + } + } + } + return map; + } + + private static Map<Integer, List<Float>> buildPointMapForMesh(Mesh mesh, Map<Integer, List<Float>> map) { + + FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); + ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); + FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); + + vertices.rewind(); + boneIndices.rewind(); + boneWeight.rewind(); + + int vertexComponents = mesh.getVertexCount() * 3; + int k, start, index; + float maxWeight = 0; + + for (int i = 0; i < vertexComponents; i += 3) { + + + start = i / 3 * 4; + index = 0; + maxWeight = -1; + for (k = start; k < start + 4; k++) { + float weight = boneWeight.get(k); + if (weight > maxWeight) { + maxWeight = weight; + index = boneIndices.get(k); + } + } + List<Float> points = map.get(index); + if (points == null) { + points = new ArrayList<Float>(); + map.put(index, points); + } + points.add(vertices.get(i)); + points.add(vertices.get(i + 1)); + points.add(vertices.get(i + 2)); + } + return map; + } + + /** + * Create a hull collision shape from linked vertices to this bone. + * Vertices have to be previoulsly gathered in a map using buildPointMap method + * @param link + * @param model + * @return + */ + public static HullCollisionShape makeShapeFromPointMap(Map<Integer, List<Float>> pointsMap, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition) { + + ArrayList<Float> points = new ArrayList<Float>(); + for (Integer index : boneIndices) { + List<Float> l = pointsMap.get(index); + if (l != null) { + + for (int i = 0; i < l.size(); i += 3) { + Vector3f pos = new Vector3f(); + pos.x = l.get(i); + pos.y = l.get(i + 1); + pos.z = l.get(i + 2); + pos.subtractLocal(initialPosition).multLocal(initialScale); + points.add(pos.x); + points.add(pos.y); + points.add(pos.z); + } + } + } + + float[] p = new float[points.size()]; + for (int i = 0; i < points.size(); i++) { + p[i] = points.get(i); + } + + + return new HullCollisionShape(p); + } + + //retruns the list of bone indices of the given bone and its child(if they are not in the boneList) + public static List<Integer> getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList) { + List<Integer> list = new LinkedList<Integer>(); + if (boneList.isEmpty()) { + list.add(skeleton.getBoneIndex(bone)); + } else { + list.add(skeleton.getBoneIndex(bone)); + for (Bone chilBone : bone.getChildren()) { + if (!boneList.contains(chilBone.getName())) { + list.addAll(getBoneIndices(chilBone, skeleton, boneList)); + } + } + } + return list; + } + + /** + * Create a hull collision shape from linked vertices to this bone. + * + * @param link + * @param model + * @return + */ + public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold) { + + ArrayList<Float> points = new ArrayList<Float>(); + if (model instanceof Geometry) { + Geometry g = (Geometry) model; + for (Integer index : boneIndices) { + points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold)); + } + } else if (model instanceof Node) { + Node node = (Node) model; + for (Spatial s : node.getChildren()) { + if (s instanceof Geometry) { + Geometry g = (Geometry) s; + for (Integer index : boneIndices) { + points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold)); + } + + } + } + } + float[] p = new float[points.size()]; + for (int i = 0; i < points.size(); i++) { + p[i] = points.get(i); + } + + + return new HullCollisionShape(p); + } + + /** + * returns a list of points for the given bone + * @param mesh + * @param boneIndex + * @param offset + * @param link + * @return + */ + private static List<Float> 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(); + FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); + + vertices.rewind(); + boneIndices.rewind(); + boneWeight.rewind(); + + ArrayList<Float> results = new ArrayList<Float>(); + + int vertexComponents = mesh.getVertexCount() * 3; + + for (int i = 0; i < vertexComponents; i += 3) { + int k; + boolean add = false; + int start = i / 3 * 4; + for (k = start; k < start + 4; k++) { + if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) { + add = true; + break; + } + } + if (add) { + + Vector3f pos = new Vector3f(); + pos.x = vertices.get(i); + pos.y = vertices.get(i + 1); + pos.z = vertices.get(i + 2); + pos.subtractLocal(offset).multLocal(initialScale); + results.add(pos.x); + results.add(pos.y); + results.add(pos.z); + + } + } + + return results; + } + + /** + * Updates a bone position and rotation. + * if the child bones are not in the bone list this means, they are not associated with a physic shape. + * So they have to be updated + * @param bone the bone + * @param pos the position + * @param rot the rotation + */ + public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set<String> boneList) { + //we ensure that we have the control + if (restoreBoneControl) { + bone.setUserControl(true); + } + //we set te user transforms of the bone + bone.setUserTransformsWorld(pos, rot); + for (Bone childBone : bone.getChildren()) { + //each child bone that is not in the list is updated + if (!boneList.contains(childBone.getName())) { + Transform t = childBone.getCombinedTransform(pos, rot); + setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl, boneList); + } + } + //we give back the control to the keyframed animation + if (restoreBoneControl) { + bone.setUserControl(false); + } + } + + public static void setUserControl(Bone bone, boolean bool) { + bone.setUserControl(bool); + for (Bone child : bone.getChildren()) { + setUserControl(child, bool); + } + } +} |