Ok that one is a cool one: Rigid body for the gang.
I’ve been studying ODE, blODEd script, pyODE and did some tutorials in python & blender and came up with the following result.
You need pyODE and a full install of python.
A simple way to get rigidbody simulation.
Concept is simple the gasp. Reading the ODE manual give instant hint about a way to easily get rigid body simulation.
The first things to know:
a simulation engine have the following structure:
A typical simulation will proceed like this:
1. Create a dynamics world.
2. Create bodies in the dynamics world.
3. Set the state (position etc) of all bodies.
4. Create joints in the dynamics world.
5. Attach the joints to the bodies.
6. Set the parameters of all joints.
7. Create a collision world and collision geometry objects, as necessary.
8. Create a joint group to hold the contact joints.
9. Loop:
1. Apply forces to the bodies as necessary.
2. Adjust the joint parameters as necessary.
3. Call collision detection.
4. Create a contact joint for every collision point, and put it in the contact joint group.
5. Take a simulation step.
6. Remove all joints in the contact joint group.
10. Destroy the dynamics and collision worlds.
And here is the basic concept of ODE:
-You have a world, which define primary information such as gravity, error correction and in generale way contain all bodies and joins. object from different world cannot collide.
-Then in a world the is a space. a Space is there just to optimise simulation speed, it contain other bodies and make group of bodies that may probably not always need to be calculated together. object from multiple space can collide, but if they don’t. you have half less simulation to do.
-Then you create bodies. A body is a point in the world, containting position, scale, rotation information, the mass and the center of mass. a body could represent onlyan object in blender and not the data it containt.
-After you have bodies you create joints(or constraints), quoting the ODE manual:
In real life a joint is something like a hinge, that is used to connect two objects. In ODE a joint is very similar: It is a relationship that is enforced between two bodies so that they can only have certain positions and orientations relative to each other.
Think about a necklass. every little ball is connected to the next one in a way that it stay on your shoulder.
-After that you create geometries (or geoms) that define what a body look like and link the body to the geom. you can have a plane, cube, sphere, caped tube, and a geoms made of triangular face(like a mesh).
-Then when all parameter for contact is made, you enter the simulation loop, which will turn 24 time a seconde (or each frame if you want ) Some system may turn 2 time by frame for better result. at each frame a snapshot of all bodies’s positions is taken and returned to the system (here blender). at each turn the collision routine look at all pair of bodies and if an overlapping of bodies is detected in a space, the pair of objects is sent to a collision callback, which then take a deep look at it and decide what to do with this pair of bodies, return the result(the move and rotation for the next step… blabla) as a joint (constraint) that will make them consider each other and stop them going further(the ball will change direction when it hit a wall). The joint just created was part of a joint group especially for collision. What it do is after each loop, this group is deleted, so next time it has to do it all over again, as the collision append only once(if not, the ball will stick to wall, lol).
Hope it make sens a bit.
Here is the result:
tutorial 2:
Click here to watch Join_demo0001_0120
# pyODE example 2: Connecting bodies with joints
import Blender
from Blender import BGL, Draw, Object, Ipo
from Blender.BGL import *
from Blender.Draw import *
import ode
# Create an Ipo Object
def makeipo(name):
print "Creating new IPO "+name
ipo = Ipo.New('Object', name)
Locx = ipo.addCurve('LocX')
Locy = ipo.addCurve('LocY')
Locz = ipo.addCurve('LocZ')
Rotx = ipo.addCurve('RotX')
Roty = ipo.addCurve('RotY')
Rotz = ipo.addCurve('RotZ')
for c in [Locx,Locy,Locz,Rotx,Roty,Rotz]:
c.setInterpolation('Linear')
c.setExtrapolation('Constant')
return ipo
# Bakes the Blender settings to the IPO
def bakeipo(ipo, frame, obj):
Locx = ipo.getCurve('LocX')
Locy = ipo.getCurve('LocY')
Locz = ipo.getCurve('LocZ')
Rotx = ipo.getCurve('RotX')
Roty = ipo.getCurve('RotY')
Rotz = ipo.getCurve('RotZ')
Locx.addBezier((frame,obj.LocX))
Locy.addBezier((frame,obj.LocY))
Locz.addBezier((frame,obj.LocZ))
Rotx.addBezier((frame,obj.RotX*5.729))
Roty.addBezier((frame,obj.RotY*5.729))
Rotz.addBezier((frame,obj.RotZ*5.729))
def set_rot(obj, R):
m = obj.getMatrix()
m[0][0] = R[0]
m[1][0] = R[1]
m[2][0] = R[2]
m[0][1] = R[3]
m[1][1] = R[4]
m[2][1] = R[5]
m[0][2] = R[6]
m[1][2] = R[7]
m[2][2] = R[8]
obj.setMatrix(m)
# Create a world object
world = ode.World()
world.setGravity((0,0,-9.81))
# Create 3 bodies
body1 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body1.setMass(M)
body1.setPosition((1,2,0))
body2 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body2.setMass(M)
body2.setPosition((2,2,0))
body3 = ode.Body(world)
M = ode.Mass()
M.setSphere(2500, 0.05)
body3.setMass(M)
body3.setPosition((3,2,0))
# Connect body1 with the static environment
j1 = ode.BallJoint(world)
j1.attach(body1, ode.environment)
j1.setAnchor( (0,2,0) )
# Connect body2 with body1
j2 = ode.BallJoint(world)
j2.attach(body2, body1)
j2.setAnchor( (1,2,0) )
# Connect body3 with body1
j3 = ode.BallJoint(world)
j3.attach(body3, body2)
j3.setAnchor( (2,2,0) )
#Get Blender Object
obj1 = Object.Get("Cube")
obj2 = Object.Get("Cube.001")
obj3 = Object.Get("Cube.002")
ipo1 = makeipo("Cube")
ipo2 = makeipo("Cube.001")
ipo3 = makeipo("Cube.002")
# Simulation loop...
fps = 24
dt = 1.0/fps
frame = 1 #first frame
while frame < 150: #tot of frame to do
# Draw the two bodies
x1,y1,z1 = body1.getPosition()
x2,y2,z2 = body2.getPosition()
x3,y3,z3 = body3.getPosition()
obj1.setLocation(x1,y1,z1)
obj2.setLocation(x2,y2,z2)
obj3.setLocation(x3,y3,z3)
set_rot(obj1,body1.getRotation())
set_rot(obj2,body2.getRotation())
set_rot(obj3,body3.getRotation())
bakeipo(ipo1,frame,obj1)
bakeipo(ipo2,frame,obj2)
bakeipo(ipo3,frame,obj3)
# Next simulation step
world.step(dt)
Blender.Set("curframe",Blender.Get("curframe")+1)
frame += 1
obj1.setIpo(ipo1)
obj2.setIpo(ipo2)
obj3.setIpo(ipo3)
print "done!"
Tutorial 3:
Click here to watch Collision_demo0001_0600
# pyODE example 3: Collision detection
import Blender
from Blender import Ipo, Scene, Mathutils, Window
from Blender.Object import *
from Blender.Mathutils import *
import ode
# create_box
def create_box(world, space, density, lx, ly, lz):
"""Create a box body and its corresponding geom."""
global objlist
# Create body
body = ode.Body(world)
M = ode.Mass()
M.setBox(density, lx, ly, lz)
body.setMass(M)
# Set parameters for drawing the body
body.shape = "box"
body.boxsize = (lx, ly, lz)
zpos = Rand(10,15)
xpos = Rand(-2,2)
ypos = Rand(-2,2)
body.setPosition((xpos,ypos,zpos))
# Create a box geom for collision detection
geom = ode.GeomBox(space, lengths=body.boxsize)
geom.setBody(body)
# Create Blender object
obj = New("Mesh","box")
obj.link(Get("box").getData())
obj.setLocation(xpos,ypos,zpos)
objlist.append(obj)
ipo = makeipo("box_move")
ipolist.append(ipo)
obj.setIpo(ipo)
sc = Scene.getCurrent()
sc.link(obj)
return body
# drop_object
def drop_object():
"""Drop an object into the scene."""
global bodies, counter, objcount
body = create_box(world, space, 3000, 1.0,0.5,0.5)
bodies.append(body)
counter=0
objcount+=1
# Collision callback
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.
"""
# Check if the objects do collide
contacts = ode.collide(geom1, geom2)
# Create contact joints
world,contactgroup = args
for c in contacts:
c.setBounce(0.1)
c.setMu(5000)
j = ode.ContactJoint(world, contactgroup, c)
j.attach(geom1.getBody(), geom2.getBody())
# Create an Ipo Object
def makeipo(name):
print "Creating new IPO "+name
ipo = Ipo.New('Object', name)
Locx = ipo.addCurve('LocX')
Locy = ipo.addCurve('LocY')
Locz = ipo.addCurve('LocZ')
Rotx = ipo.addCurve('RotX')
Roty = ipo.addCurve('RotY')
Rotz = ipo.addCurve('RotZ')
for c in [Locx,Locy,Locz,Rotx,Roty,Rotz]:
c.setInterpolation('Linear')
c.setExtrapolation('Constant')
return ipo
# Bakes the Blender settings to the IPO
def bakeipo(ipo, frame, obj):
Locx = ipo.getCurve('LocX')
Locy = ipo.getCurve('LocY')
Locz = ipo.getCurve('LocZ')
Rotx = ipo.getCurve('RotX')
Roty = ipo.getCurve('RotY')
Rotz = ipo.getCurve('RotZ')
Locx.addBezier((frame,obj.LocX))
Locy.addBezier((frame,obj.LocY))
Locz.addBezier((frame,obj.LocZ))
Rotx.addBezier((frame,obj.RotX*5.729))
Roty.addBezier((frame,obj.RotY*5.729))
Rotz.addBezier((frame,obj.RotZ*5.729))
def draw_body(body,obj,ipo):
global frame
x,y,z = body.getPosition()
obj.setLocation(x,y,z)
set_rot(obj,body.getRotation())
bakeipo(ipo,frame,obj)
def set_rot(obj, R):
m = obj.getMatrix()
m[0][0] = R[0]
m[1][0] = R[1]
m[2][0] = R[2]
m[0][1] = R[3]
m[1][1] = R[4]
m[2][1] = R[5]
m[0][2] = R[6]
m[1][2] = R[7]
m[2][2] = R[8]
obj.setMatrix(m)
######################################################################
# Create a world object
world = ode.World()
world.setGravity( (0,0,-9.81) )
world.setERP(0.8)
world.setCFM(1E-5)
# Create a space object
space = ode.Space()
# Create a plane geom which prevent the objects from falling forever
floor = ode.GeomPlane(space, (0,0,1), 0)
# A list with ODE bodies
bodies = []
objlist = []
ipolist = []
# A joint group for the contact joints that are generated whenever
# two bodies collide
contactgroup = ode.JointGroup()
# Some variables used inside the simulation loop
fps = 24
dt = 1.0/fps
state = 0
counter = 0
objcount = 0
frame = 0.0
totframe = 700
while frame < totframe:
counter+=1
frame += 1
Window.DrawProgressBar(frame/totframe,"Simulation...")
Blender.Set("curframe",frame)
if counter==5:
drop_object()
# Draw the scene
for b in range(len(bodies)):
draw_body(bodies[b],objlist[b],ipolist[b])
# Simulate
n = 2
for i in range(n):
# Detect collisions and create contact joints
space.collide((world,contactgroup), near_callback)
# Simulation step
world.step(dt/n)
# Remove all contact joints
contactgroup.empty()
Window.DrawProgressBar(1,"Done")
A personnal test using lego:
Click here to watch playblast_legowall2
Click here to watch Render_legowall0001_0200
import Blender
from Blender import BGL, Draw, Object, Ipo, Scene, Window
from Blender.BGL import *
from Blender.Draw import *
import ode
# Create an Ipo Object
def makeipo(name):
print "Creating new IPO "+name
ipo = Ipo.New('Object', name)
Locx = ipo.addCurve('LocX')
Locy = ipo.addCurve('LocY')
Locz = ipo.addCurve('LocZ')
Rotx = ipo.addCurve('RotX')
Roty = ipo.addCurve('RotY')
Rotz = ipo.addCurve('RotZ')
for c in [Locx,Locy,Locz,Rotx,Roty,Rotz]:
c.setInterpolation('Linear')
c.setExtrapolation('Constant')
return ipo
# Bakes the Blender settings to the IPO
def bakeipo(ipo, frame, obj):
Locx = ipo.getCurve('LocX')
Locy = ipo.getCurve('LocY')
Locz = ipo.getCurve('LocZ')
Rotx = ipo.getCurve('RotX')
Roty = ipo.getCurve('RotY')
Rotz = ipo.getCurve('RotZ')
Locx.addBezier((frame,obj.LocX))
Locy.addBezier((frame,obj.LocY))
Locz.addBezier((frame,obj.LocZ))
Rotx.addBezier((frame,obj.RotX*5.729))
Roty.addBezier((frame,obj.RotY*5.729))
Rotz.addBezier((frame,obj.RotZ*5.729))
def set_rot(obj, R):
m = obj.getMatrix()
m[0][0] = R[0]
m[1][0] = R[1]
m[2][0] = R[2]
m[0][1] = R[3]
m[1][1] = R[4]
m[2][1] = R[5]
m[0][2] = R[6]
m[1][2] = R[7]
m[2][2] = R[8]
obj.setMatrix(m)
def draw_body(body,obj,ipo):
global frame
x,y,z = body.getPosition()
obj.setLocation(x,y,z)
set_rot(obj,body.getRotation())
bakeipo(ipo,frame,obj)
# Collision callback
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.
"""
# Check if the objects do collide
contacts = ode.collide(geom1, geom2)
# Create contact joints
world,contactgroup = args
for c in contacts:
c.setBounce(.5)
c.setMu(7000)
j = ode.ContactJoint(world, contactgroup, c)
j.attach(geom1.getBody(), geom2.getBody())
# Create a world object
world = ode.World()
world.setGravity((0,0,-9.81))
world.setERP(0.2) #error correction
world.setCFM(1E-10) #softness of collision
# Create a wall
lbody = []
lobj = []
lipo = []
contactgroup = ode.JointGroup()
# Create a space object
space = ode.Space()
# Create a plane geom which prevent the objects from falling forever
floor = ode.GeomPlane(space, (0,0,1), 0)
ori = Object.Get("Lego") #lego is 3,1,1 long as x,y,z)
mesh = ori.getData()
sc = Scene.GetCurrent()
a = 1.5
for z in range(20):
for x in range(5):
cur = Object.New("Mesh","wall")
cur.link(mesh)
cur.setLocation(x*3+a,1,z+.5)
sc.link(cur)
lobj.append(cur)
if a == 1.5:
a = 3
else:
a = 1.5
for obj in lobj:
body = ode.Body(world)
M = ode.Mass()
M.setBox(5000,3,1,1)
body.setMass(M)
body.setPosition(obj.getLocation())
geom = ode.GeomBox(space,(3,1,1))
geom.setBody(body)
ipo = makeipo(obj.name)
obj.setIpo(ipo)
lipo.append(ipo)
lbody.append(body)
body.disable() #wait for the cue
bball = ode.Body(world)
obj2 = Object.Get("Ball")
M = ode.Mass()
M.setSphere(700,5)
bball.setMass(M)
bball.setPosition(obj2.getLocation())
geom2 = ode.GeomSphere(space,5)
geom2.setBody(bball)
bball.setLinearVel((0,20,0)) #give some speed.
lobj.append(obj2)
lbody.append(bball)
ipo = makeipo(obj2.name)
lipo.append(ipo)
obj2.setIpo(ipo)
# Simulation loop...
fps = 24
dt = 1.0/fps
frame = 1 #first frame
totframe = 300
while frame < totframe: #tot of frame to do
Window.DrawProgressBar(frame/totframe,"Simulation...")
Blender.Set("curframe",frame)
for i in range(2):
space.collide((world,contactgroup), near_callback) #colision
for b in range(len(lbody)):
draw_body(lbody[b],lobj[b],lipo[b])
world.quickStep(dt) #shift time
contactgroup.empty() #empty mem
Blender.Set("curframe",Blender.Get("curframe")+1)
frame += 1
ode.CloseODE()
Window.DrawProgressBar(1,"Done!")
Here is the .blend. You just need to switch screen with ctrl-left or right. And then start the script with alt-p to generate the goodies.
(edit) added stuff