/* * Copyright (c) 2009-2012 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 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 pointsMap * @param boneIndices * @param initialScale * @param initialPosition * @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 model * @param boneIndices * @param initialScale * @param initialPosition * @param weightThreshold * @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.setUserTransformsInModelSpace(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); } } }