Now it should work
Good to debug:
package emp.physics.ragdoll;
import com.jme.animation.Bone;
import com.jme.math.Matrix3f;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.model.md5.interfaces.IMD5Node;
import com.model.md5.interfaces.mesh.IJoint;
public class MD5Bone extends Bone {
private IJoint org;
private MD5Bone(IJoint originalBone){
super(originalBone.getName());
org=originalBone;
}
public MD5Bone(IMD5Node node) {
IJoint[] js = node.getJoints();
int l = js.length;
MD5Bone[] our = new MD5Bone[l];
for (int i = 0; i < l; i++) {
IJoint parent = js[i].getParent();
if(parent!=null)
our[i] = new MD5Bone(js[i]);
else{
skinRoot=true;
org=js[i];
setName(org.getName()+"-node");
our[i] = this;
}
}
for (int i = 0; i < our.length; i++) {
IJoint parent = js[i].getParent();
if(parent!=null){
our[parent.getIndex()].attachChild(our[i]);
}
}
}
@Override
public void updateGeometricState(float time, boolean initiator) {
super.setLocalTranslation(org.getTranslation());
super.setLocalRotation(org.getOrientation());
super.updateGeometricState(time, initiator);
}
@Override
public void setLocalRotation(Matrix3f rotation) {
Quaternion rot = new Quaternion();
rot.fromRotationMatrix(rotation);
setLocalRotation(rot);
}
@Override
public void setLocalRotation(Quaternion rotation) {
org.updateTransform(localTranslation, rotation);
}
@Override
public void setLocalTranslation(float x, float y, float z) {
setLocalTranslation(new Vector3f(x,y,z));
}
@Override
public void setLocalTranslation(Vector3f localTranslation) {
org.updateTransform(localTranslation, localRotation);
}
public final IJoint getOrg() {
return org;
}
}
More DebugStuff
package emp.physics.ragdoll;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.List;
import com.jme.bounding.BoundingBox;
import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.TriMesh;
import com.jme.util.geom.BufferUtils;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsCollisionGeometry;
import com.jmex.physics.PhysicsDebugger;
import com.jmex.physics.PhysicsNode;
import com.jmex.physics.geometry.PhysicsMesh;
public class DynamicMesh extends PhysicsCollisionGeometry {
private TriMesh triMesh;
private PhysicsMesh ph;
public DynamicMesh(String name, PhysicsNode physicsNode, List<Triangle> triangles){
super(physicsNode);
setName(name);
int size = triangles.size();
FloatBuffer vertices = BufferUtils.createFloatBuffer(size*9);
FloatBuffer normals = BufferUtils.createFloatBuffer(size*9);
IntBuffer indices = BufferUtils.createIntBuffer(size*9);
for (int i = 0; i < size; i++) {
Triangle t = triangles.get(i);
for (int j = 0; j < 3; j++) {
BufferUtils.setInBuffer(t.get(j), vertices, i*3+j);
indices.put(i*3+j);
BufferUtils.setInBuffer(Vector3f.UNIT_Y, normals, i*3+j);
}
}
triMesh = new TriMesh(name, vertices,normals,null,null,indices );
triMesh.setCullHint(CullHint.Always);
ph = physicsNode.createMesh(name);
ph.copyFrom(triMesh);
PhysicsDebugger.setupDebugGeom(triMesh);
physicsNode.attachChild(this);
if(!physicsNode.isStatic()){
DynamicPhysicsNode dyn = (DynamicPhysicsNode)physicsNode;
float mass = dyn.getMass();
mass+=dyn.getMaterial().getDensity()*getVolume();
dyn.setMass(mass);
}
}
@Override
protected void drawDebugShape(PhysicsNode physicsNode, Renderer renderer) {
triMesh.setLocalTranslation(ph.getWorldTranslation());
triMesh.setLocalRotation(ph.getWorldRotation());
triMesh.setLocalScale(ph.getWorldScale());
PhysicsDebugger.drawDebugShape( triMesh, triMesh.getWorldTranslation(), this, renderer, 1 );
}
@Override
public float getVolume() {
BoundingBox b = new BoundingBox();
b.computeFromPoints(triMesh.getVertexBuffer());
return b.getVolume();
}
}
the PhysicStuff
package emp.physics.ragdoll.test;
import java.util.ArrayList;
import java.util.List;
import com.jme.animation.Bone;
import com.jme.math.Quaternion;
import com.jme.math.Triangle;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.Joint;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.RotationalJointAxis;
import com.jmex.physics.material.Material;
import com.model.md5.MD5Node;
import com.model.md5.interfaces.mesh.IJoint;
import com.model.md5.interfaces.mesh.primitive.IWeight;
import com.model.md5.resource.mesh.Mesh;
import com.model.md5.resource.mesh.primitive.Vertex;
import emp.physics.ragdoll.DynamicMesh;
public class PhysicBone extends Node {
private DynamicPhysicsNode parentBody;
private DynamicPhysicsNode body;
private Bone realBone;
private Vector3f meshScale;
private MD5Node mesh;
public PhysicBone(Bone md, MD5Node mesh, PhysicsSpace space) {
this(md, mesh.getParent(),null, mesh.getLocalScale(), space, mesh);
this.mesh = mesh;
}
private PhysicBone(Bone md, Node parent, PhysicBone myParent, Vector3f meshScale, PhysicsSpace space, MD5Node source) {
super(md.getName());
parent.attachChild(this);
this.meshScale = meshScale;
realBone = md;
Vector3f myPos = md.getWorldTranslation().clone();
Vector3f parentPos = md.getParent().getWorldTranslation();
Quaternion myRot = md.getWorldRotation().clone();
float height = myPos.distanceSquared(parentPos);
if(height>0){
body = space.createDynamicNode();
// TODO: choose yourself, if you need this line
// body.createSphere("s").setLocalScale(.05f);
body.setMaterial(Material.WOOD);
attachSkin(source);
body.setLocalTranslation(myPos);
body.setLocalRotation(myRot);
attachChild(body);
if(myParent!=null&&myParent.body!=null){
Vector3f ncor = new Vector3f(0,0,0);
join( myParent.body,body, ncor,getLookAtDirection(myRot), -.1f, .1f);
parentBody = myParent.body;
}
}else{
body=myParent.body;
parentBody=myParent.parentBody;
}
if(md.getName().contains("hand")){
return;
}
for (int i = 0; i < md.getQuantity(); i++) {
Spatial child = md.getChild(i);
if(child instanceof Bone){
new PhysicBone((Bone) child,this, this,meshScale, space, source);
}
}
}
private void attachSkin(MD5Node modelnode) {
Mesh m = (Mesh) modelnode.getMesh(0);
int index = -1;
IJoint[] allJ = modelnode.getJoints();
for (int i = 0; i < allJ.length; i++) {
if(allJ[i].getName().equals(name)){
index = i;
break;
}
}
List<Triangle> tris = new ArrayList<Triangle>();
for (int i = 0; i < m.getTriangleCount(); i++) {
int[] storage = new int[3];
m.getTriangle(i, storage);
if(checkTriangle(storage, m, index)){
Vector3f[] vertices = new Vector3f[3];
m.getTriangle(i, vertices);
for (int j = 0; j < vertices.length; j++) {
Vector3f bPos = vertices[j];
modelnode.localToWorld(bPos, bPos);
bPos.addLocal(realBone.getWorldTranslation().negate());
Quaternion rot = realBone.getWorldRotation().inverse().mult(body.getLocalRotation());
rot.mult(bPos,bPos);
}
Triangle triangle = new Triangle(vertices[0], vertices[1], vertices[2]);
tris.add(triangle);
}
}
int size = tris.size();
if(size>0){
new DynamicMesh(name, body, tris);
}
}
private boolean checkTriangle(int[] vStorage, Mesh m, int jIndex){
int b = 0;
for (int j = 0; j < vStorage.length; j++) {
if(checkVertex(vStorage[j], m, jIndex)){
b++;
}
}
if(b=:3){
return true;
}
return false;
}
private boolean checkVertex(int vIndex, Mesh m, int jIndex){
IWeight[] wall = ((Vertex)m.getVertex(vIndex)).getWeightIndices();
float max = 0;
IWeight wMax = null;
for (int i = 0; i < wall.length; i++) {
float v = wall[i].getWeightValue();
if(v>max){
wMax = wall[i];
max = v;
}
}
if(wMax.getJoint().getIndex()==jIndex){
return true;
}
return false;
}
@Override
public void updateGeometricState(float time, boolean initiator) {
if(parentBody!=null){
Vector3f store = new Vector3f();
parentBody.worldToLocal(body.getLocalTranslation(), store);
store.divideLocal(meshScale);
realBone.setLocalTranslation(store);
Quaternion res = new Quaternion();
parentBody.getLocalRotation().inverse().mult(body.getLocalRotation(), res);
realBone.setLocalRotation(res);
}else{
realBone.setLocalTranslation(Vector3f.ZERO);
realBone.setLocalRotation(new Quaternion());
}
if(mesh!=null){
mesh.setLocalTranslation(new Vector3f(body.getLocalTranslation()));
mesh.setLocalRotation(new Quaternion(body.getLocalRotation()));
mesh.flagUpdate();
}
super.updateGeometricState(time, initiator);
}
public static Joint join(DynamicPhysicsNode node1, DynamicPhysicsNode node2, Vector3f anchor, Vector3f direction, float min, float max) {
Joint jmeJoint = node1.getSpace().createJoint();
RotationalJointAxis jmeAxis = jmeJoint.createRotationalAxis();
jmeAxis.setPositionMinimum(min);
jmeAxis.setPositionMaximum(max);
jmeJoint.attach(node1, node2);
jmeJoint.setAnchor(anchor);
jmeAxis.setDirection(direction);
return jmeJoint;
}
public static Vector3f getLookAtDirection(Quaternion quaternion) {
Vector3f[] neu =new Vector3f[3];
quaternion.toAxes(neu);
return new Vector3f(neu[1].crossLocal(neu[0]).normalizeLocal().negateLocal());
}
}
and the TestClass
package emp.physics.ragdoll.test;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme.animation.Bone;
import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.util.BoneDebugger;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.SimplePhysicsGame;
import com.model.md5.MD5Node;
import com.model.md5.importer.MD5Importer;
import emp.physics.ragdoll.MD5Bone;
public class RagDollParseTest extends SimplePhysicsGame {
private boolean showBones;
@Override
protected void simpleInitGame() {
pause = true;
initGround();
parseRhino(rootNode);
rootNode.updateGeometricState(0, true);
rootNode.updateRenderState();
input.addAction( new InputAction() {
public void performAction( InputActionEvent evt ) {
if ( evt.getTriggerPressed() ) {
showBones = !showBones;
}
}
}, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_SPACE, InputHandler.AXIS_NONE, false );
}
@Override
protected void doDebug(Renderer r) {
super.doDebug(r);
if(showBones){
BoneDebugger.drawBones(rootNode, r, true);
}
}
public PhysicBone parseRhino(Node rootNode) {
PhysicBone character = null;
MD5Node rhino = loadOnlyMd5Model("/test/model/md5/data/marine");
rhino.getControllers().clear();
MD5Bone md = new MD5Bone(rhino);
rhino.attachChild(md);
rhino.setLocalScale(.2f);
rhino.updateGeometricState(0, true);
rootNode.attachChild(rhino);
character = new PhysicBone((Bone) md.getChild(0), rhino, getPhysicsSpace());
return character;
}
public static MD5Node loadOnlyMd5Model(String path) {
MD5Importer im = MD5Importer.getInstance();
MD5Node node;
try {
im.loadMesh(MD5Importer.class.getResource(path+".md5mesh"), "m5s");
}
catch (Exception e1) {
e1.printStackTrace();
}
node = (MD5Node) im.getModelNode();
im.cleanup();
return node;
}
private void initGround(){
StaticPhysicsNode staticNode = getPhysicsSpace().createStaticNode();
Spatial floorBox = new Box("Terrain", new Vector3f(0, 0, 0), 100, 1f, 100);
floorBox.setModelBound(new BoundingBox());
floorBox.updateModelBound();
staticNode.attachChild(floorBox);
staticNode.getLocalTranslation().set(0, -14, 0);
staticNode.updateGeometricState(0, false);
staticNode.generatePhysicsGeometry();
staticNode.setMaterial(Material.GRANITE);
rootNode.attachChild(staticNode);
}
public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
BaseGame game = new RagDollParseTest();
game.setConfigShowMode(ConfigShowMode.AlwaysShow);
game.start();
}
}
now I use the actualy Version from the MD5Importer for JME2.0 from their SVN.
In the class "Vertex" from the Importer you have to add the fuction:
public IWeight[] getWeightIndices() {
return weights;
}