1 /* 2 * To change this template, choose Tools | Templates 3 * and open the template in the editor. 4 */ 5 package com.jme3.bullet.control.ragdoll; 6 7 import com.jme3.animation.Bone; 8 import com.jme3.animation.Skeleton; 9 import com.jme3.bullet.collision.shapes.HullCollisionShape; 10 import com.jme3.bullet.joints.SixDofJoint; 11 import com.jme3.math.Quaternion; 12 import com.jme3.math.Transform; 13 import com.jme3.math.Vector3f; 14 import com.jme3.scene.Geometry; 15 import com.jme3.scene.Mesh; 16 import com.jme3.scene.Node; 17 import com.jme3.scene.Spatial; 18 import com.jme3.scene.VertexBuffer.Type; 19 import java.nio.ByteBuffer; 20 import java.nio.FloatBuffer; 21 import java.util.*; 22 23 /** 24 * 25 * @author Nehon 26 */ 27 public class RagdollUtils { 28 setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ)29 public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { 30 31 joint.getRotationalLimitMotor(0).setHiLimit(maxX); 32 joint.getRotationalLimitMotor(0).setLoLimit(minX); 33 joint.getRotationalLimitMotor(1).setHiLimit(maxY); 34 joint.getRotationalLimitMotor(1).setLoLimit(minY); 35 joint.getRotationalLimitMotor(2).setHiLimit(maxZ); 36 joint.getRotationalLimitMotor(2).setLoLimit(minZ); 37 } 38 buildPointMap(Spatial model)39 public static Map<Integer, List<Float>> buildPointMap(Spatial model) { 40 41 42 Map<Integer, List<Float>> map = new HashMap<Integer, List<Float>>(); 43 if (model instanceof Geometry) { 44 Geometry g = (Geometry) model; 45 buildPointMapForMesh(g.getMesh(), map); 46 } else if (model instanceof Node) { 47 Node node = (Node) model; 48 for (Spatial s : node.getChildren()) { 49 if (s instanceof Geometry) { 50 Geometry g = (Geometry) s; 51 buildPointMapForMesh(g.getMesh(), map); 52 } 53 } 54 } 55 return map; 56 } 57 buildPointMapForMesh(Mesh mesh, Map<Integer, List<Float>> map)58 private static Map<Integer, List<Float>> buildPointMapForMesh(Mesh mesh, Map<Integer, List<Float>> map) { 59 60 FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); 61 ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); 62 FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); 63 64 vertices.rewind(); 65 boneIndices.rewind(); 66 boneWeight.rewind(); 67 68 int vertexComponents = mesh.getVertexCount() * 3; 69 int k, start, index; 70 float maxWeight = 0; 71 72 for (int i = 0; i < vertexComponents; i += 3) { 73 74 75 start = i / 3 * 4; 76 index = 0; 77 maxWeight = -1; 78 for (k = start; k < start + 4; k++) { 79 float weight = boneWeight.get(k); 80 if (weight > maxWeight) { 81 maxWeight = weight; 82 index = boneIndices.get(k); 83 } 84 } 85 List<Float> points = map.get(index); 86 if (points == null) { 87 points = new ArrayList<Float>(); 88 map.put(index, points); 89 } 90 points.add(vertices.get(i)); 91 points.add(vertices.get(i + 1)); 92 points.add(vertices.get(i + 2)); 93 } 94 return map; 95 } 96 97 /** 98 * Create a hull collision shape from linked vertices to this bone. 99 * Vertices have to be previoulsly gathered in a map using buildPointMap method 100 * @param link 101 * @param model 102 * @return 103 */ makeShapeFromPointMap(Map<Integer, List<Float>> pointsMap, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition)104 public static HullCollisionShape makeShapeFromPointMap(Map<Integer, List<Float>> pointsMap, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition) { 105 106 ArrayList<Float> points = new ArrayList<Float>(); 107 for (Integer index : boneIndices) { 108 List<Float> l = pointsMap.get(index); 109 if (l != null) { 110 111 for (int i = 0; i < l.size(); i += 3) { 112 Vector3f pos = new Vector3f(); 113 pos.x = l.get(i); 114 pos.y = l.get(i + 1); 115 pos.z = l.get(i + 2); 116 pos.subtractLocal(initialPosition).multLocal(initialScale); 117 points.add(pos.x); 118 points.add(pos.y); 119 points.add(pos.z); 120 } 121 } 122 } 123 124 float[] p = new float[points.size()]; 125 for (int i = 0; i < points.size(); i++) { 126 p[i] = points.get(i); 127 } 128 129 130 return new HullCollisionShape(p); 131 } 132 133 //retruns the list of bone indices of the given bone and its child(if they are not in the boneList) getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList)134 public static List<Integer> getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList) { 135 List<Integer> list = new LinkedList<Integer>(); 136 if (boneList.isEmpty()) { 137 list.add(skeleton.getBoneIndex(bone)); 138 } else { 139 list.add(skeleton.getBoneIndex(bone)); 140 for (Bone chilBone : bone.getChildren()) { 141 if (!boneList.contains(chilBone.getName())) { 142 list.addAll(getBoneIndices(chilBone, skeleton, boneList)); 143 } 144 } 145 } 146 return list; 147 } 148 149 /** 150 * Create a hull collision shape from linked vertices to this bone. 151 * 152 * @param link 153 * @param model 154 * @return 155 */ makeShapeFromVerticeWeights(Spatial model, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold)156 public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold) { 157 158 ArrayList<Float> points = new ArrayList<Float>(); 159 if (model instanceof Geometry) { 160 Geometry g = (Geometry) model; 161 for (Integer index : boneIndices) { 162 points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold)); 163 } 164 } else if (model instanceof Node) { 165 Node node = (Node) model; 166 for (Spatial s : node.getChildren()) { 167 if (s instanceof Geometry) { 168 Geometry g = (Geometry) s; 169 for (Integer index : boneIndices) { 170 points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold)); 171 } 172 173 } 174 } 175 } 176 float[] p = new float[points.size()]; 177 for (int i = 0; i < points.size(); i++) { 178 p[i] = points.get(i); 179 } 180 181 182 return new HullCollisionShape(p); 183 } 184 185 /** 186 * returns a list of points for the given bone 187 * @param mesh 188 * @param boneIndex 189 * @param offset 190 * @param link 191 * @return 192 */ getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold)193 private static List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) { 194 195 FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); 196 ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); 197 FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); 198 199 vertices.rewind(); 200 boneIndices.rewind(); 201 boneWeight.rewind(); 202 203 ArrayList<Float> results = new ArrayList<Float>(); 204 205 int vertexComponents = mesh.getVertexCount() * 3; 206 207 for (int i = 0; i < vertexComponents; i += 3) { 208 int k; 209 boolean add = false; 210 int start = i / 3 * 4; 211 for (k = start; k < start + 4; k++) { 212 if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) { 213 add = true; 214 break; 215 } 216 } 217 if (add) { 218 219 Vector3f pos = new Vector3f(); 220 pos.x = vertices.get(i); 221 pos.y = vertices.get(i + 1); 222 pos.z = vertices.get(i + 2); 223 pos.subtractLocal(offset).multLocal(initialScale); 224 results.add(pos.x); 225 results.add(pos.y); 226 results.add(pos.z); 227 228 } 229 } 230 231 return results; 232 } 233 234 /** 235 * Updates a bone position and rotation. 236 * if the child bones are not in the bone list this means, they are not associated with a physic shape. 237 * So they have to be updated 238 * @param bone the bone 239 * @param pos the position 240 * @param rot the rotation 241 */ setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set<String> boneList)242 public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set<String> boneList) { 243 //we ensure that we have the control 244 if (restoreBoneControl) { 245 bone.setUserControl(true); 246 } 247 //we set te user transforms of the bone 248 bone.setUserTransformsWorld(pos, rot); 249 for (Bone childBone : bone.getChildren()) { 250 //each child bone that is not in the list is updated 251 if (!boneList.contains(childBone.getName())) { 252 Transform t = childBone.getCombinedTransform(pos, rot); 253 setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl, boneList); 254 } 255 } 256 //we give back the control to the keyframed animation 257 if (restoreBoneControl) { 258 bone.setUserControl(false); 259 } 260 } 261 setUserControl(Bone bone, boolean bool)262 public static void setUserControl(Bone bone, boolean bool) { 263 bone.setUserControl(bool); 264 for (Bone child : bone.getChildren()) { 265 setUserControl(child, bool); 266 } 267 } 268 } 269