unfortunately i havent got a small sample that shows the bug clearly. but you can try this code i’ve been working on and check if the legs and arms move as one would expect. i’ve been experiencing that the bones were stuck in one direction or did move freely (in one axis-rotation) with the previous version. (i hope the parameters correspond to my workaround).
models and “normal” code:
http://www.nouser.org/WD/projects/sparetime/humanModel/ragdoll_2009-08-21-v002.zip
replace the ragdoll_v005.py contents with:
import sys, os, random, time, math
from pandac.PandaModules import *
loadPrcFileData("", "basic-shaders-only #f")
loadPrcFileData("", "show-frame-rate-meter 1")
loadPrcFileData("", "vsync #f")
import direct.directbase.DirectStart
from direct.showbase.DirectObject import *
from direct.actor.Actor import Actor
from direct.task import Task
dt = 0.03
loopFlag = True
world = OdeWorld()
world.setGravity((0,0,-9))
world.setQuickStepNumIterations(600)
# The surface table is needed for autoCollide
world.initSurfaceTable(1)
# pos1, pos2, mu, bounce, bounce_vel, soft_erp, soft_cfm, slip, dampen
# soft_erp (0.2 default)
# soft_cfm (0 is hard, larger = soft)
# dampen (0.01 default)
world.setSurfaceEntry(0, 0, 100, 0.0, 0.0, 0.8, 0.0, 0.0, 0.01)
space = OdeSimpleSpace()
space.setAutoCollideWorld(world)
contactgroup = OdeJointGroup()
space.setAutoCollideJointGroup(contactgroup)
space.setCollisionEvent("ode-collision")
render.setShaderAuto(1)
class World(DirectObject):
def __init__(self):
# create plane
plane = loader.loadModel('plane')
plane.setScale(2)
plane.reparentTo(render)
self.plane = OdePlaneGeom(space, 0, 0, 1, 0)
self.plane.setCollideBits(BitMask32(0x00000001))
self.plane.setCategoryBits(BitMask32(0x00000001))
self.rag1 = Ragdoll() #make an instance of Ragdoll class
c = 0.7
self.charLight = Spotlight('slight')
self.charLight.setColor(VBase4(c,c,c, 1))
#self.charLight.setExponent(32.0)
try:
self.charLight.setShadowCaster(True, 2048, 2048)
pass
except:
pass
#lens = PerspectiveLens()
#lens.setFov(45)
#self.charLight.setLens(lens)
self.charLightNp = render.attachNewNode(self.charLight)
self.charLightNp.setPos(2, 2, 20)
self.charLightNp.lookAt(1, 1, 1)
render.setLight(self.charLightNp)
c = 0.3
self.alight = AmbientLight('alight')
self.alight.setColor(VBase4(c,c,c, 1))
self.alnp = render.attachNewNode(self.alight)
render.setLight(self.alnp)
f = 3
base.accept('e', self.addForce, [Vec3(0,0,f)])
base.accept('q', self.addForce, [Vec3(0,0,-f)])
base.accept('a', self.addForce, [Vec3(-f,0,0)])
base.accept('d', self.addForce, [Vec3(f,0,0)])
base.accept('w', self.addForce, [Vec3(0,f,0)])
base.accept('s', self.addForce, [Vec3(0,-f,0)])
base.accept('r', self.reset)
self.doSimulate = False
base.accept('space', self.toggleSimulate)
#base.camera.setPos(20,20,30)
self.simulate()
taskMgr.add(self.simulate, "runSim")
def reset(self):
print "reset"
for i in xrange(len(self.rag1.bones)):
body = self.rag1.bones[i].body
print i,self.rag1.bones[i].body
body.setTorque(0,0,0)
body.setAngularVel(0,0,0)
body.setLinearVel(0,0,0)
body.setForce(0,0,0)
self.rag1.bones[i].reset()
#body.setRotation(Mat3.identMat())
print body.getAngularVel()
print
def toggleSimulate(self):
self.doSimulate = not self.doSimulate
if self.doSimulate:
self.reset()
def addForce(self, force):
for i in xrange(len(self.rag1.bones)):
self.rag1.bones[i].body.addForce(force * (random.random()* 0.9 + 0.2))
#self.rag1.boneDict['neck'].body.addForce(force)
def simulate(self,task=None):
if self.doSimulate:
for i in xrange(len(self.rag1.bones)):
self.rag1.bones[i].updateSolid()
# artificial slowdown
#bone.body.setAngularVel( bone.body.getAngularVel() * 0.9 )
#bone.body.setLinearVel( bone.body.getLinearVel() * 0.9 )
space.autoCollide()
world.quickStep(dt)
contactgroup.empty()
#base.camera.lookAt(self.rag1.bones[0].solid.getPos(render))
#self.charLightNp.lookAt(self.rag1.bones[0].solid.getPos(render))
return Task.cont
base.accept('p', base.toggleWireframe)
base.accept('escape', sys.exit)
class Ragdoll():
def __init__(self):
self.actor = Actor('char-20-v004.egg')
self.actor.reparentTo(render)
#self.actor.setRenderModeWireframe()
# keep this if you ever want to modify the actor again...
#print "getActorInfo", playerActor.getActorInfo()
#print "getAnimNames", playerActor.getAnimNames()
#print "listJoints", self.actor.listJoints()
# expose the joints of the model
jointConnections = list()
actorArmature = self.actor.getJoints(jointName='*')
exposedJoints = dict()
exposedParents = dict()
for parentJoint in actorArmature:
# dont expose fingers and toes, it's too much detail
if 'finger' in parentJoint.getName() or 'toe' in parentJoint.getName():
continue
else:
for i in xrange(parentJoint.getNumChildren()):
childJoint = parentJoint.getChild(i)
jointConnections.append([parentJoint, childJoint])
exposedParents[childJoint.getName()] = parentJoint.getName()
exposedJoints[parentJoint.getName()] = self.actor.controlJoint(None,"modelRoot",parentJoint.getName())
# we need to restore the parents of the nodes
for childJointName, parentJointName in exposedParents.items():
if childJointName in exposedJoints:
print "reparent", childJointName, "to", parentJointName
exposedJoints[childJointName].reparentTo(exposedJoints[parentJointName])
exposedJoints['pelvis'].reparentTo(self.actor)
# create the bones
boneDict = dict()
for pJ, cJ in jointConnections:
pJName = pJ.getName()
cJName = cJ.getName()
if pJName in exposedJoints and cJName in exposedJoints:
startJoint = exposedJoints[pJName]
endJoint = exposedJoints[cJName]
pos1 = startJoint.getPos(render)
pos2 = endJoint.getPos(render)
radius = (pos1-pos2).length() / 7.0
length = (pos1-pos2).length() * 0.25
print "bone", pJName, cJName, pos1, pos2, radius
bone = Bone(exposedJoints[cJName], pos1, pos2, radius, length)
boneDict[cJName] = bone
startJoint = exposedJoints['pelvis']
endJoint = exposedJoints['pelvis']
pos1 = startJoint.getPos(render)
pos2 = endJoint.getPos(render)
bone = Bone(exposedJoints['pelvis'], pos1, pos2, radius, radius)
boneDict['pelvis'] = bone
self.bones = boneDict.values()[:]
self.boneDict = boneDict
self.exposedJoints = exposedJoints
joints = list()
p = math.pi
p34 = math.pi*3/4 * 0.75
p2 = math.pi/2 * 0.75
p4 = math.pi/4 * 0.75
p8 = math.pi/8 * 0.75
# when creating a new model (modifying the existing)
# define all jointConnectors as ['fix', []] first, and modifiy each invidually
jointConnectors = {
('pelvis', 'L.upper-leg') : ['fix', []], #['uni', [(1,0,0),0,p34, (0,1,0),0,p4]],
('pelvis', 'R.upper-leg') : ['fix', []], #['uni', [(1,0,0),0,p34, (0,1,0),0,p4]],
('pelvis', 'back') : ['fix', []], #['uni', [(1,0,0),-p8,p8, (0,1,0),-p8,p8]],
('L.upper-leg', 'L.lower-leg'): ['fix', []], #['hin', [(1,0,0), -p34,0]],
('L.lower-leg', 'L.footbase') : ['uni', [(1,0,0),-p4,p4, (0,1,0),-p4,p4]],
('R.upper-leg', 'R.lower-leg'): ['fix', []], #['hin', [(1,0,0), -p34,0]],
('R.lower-leg', 'R.footbase') : ['uni', [(1,0,0),-p4,p4, (0,1,0),-p4,p4]],
('back', 'L.shoulder') : ['fix', []], #['uni', [(1,0,0),-p8,p8, (0,1,0),-p8,p8]],
('back', 'R.shoulder') : ['fix', []], #['uni', [(0,0,1),-p8,p8, (0,1,0),-p8,p8]],
('back', 'neck') : ['fix', []], #['uni', [(0,0,1),-p8,p8, (0,1,0),-p8,p8]],
('L.shoulder', 'L.upper-arm') : ['uni', [(0,0,1),0,p2, (0,1,0),-p2,p2]], # good
('L.upper-arm', 'L.lower-arm'): ['fix', []], #['hin', [(0,0,1), 0,p34]], # good
('L.lower-arm', 'L.hand') : ['fix', []], #['uni', [(0,0,1),-p4,p4, (0,1,0),-p4,p4]],
('R.shoulder', 'R.upper-arm') : ['uni', [(0,0,1),-p2,0, (0,1,0),-p2,p2]], # good
('R.upper-arm', 'R.lower-arm'): ['fix', []], #['hin', [(0,0,1), -p34,0]], # good
('R.lower-arm', 'R.hand') : ['fix', []], #['uni', [(0,0,1),-p4,p4, (0,1,0),-p4,p4]],
('neck', 'head') : ['fix', []], #['uni', [(1,0,0),-p8,p8, (0,1,0),-p8,p8]],
}
for pJ, cJ in jointConnections:
pJName = pJ.getName()
cJName = cJ.getName()
if pJName in boneDict and cJName in boneDict:
pBone = boneDict[pJName]
cBone = boneDict[cJName]
jType, jArgs = jointConnectors[(pJName, cJName)]
if jType == 'fix':
joint = self.fixedJoint(pBone, cBone)
elif jType == 'uni':
joint = self.universalJoint(pBone, cBone, jArgs)
elif jType == 'hin':
joint = self.hingeJoint(pBone, cBone, jArgs)
joints.append(joint)
#print "('%s', '%s'): []," % (pJName, cJName)
print "create done"
if 0:
# attach head to the world
headJoint = OdeBallJoint(world)
headJoint.attach(boneDict['head'].body, None)
headJoint.setAnchor((boneDict['head'].body.getPosition())) #+Vec3(0,0,10)))
def universalJoint(self,obj1,obj2,jArgs):
axis1, loStop1, hiStop1, axis2, loStop2, hiStop2 = jArgs
joint = OdeUniversalJoint(world)
joint.attach(obj1.body,obj2.body)
joint.setAnchor((obj2.body.getPosition()))
joint.setAxis1(axis1)
joint.setParamLoStop(0,loStop1)
joint.setParamHiStop(0,hiStop1)
joint.setAxis2(axis2)
joint.setParamLoStop(1,loStop2)
joint.setParamHiStop(1,hiStop2)
return joint
def hingeJoint(self,obj1,obj2,jArgs):
axis, loStop, hiStop = jArgs
#for elbows and khees
joint = OdeHingeJoint(world)
joint.attach(obj1.body,obj2.body)
joint.setAnchor((obj2.body.getPosition()))
joint.setAxis(axis)
joint.setParamLoStop(loStop)
joint.setParamHiStop(hiStop)
return joint
def ballJoint(self,obj1,obj2):
#not in use for now
joint = OdeBallJoint(world)
joint.attach(obj1.body,obj2.body)
joint.setAnchor((obj2.body.getPosition()))
return joint
def fixedJoint(self,obj1,obj2):
joint = OdeFixedJoint(world)
joint.attach(obj1.body,obj2.body)
joint.set()
return joint
class Bone():
def __init__(self,parent,startPos,endPos,radius,length):
# create panda node to visualisation
s2 = loader.loadModel('RGB_CUBE.egg')
s2.reparentTo(parent)
s2.setScale(render, radius, length, radius)
s2.setPos(render, endPos)
# create mass object to represent body mass
self.M = OdeMass()
self.M.setSphere(5, 0.1)
#now we will create ode body
self.body = OdeBody(world)
self.body.setMass(self.M)
# create geometry to collisions
self.geom1 = OdeBoxGeom(space, radius, length, radius)
self.geom1.setBody(self.body)
# ajust position of ode body node
#self.body.setQuaternion(s2.getQuat(render))
#self.body.setPosition(s2.getPos(render))
self.startQuat = s2.getQuat(render)
self.startPos = s2.getPos(render)
self.solid = parent
self.reset()
self.updateSolid()
def updateSolid(self):
# dont change this, it's correct
self.solid.setPosQuat(render, self.geom1.getPosition(), Quat(self.geom1.getQuaternion()))
def reset(self):
self.body.setQuaternion(self.startQuat)
self.body.setPosition(self.startPos)
w = World()
run()
press to start the physics,
q: down
e: up
w: forward
s: backward
a: left
d: right
r: restart/reset
zoom out with the mouse first, it’s the default controls
you could play around with the jointConnectors vectors if the rotations seem wrong as well. i have fixed some of the joints so it’s more clearly visible if it works fine.