git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9838 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
302 lines
11 KiB
Java
302 lines
11 KiB
Java
/*
|
|
* 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.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);
|
|
}
|
|
}
|
|
}
|