# Copyright (c) 2007, Matt Heinzen
# 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.
#
# THIS SOFTWARE IS PROVIDED BY MATT HEINZEN ``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 MATT HEINZEN 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.


import sys, os, random, time
from math import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import ode

def sign(x):
	"""Returns 1.0 if x is positive, -1.0 if x is negative or zero."""
	if x > 0.0: return 1.0
	else: return -1.0

def len3(v):
	"""Returns the length of 3-vector v."""
	return sqrt(v[0]**2 + v[1]**2 + v[2]**2)

def neg3(v):
	"""Returns the negation of 3-vector v."""
	return (-v[0], -v[1], -v[2])

def add3(a, b):
	"""Returns the sum of 3-vectors a and b."""
	return (a[0] + b[0], a[1] + b[1], a[2] + b[2])

def sub3(a, b):
	"""Returns the difference between 3-vectors a and b."""
	return (a[0] - b[0], a[1] - b[1], a[2] - b[2])

def mul3(v, s):
	"""Returns 3-vector v multiplied by scalar s."""
	return (v[0] * s, v[1] * s, v[2] * s)

def div3(v, s):
	"""Returns 3-vector v divided by scalar s."""
	return (v[0] / s, v[1] / s, v[2] / s)

def dist3(a, b):
	"""Returns the distance between point 3-vectors a and b."""
	return len3(sub3(a, b))

def norm3(v):
	"""Returns the unit length 3-vector parallel to 3-vector v."""
	l = len3(v)
	if (l > 0.0): return (v[0] / l, v[1] / l, v[2] / l)
	else: return (0.0, 0.0, 0.0)

def dot3(a, b):
	"""Returns the dot product of 3-vectors a and b."""
	return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])

def cross(a, b):
	"""Returns the cross product of 3-vectors a and b."""
	return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
		a[0] * b[1] - a[1] * b[0])

def project3(v, d):
	"""Returns projection of 3-vector v onto unit 3-vector d."""
	return mul3(v, dot3(norm3(v), d))

def acosdot3(a, b):
	"""Returns the angle between unit 3-vectors a and b."""
	x = dot3(a, b)
	if x < -1.0: return pi
	elif x > 1.0: return 0.0
	else: return acos(x)

def rotate3(m, v):
	"""Returns the rotation of 3-vector v by 3x3 (row major) matrix m."""
	return (v[0] * m[0] + v[1] * m[1] + v[2] * m[2],
		v[0] * m[3] + v[1] * m[4] + v[2] * m[5],
		v[0] * m[6] + v[1] * m[7] + v[2] * m[8])

def invert3x3(m):
	"""Returns the inversion (transpose) of 3x3 rotation matrix m."""
	return (m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8])

def zaxis(m):
	"""Returns the z-axis vector from 3x3 (row major) rotation matrix m."""
	return (m[2], m[5], m[8])

def calcRotMatrix(axis, angle):
	"""
	Returns the row-major 3x3 rotation matrix defining a rotation around axis by
	angle.
	"""
	cosTheta = cos(angle)
	sinTheta = sin(angle)
	t = 1.0 - cosTheta
	return (
		t * axis[0]**2 + cosTheta,
		t * axis[0] * axis[1] - sinTheta * axis[2],
		t * axis[0] * axis[2] + sinTheta * axis[1],
		t * axis[0] * axis[1] + sinTheta * axis[2],
		t * axis[1]**2 + cosTheta,
		t * axis[1] * axis[2] - sinTheta * axis[0],
		t * axis[0] * axis[2] - sinTheta * axis[1],
		t * axis[1] * axis[2] + sinTheta * axis[0],
		t * axis[2]**2 + cosTheta)

def makeOpenGLMatrix(r, p):
	"""
	Returns an OpenGL compatible (column-major, 4x4 homogeneous) transformation
	matrix from ODE compatible (row-major, 3x3) rotation matrix r and position
	vector p.
	"""
	return (
		r[0], r[3], r[6], 0.0,
		r[1], r[4], r[7], 0.0,
		r[2], r[5], r[8], 0.0,
		p[0], p[1], p[2], 1.0)

def getBodyRelVec(b, v):
	"""
	Returns the 3-vector v transformed into the local coordinate system of ODE
	body b.
	"""
	return rotate3(invert3x3(b.getRotation()), v)


# rotation directions are named by the third (z-axis) row of the 3x3 matrix,
#   because ODE capsules are oriented along the z-axis
rightRot = (0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0)
leftRot = (0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0)
upRot = (1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0)
downRot = (1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0)
bkwdRot = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)

# axes used to determine constrained joint rotations
rightAxis = (1.0, 0.0, 0.0)
leftAxis = (-1.0, 0.0, 0.0)
upAxis = (0.0, 1.0, 0.0)
downAxis = (0.0, -1.0, 0.0)
bkwdAxis = (0.0, 0.0, 1.0)
fwdAxis = (0.0, 0.0, -1.0)

UPPER_ARM_LEN = 0.30
FORE_ARM_LEN = 0.25
HAND_LEN = 0.13 # wrist to mid-fingers only
FOOT_LEN = 0.18 # ankles to base of ball of foot only
HEEL_LEN = 0.05

BROW_H = 1.68
MOUTH_H = 1.53
NECK_H = 1.50
SHOULDER_H = 1.37
CHEST_H = 1.35
HIP_H = 0.86
KNEE_H = 0.48
ANKLE_H = 0.08

SHOULDER_W = 0.41
CHEST_W = 0.36 # actually wider, but we want narrower than shoulders (esp. with large radius)
LEG_W = 0.28 # between middles of upper legs
PELVIS_W = 0.25 # actually wider, but we want smaller than hip width

R_SHOULDER_POS = (-SHOULDER_W * 0.5, SHOULDER_H, 0.0)
L_SHOULDER_POS = (SHOULDER_W * 0.5, SHOULDER_H, 0.0)
R_ELBOW_POS = sub3(R_SHOULDER_POS, (UPPER_ARM_LEN, 0.0, 0.0))
L_ELBOW_POS = add3(L_SHOULDER_POS, (UPPER_ARM_LEN, 0.0, 0.0))
R_WRIST_POS = sub3(R_ELBOW_POS, (FORE_ARM_LEN, 0.0, 0.0))
L_WRIST_POS = add3(L_ELBOW_POS, (FORE_ARM_LEN, 0.0, 0.0))
R_FINGERS_POS = sub3(R_WRIST_POS, (HAND_LEN, 0.0, 0.0))
L_FINGERS_POS = add3(L_WRIST_POS, (HAND_LEN, 0.0, 0.0))

R_HIP_POS = (-LEG_W * 0.5, HIP_H, 0.0)
L_HIP_POS = (LEG_W * 0.5, HIP_H, 0.0)
R_KNEE_POS = (-LEG_W * 0.5, KNEE_H, 0.0)
L_KNEE_POS = (LEG_W * 0.5, KNEE_H, 0.0)
R_ANKLE_POS = (-LEG_W * 0.5, ANKLE_H, 0.0)
L_ANKLE_POS = (LEG_W * 0.5, ANKLE_H, 0.0)
R_HEEL_POS = sub3(R_ANKLE_POS, (0.0, 0.0, HEEL_LEN))
L_HEEL_POS = sub3(L_ANKLE_POS, (0.0, 0.0, HEEL_LEN))
R_TOES_POS = add3(R_ANKLE_POS, (0.0, 0.0, FOOT_LEN))
L_TOES_POS = add3(L_ANKLE_POS, (0.0, 0.0, FOOT_LEN))


class RagDoll():
	def __init__(self, world, space, density, offset = (0.0, 0.0, 0.0)):
		"""Creates a ragdoll of standard size at the given offset."""

		self.world = world
		self.space = space
		self.density = density
		self.bodies = []
		self.geoms = []
		self.joints = []
		self.totalMass = 0.0

		self.offset = offset

		self.chest = self.addBody((-CHEST_W * 0.5, CHEST_H, 0.0),
			(CHEST_W * 0.5, CHEST_H, 0.0), 0.13)
		self.belly = self.addBody((0.0, CHEST_H - 0.1, 0.0),
			(0.0, HIP_H + 0.1, 0.0), 0.125)
		self.midSpine = self.addFixedJoint(self.chest, self.belly)
		self.pelvis = self.addBody((-PELVIS_W * 0.5, HIP_H, 0.0),
			(PELVIS_W * 0.5, HIP_H, 0.0), 0.125)
		self.lowSpine = self.addFixedJoint(self.belly, self.pelvis)

		self.head = self.addBody((0.0, BROW_H, 0.0), (0.0, MOUTH_H, 0.0), 0.11)
		self.neck = self.addBallJoint(self.chest, self.head,
			(0.0, NECK_H, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, 1.0), pi * 0.25,
			pi * 0.25, 80.0, 40.0)

		self.rightUpperLeg = self.addBody(R_HIP_POS, R_KNEE_POS, 0.11)
		self.rightHip = self.addUniversalJoint(self.pelvis, self.rightUpperLeg,
			R_HIP_POS, bkwdAxis, rightAxis, -0.1 * pi, 0.3 * pi, -0.15 * pi,
			0.75 * pi)
		self.leftUpperLeg = self.addBody(L_HIP_POS, L_KNEE_POS, 0.11)
		self.leftHip = self.addUniversalJoint(self.pelvis, self.leftUpperLeg,
			L_HIP_POS, fwdAxis, rightAxis, -0.1 * pi, 0.3 * pi, -0.15 * pi,
			0.75 * pi)

		self.rightLowerLeg = self.addBody(R_KNEE_POS, R_ANKLE_POS, 0.09)
		self.rightKnee = self.addHingeJoint(self.rightUpperLeg,
			self.rightLowerLeg, R_KNEE_POS, leftAxis, 0.0, pi * 0.75)
		self.leftLowerLeg = self.addBody(L_KNEE_POS, L_ANKLE_POS, 0.09)
		self.leftKnee = self.addHingeJoint(self.leftUpperLeg,
			self.leftLowerLeg, L_KNEE_POS, leftAxis, 0.0, pi * 0.75)

		self.rightFoot = self.addBody(R_HEEL_POS, R_TOES_POS, 0.09)
		self.rightAnkle = self.addHingeJoint(self.rightLowerLeg,
			self.rightFoot, R_ANKLE_POS, rightAxis, -0.1 * pi, 0.05 * pi)
		self.leftFoot = self.addBody(L_HEEL_POS, L_TOES_POS, 0.09)
		self.leftAnkle = self.addHingeJoint(self.leftLowerLeg,
			self.leftFoot, L_ANKLE_POS, rightAxis, -0.1 * pi, 0.05 * pi)

		self.rightUpperArm = self.addBody(R_SHOULDER_POS, R_ELBOW_POS, 0.08)
		self.rightShoulder = self.addBallJoint(self.chest, self.rightUpperArm,
			R_SHOULDER_POS, norm3((-1.0, -1.0, 4.0)), (0.0, 0.0, 1.0), pi * 0.5,
			pi * 0.25, 150.0, 100.0)
		self.leftUpperArm = self.addBody(L_SHOULDER_POS, L_ELBOW_POS, 0.08)
		self.leftShoulder = self.addBallJoint(self.chest, self.leftUpperArm,
			L_SHOULDER_POS, norm3((1.0, -1.0, 4.0)), (0.0, 0.0, 1.0), pi * 0.5,
			pi * 0.25, 150.0, 100.0)

		self.rightForeArm = self.addBody(R_ELBOW_POS, R_WRIST_POS, 0.075)
		self.rightElbow = self.addHingeJoint(self.rightUpperArm,
			self.rightForeArm, R_ELBOW_POS, downAxis, 0.0, 0.6 * pi)
		self.leftForeArm = self.addBody(L_ELBOW_POS, L_WRIST_POS, 0.075)
		self.leftElbow = self.addHingeJoint(self.leftUpperArm,
			self.leftForeArm, L_ELBOW_POS, upAxis, 0.0, 0.6 * pi)

		self.rightHand = self.addBody(R_WRIST_POS, R_FINGERS_POS, 0.075)
		self.rightWrist = self.addHingeJoint(self.rightForeArm,
			self.rightHand, R_WRIST_POS, fwdAxis, -0.1 * pi, 0.2 * pi)
		self.leftHand = self.addBody(L_WRIST_POS, L_FINGERS_POS, 0.075)
		self.leftWrist = self.addHingeJoint(self.leftForeArm,
			self.leftHand, L_WRIST_POS, bkwdAxis, -0.1 * pi, 0.2 * pi)

	def addBody(self, p1, p2, radius):
		"""
		Adds a capsule body between joint positions p1 and p2 and with given
		radius to the ragdoll.
		"""

		p1 = add3(p1, self.offset)
		p2 = add3(p2, self.offset)

		# cylinder length not including endcaps, make capsules overlap by half
		#   radius at joints
		cyllen = dist3(p1, p2) - radius

		body = ode.Body(self.world)
		m = ode.Mass()
		m.setCappedCylinder(self.density, 3, radius, cyllen)
		body.setMass(m)

		# set parameters for drawing the body
		body.shape = "capsule"
		body.length = cyllen
		body.radius = radius

		# create a capsule geom for collision detection
		geom = ode.GeomCCylinder(self.space, radius, cyllen)
		geom.setBody(body)

		# define body rotation automatically from body axis
		za = norm3(sub3(p2, p1))
		if (abs(dot3(za, (1.0, 0.0, 0.0))) < 0.7): xa = (1.0, 0.0, 0.0)
		else: xa = (0.0, 1.0, 0.0)
		ya = cross(za, xa)
		xa = norm3(cross(ya, za))
		ya = cross(za, xa)
		rot = (xa[0], ya[0], za[0], xa[1], ya[1], za[1], xa[2], ya[2], za[2])

		body.setPosition(mul3(add3(p1, p2), 0.5))
		body.setRotation(rot)

		self.bodies.append(body)
		self.geoms.append(geom)
		
		self.totalMass += body.getMass().mass

		return body

	def addFixedJoint(self, body1, body2):
		joint = ode.FixedJoint(self.world)
		joint.attach(body1, body2)
		joint.setFixed()

		joint.style = "fixed"
		self.joints.append(joint)

		return joint

	def addHingeJoint(self, body1, body2, anchor, axis, loStop = -ode.Infinity,
		hiStop = ode.Infinity):

		anchor = add3(anchor, self.offset)

		joint = ode.HingeJoint(self.world)
		joint.attach(body1, body2)
		joint.setAnchor(anchor)
		joint.setAxis(axis)
		joint.setParam(ode.ParamLoStop, loStop)
		joint.setParam(ode.ParamHiStop, hiStop)

		joint.style = "hinge"
		self.joints.append(joint)

		return joint

	def addUniversalJoint(self, body1, body2, anchor, axis1, axis2,
		loStop1 = -ode.Infinity, hiStop1 = ode.Infinity,
		loStop2 = -ode.Infinity, hiStop2 = ode.Infinity):

		anchor = add3(anchor, self.offset)

		joint = ode.UniversalJoint(self.world)
		joint.attach(body1, body2)
		joint.setAnchor(anchor)
		joint.setAxis1(axis1)
		joint.setAxis2(axis2)
		joint.setParam(ode.ParamLoStop, loStop1)
		joint.setParam(ode.ParamHiStop, hiStop1)
		joint.setParam(ode.ParamLoStop2, loStop2)
		joint.setParam(ode.ParamHiStop2, hiStop2)

		joint.style = "univ"
		self.joints.append(joint)

		return joint

	def addBallJoint(self, body1, body2, anchor, baseAxis, baseTwistUp,
		flexLimit = pi, twistLimit = pi, flexForce = 0.0, twistForce = 0.0):

		anchor = add3(anchor, self.offset)

		# create the joint
		joint = ode.BallJoint(self.world)
		joint.attach(body1, body2)
		joint.setAnchor(anchor)

		# store the base orientation of the joint in the local coordinate system
		#   of the primary body (because baseAxis and baseTwistUp may not be
		#   orthogonal, the nearest vector to baseTwistUp but orthogonal to
		#   baseAxis is calculated and stored with the joint)
		joint.baseAxis = getBodyRelVec(body1, baseAxis)
		tempTwistUp = getBodyRelVec(body1, baseTwistUp)
		baseSide = norm3(cross(tempTwistUp, joint.baseAxis))
		joint.baseTwistUp = norm3(cross(joint.baseAxis, baseSide))

		# store the base twist up vector (original version) in the local
		#   coordinate system of the secondary body
		joint.baseTwistUp2 = getBodyRelVec(body2, baseTwistUp)

		# store joint rotation limits and resistive force factors
		joint.flexLimit = flexLimit
		joint.twistLimit = twistLimit
		joint.flexForce = flexForce
		joint.twistForce = twistForce

		joint.style = "ball"
		self.joints.append(joint)

		return joint

	def update(self):
		for j in self.joints:
			if j.style == "ball":
				# determine base and current attached body axes
				baseAxis = rotate3(j.getBody(0).getRotation(), j.baseAxis)
				currAxis = zaxis(j.getBody(1).getRotation())

				# get angular velocity of attached body relative to fixed body
				relAngVel = sub3(j.getBody(1).getAngularVel(),
					j.getBody(0).getAngularVel())
				twistAngVel = project3(relAngVel, currAxis)
				flexAngVel = sub3(relAngVel, twistAngVel)

				# restrict limbs rotating too far from base axis
				angle = acosdot3(currAxis, baseAxis)
				if angle > j.flexLimit:
					# add torque to push body back towards base axis
					j.getBody(1).addTorque(mul3(
						norm3(cross(currAxis, baseAxis)),
						(angle - j.flexLimit) * j.flexForce))

					# dampen flex to prevent bounceback
					j.getBody(1).addTorque(mul3(flexAngVel,
						-0.01 * j.flexForce))

				# determine the base twist up vector for the current attached
				#   body by applying the current joint flex to the fixed body's
				#   base twist up vector
				baseTwistUp = rotate3(j.getBody(0).getRotation(), j.baseTwistUp)
				base2current = calcRotMatrix(norm3(cross(baseAxis, currAxis)),
					acosdot3(baseAxis, currAxis))
				projBaseTwistUp = rotate3(base2current, baseTwistUp)

				# determine the current twist up vector from the attached body
				actualTwistUp = rotate3(j.getBody(1).getRotation(),
					j.baseTwistUp2)

				# restrict limbs twisting
				angle = acosdot3(actualTwistUp, projBaseTwistUp)
				if angle > j.twistLimit:
					# add torque to rotate body back towards base angle
					j.getBody(1).addTorque(mul3(
						norm3(cross(actualTwistUp, projBaseTwistUp)),
						(angle - j.twistLimit) * j.twistForce))

					# dampen twisting
					j.getBody(1).addTorque(mul3(twistAngVel,
						-0.01 * j.twistForce))


def createCapsule(world, space, density, length, radius):
	"""Creates a capsule body and corresponding geom."""

	# create capsule body (aligned along the z-axis so that it matches the
	#   GeomCCylinder created below, which is aligned along the z-axis by
	#   default)
	body = ode.Body(world)
	M = ode.Mass()
	M.setCappedCylinder(density, 3, radius, length)
	body.setMass(M)

	# set parameters for drawing the body
	body.shape = "capsule"
	body.length = length
	body.radius = radius

	# create a capsule geom for collision detection
	geom = ode.GeomCCylinder(space, radius, length)
	geom.setBody(body)

	return body, geom

def near_callback(args, geom1, geom2):
	"""
	Callback function for the collide() method.

	This function checks if the given geoms do collide and creates contact
	joints if they do.
	"""

	if (ode.areConnected(geom1.getBody(), geom2.getBody())):
		return

	# check if the objects collide
	contacts = ode.collide(geom1, geom2)

	# create contact joints
	world, contactgroup = args
	for c in contacts:
		c.setBounce(0.2)
		c.setMu(500) # 0-5 = very slippery, 50-500 = normal, 5000 = very sticky
		j = ode.ContactJoint(world, contactgroup, c)
		j.attach(geom1.getBody(), geom2.getBody())

def prepare_GL():
	"""Setup basic OpenGL rendering with smooth shading and a single light."""

	glClearColor(0.8, 0.8, 0.9, 0.0)
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST)
	glEnable(GL_LIGHTING)
	glEnable(GL_NORMALIZE)
	glShadeModel(GL_SMOOTH)

	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective (45.0, 1.3333, 0.2, 20.0)

	glViewport(0, 0, 640, 480)

	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()

	glLightfv(GL_LIGHT0,GL_POSITION,[0, 0, 1, 0])
	glLightfv(GL_LIGHT0,GL_DIFFUSE,[1, 1, 1, 1])
	glLightfv(GL_LIGHT0,GL_SPECULAR,[1, 1, 1, 1])
	glEnable(GL_LIGHT0)

	glEnable(GL_COLOR_MATERIAL)
	glColor3f(0.8, 0.8, 0.8)

	gluLookAt(1.5, 4.0, 3.0, 0.5, 1.0, 0.0, 0.0, 1.0, 0.0)


# polygon resolution for capsule bodies
CAPSULE_SLICES = 16
CAPSULE_STACKS = 12

def draw_body(body):
	"""Draw an ODE body."""

	rot = makeOpenGLMatrix(body.getRotation(), body.getPosition())
	glPushMatrix()
	glMultMatrixd(rot)
	if body.shape == "capsule":
		cylHalfHeight = body.length / 2.0
		glBegin(GL_QUAD_STRIP)
		for i in range(0, CAPSULE_SLICES + 1):
			angle = i / float(CAPSULE_SLICES) * 2.0 * pi
			ca = cos(angle)
			sa = sin(angle)
			glNormal3f(ca, sa, 0)
			glVertex3f(body.radius * ca, body.radius * sa, cylHalfHeight)
			glVertex3f(body.radius * ca, body.radius * sa, -cylHalfHeight)
		glEnd()
		glTranslated(0, 0, cylHalfHeight)
		glutSolidSphere(body.radius, CAPSULE_SLICES, CAPSULE_STACKS)
		glTranslated(0, 0, -2.0 * cylHalfHeight)
		glutSolidSphere(body.radius, CAPSULE_SLICES, CAPSULE_STACKS)
	glPopMatrix()


def onKey(c, x, y):
	"""GLUT keyboard callback."""

	global SloMo, Paused

	# set simulation speed
	if c >= '0' and c <= '9':
		SloMo = 4 * int(c) + 1
	# pause/unpause simulation
	elif c == 'p' or c == 'P':
		Paused = not Paused
	# quit
	elif c == 'q' or c == 'Q':
		sys.exit(0)


def onDraw():
	"""GLUT render callback."""

	prepare_GL()

	for b in bodies:
		draw_body(b)
	for b in ragdoll.bodies:
		draw_body(b)

	glutSwapBuffers()


def onIdle():
	"""GLUT idle processing callback, performs ODE simulation step."""

	global Paused, lasttime, numiter

	if Paused:
		return

	t = dt - (time.time() - lasttime)
	if (t > 0):
		time.sleep(t)

	glutPostRedisplay()

	for i in range(stepsPerFrame):
		# Detect collisions and create contact joints
		space.collide((world, contactgroup), near_callback)

		# Simulation step (with slo motion)
		world.step(dt / stepsPerFrame / SloMo)

		numiter += 1

		# apply internal ragdoll forces
		ragdoll.update()

		# Remove all contact joints
		contactgroup.empty()

	lasttime = time.time()

# initialize GLUT
glutInit([])
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE)

# create the program window
x = 0
y = 0
width = 640
height = 480
glutInitWindowPosition(x, y);
glutInitWindowSize(width, height);
glutCreateWindow("PyODE Ragdoll Simulation")

# create an ODE world object
world = ode.World()
world.setGravity((0.0, -9.81, 0.0))
world.setERP(0.1)
world.setCFM(1E-4)

# create an ODE space object
space = ode.Space()

# create a plane geom to simulate a floor
floor = ode.GeomPlane(space, (0, 1, 0), 0)

# create a list to store any ODE bodies which are not part of the ragdoll (this
#   is needed to avoid Python garbage collecting these bodies)
bodies = []

# create a joint group for the contact joints generated during collisions
#   between two bodies collide
contactgroup = ode.JointGroup()

# set the initial simulation loop parameters
fps = 60
dt = 1.0 / fps
stepsPerFrame = 2
SloMo = 1
Paused = False
lasttime = time.time()
numiter = 0

# create the ragdoll
ragdoll = RagDoll(world, space, 500, (0.0, 0.9, 0.0))
print "total mass is %.1f kg (%.1f lbs)" % (ragdoll.totalMass,
	ragdoll.totalMass * 2.2)

# create an obstacle
obstacle, obsgeom = createCapsule(world, space, 1000, 0.05, 0.15)
pos = (random.uniform(-0.3, 0.3), 0.2, random.uniform(-0.15, 0.2))
#pos = (0.27396178783269359, 0.20000000000000001, 0.17531818795388002)
obstacle.setPosition(pos)
obstacle.setRotation(rightRot)
bodies.append(obstacle)
print "obstacle created at %s" % (str(pos))

# set GLUT callbacks
glutKeyboardFunc(onKey)
glutDisplayFunc(onDraw)
glutIdleFunc(onIdle)

# enter the GLUT event loop
glutMainLoop()

