Here's the Python code for my PyODE Example 3. In order to run the script below, you will first need to install PyODE, and cgkit for the Renderman bindings.
#!/usr/bin/python
#
# python_render_ex3
import sys, os, random
from math import *
from cgkit.cgtypes import *
from cgkit.riutil import *
import ode
def draw_body(body):
"""
Draw the body with Renderman primitives.
"""
(x, y, z) = body.getPosition()
R = body.getRotation()
# Construct the transformation matrix
T = mat4()
T[0,0] = R[0]
T[0,1] = R[1]
T[0,2] = R[2]
T[1,0] = R[3]
T[1,1] = R[4]
T[1,2] = R[5]
T[2,0] = R[6]
T[2,1] = R[7]
T[2,2] = R[8]
T[3] = (x ,y ,z , 1.0)
RiTransformBegin()
# Apply rotation & translation
RiTransform(T.toList())
(sx, sy, sz) = body.boxsize
# Draw the body
RiScale(sx, sy, sz)
RiAttributeBegin()
RiColor([ 1.0, 0.6, 0.0 ])
RiPolygon(P=[ -0.5,-0.5,-0.5, -0.5,-0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5,-0.5 ]) # left side
RiPolygon(P=[ 0.5, 0.5,-0.5, 0.5, 0.5, 0.5, 0.5,-0.5, 0.5, 0.5,-0.5,-0.5 ]) # right side
RiPolygon(P=[ -0.5, 0.5,-0.5, 0.5, 0.5,-0.5, 0.5,-0.5,-0.5, -0.5,-0.5,-0.5 ]) # front side
RiPolygon(P=[ -0.5,-0.5, 0.5, 0.5,-0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5 ]) # back side
RiPolygon(P=[ -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,-0.5, -0.5, 0.5,-0.5 ]) # top
RiPolygon(P=[ -0.5,-0.5,-0.5, 0.5,-0.5,-0.5, 0.5,-0.5, 0.5, -0.5,-0.5, 0.5 ]) # bottom
RiAttributeEnd()
RiTransformEnd()
def create_box(density, lx, ly, lz):
"""
Create a box body and its corresponding geom.
"""
# 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)
# Create a box geom for collision detection
geom = ode.GeomBox(space, lengths=body.boxsize)
geom.setBody(body)
return(body, geom)
def drop_object():
"""
Drop an object into the space.
"""
global bodies, geom
# Create the box
(body, geom) = create_box(1000, 1.0, 0.2, 0.2)
# Set a random position
body.setPosition( (random.gauss(0, 0.1), 3.0, random.gauss(0, 0.1)) )
# Set a random rotation
m = mat3().rotation(objcount*pi/2 + random.gauss(-pi/4, pi/4), (0, 1, 0))
body.setRotation(m.toList())
# Append the body and geom to a list
bodies.append(body)
geoms.append(geom)
# 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.2)
c.setMu(5000)
j = ode.ContactJoint(world, contactgroup, c)
j.attach(geom1.getBody(), geom2.getBody())
################################################################################
# Set the output rib file
renderer = "pyode_ex3.rib"
# Create the header
RiBegin(renderer)
RiuDefaultHeader()
# Set the width & height for each frame
width = 640
height = 480
# Set the number of frames, iterations/frames ratio and number of iterations
NFRAMES = 300
R = 4
N = (NFRAMES - 1)*R + 1
#N = 1201
#NFRAMES = 4
#R = (N - 1)/(NFRAMES - 1)
# Set the frame rate and iteration time step
fps = 25
dt = 1.0/fps/R
# Initialize the random number generator
random.seed(0)
# Create a world object
world = ode.World()
world.setGravity( (0, -9.81, 0) )
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, 1, 0), 0)
# A list with ODE bodies
bodies = []
# The geoms for each of the bodies
geoms = []
# A joint group for the contact joints that are generated whenever two bodies collide
contactgroup = ode.JointGroup()
# Initialize the simulation
frame = 0
state = 0
counter = 0
objcount = 0
# Run the simulation
for n in range(N):
# Detect collisions and create contact joints
space.collide((world, contactgroup), near_callback)
# Simulation step
world.step(dt)
# Remove all contact joints
contactgroup.empty()
counter += 1
if (state == 0):
if (counter == 10*R):
# Drop an object
drop_object()
objcount += 1
counter = 0
if objcount == 20:
state = 1
counter = 0
if (n % R == 0):
# Create a frame
frame += 1
filename = "pyode_ex3.%03d.tif" % frame
RiFrameBegin(frame)
# Set the projection
RiProjection(RI_PERSPECTIVE, fov=22)
# Create a view transformation and apply
V = mat4(1).lookAt(2.0*vec3(-2, 3, -4), (0, 0.5, 0), up=(0, 1, 0))
V = V.inverse()
RiTransform(V.toList())
# Set the output file and frame format
RiDisplay(filename, RI_FILE, RI_RGBA)
RiFormat(width, height, 1.0)
# Apply sampling
RiPixelSamples(4,4)
RiShadingInterpolation(RI_SMOOTH)
# Begin the world
RiWorldBegin()
# Make objects visible to rays
RiDeclare("diffuse", "integer")
RiAttribute("visibility", "diffuse", 1)
RiDeclare("bias", "float")
RiAttribute("trace", "bias", 0.005)
# Don't interpolate irradiance
RiDeclare("maxerror", "float")
RiAttribute("irradiance", "maxerror", 0.0)
# Apply global illumination
RiDeclare("samples", "integer")
RiSurface("occsurf2", "samples", 256)
# Create a white ground plane
RiAttributeBegin()
RiColor([ 0.9, 0.9, 0.9 ])
RiPolygon(P=[ -20, 0, 20, 20, 0, 20, 20, 0, -20, -20, 0, -20 ])
RiAttributeEnd()
# Draw the bodies
for b in bodies:
draw_body(b)
# Complete the world & frame
RiWorldEnd()
RiFrameEnd()
# Create the termination line
RiEnd()
Here's the command pipeline to generate an mp4 video:
First, create the rib file with the pyode_render_ex3.py script:
python pyode_render_ex3.py
Compile the shader:
sdrc occsurf2.sl
Then render with Pixie:
rndr -p pyode_ex3.rib
Crop the the tif files and convert to png using convert_frames.pl:
perl convert_frames.pl
Assemble the frames into an uncompressed avi with VirtualDubMod (Windows) and these processing settings:
VirtualDubMod /spyode_ex3.vcf /x
Compress the avi with x264 and this AVISynth file:
x264 --crf 16 -o pyode_ex3.mp4 pyode_ex3.avs