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 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 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 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 */ 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) 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 */ 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 */ 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 */ 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 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