Since this answer was originally posted, the Blender software package has undergone significant revision causing the Python scripts to break. I have modified the scripts so that they should work from v2.79b to v2.93LTS.
Updated Blender scripts
import bpy
from math import pi
# Read current Blender version
version = bpy.app.version
for o in bpy.data.objects:
if version < (2, 80, 0):
if o.type == 'MESH' or o.type == 'EMPTY':
o.select = True
else:
o.select = False
else:
o.select_set(o.type == 'EMPTY' or o.type == 'MESH')
# Delete all objects in the scene
bpy.ops.object.delete()
# Add the floor
if version < (2, 80, 0):
bpy.ops.mesh.primitive_cube_add(radius=5, location=(0, 0, 0))
else:
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 0))
bpy.ops.transform.resize(value=(1, 1, 0.1))
bpy.ops.rigidbody.objects_add(type='PASSIVE')
boxObj = bpy.context.active_object
boxObj.rigid_body.collision_shape = "BOX"
boxObj.name = "Ground"
# Add the Coin
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=0.1, location=(0, 0, 3))
bpy.ops.rigidbody.objects_add(type='ACTIVE')
boxObj = bpy.context.active_object
boxObj.rigid_body.collision_shape = "CYLINDER"
bpy.context.object.rigid_body.friction = 0.25
bpy.context.object.rigid_body.restitution = 0.75
boxObj.name = "Coin"
# Set reference to the coin
coin = bpy.data.objects["Coin"]
# Set a reference to the scene
sce = bpy.context.scene
# Set first frame
sce.frame_set(1)
# Set Keyframes
coin.keyframe_insert(data_path="location")
coin.keyframe_insert(data_path="rotation_euler")
bpy.context.object.rigid_body.kinematic = True
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Advance two frames and add translational and rotational motion
sce.frame_set(34)
# Translate up a little
coin.location.z = 3.45
# Rotate coin predominantly around the x-axis
coin.rotation_euler.x = 1
coin.rotation_euler.y = 0.1
coin.rotation_euler.z = 0.1
# Set Keyframes
coin.keyframe_insert(data_path="location")
coin.keyframe_insert(data_path="rotation_euler")
bpy.context.object.rigid_body.kinematic = False
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Set frame to the end
sce.frame_set(250)
# Bake rigid body simulation
override = {'scene': bpy.context.scene,
'point_cache': bpy.context.scene.rigidbody_world.point_cache}
# bake to current frame
bpy.ops.ptcache.bake(override, bake=False)
# Get transformations
tr = coin.matrix_world.translation
eu = coin.matrix_world.to_euler()
print(" X Y Z RX RY RZ")
print(tr.x, tr.y, tr.z, eu.x, eu.y, eu.z)
if eu.x > pi / 2.:
print("Coin is heads")
else:
print("Coin is tails")
createCoinFlip[z_, rx_, ry_, rz_, friction_, restitution_] :=
StringTemplate["import bpy
from math import pi
# Read current Blender version
version = bpy.app.version
for o in bpy.data.objects:
if version < (2, 80, 0):
if o.type == 'MESH' or o.type == 'EMPTY':
o.select = True
else:
o.select = False
else:
o.select_set(o.type == 'EMPTY' or o.type == 'MESH')
# Delete all objects in the scene
bpy.ops.object.delete()
# Add the floor
if version < (2, 80, 0):
bpy.ops.mesh.primitive_cube_add(radius=5, location=(0, 0, 0))
else:
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 0))
bpy.ops.transform.resize(value=(1, 1, 0.1))
bpy.ops.rigidbody.objects_add(type='PASSIVE')
boxObj = bpy.context.active_object
boxObj.rigid_body.collision_shape = \"BOX\"
boxObj.name = \"Ground\"
# Add the Coin
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=0.1, \
location=(0, 0, 3))
bpy.ops.rigidbody.objects_add(type='ACTIVE')
boxObj = bpy.context.active_object
boxObj.rigid_body.collision_shape = \"CYLINDER\"
bpy.context.object.rigid_body.friction = `friction`
bpy.context.object.rigid_body.restitution = `restitution`
boxObj.name = \"Coin\"
# Set reference to the coin
coin = bpy.data.objects[\"Coin\"]
# Set a reference to the scene
sce = bpy.context.scene
# Set first frame
sce.frame_set(1)
# Set Keyframes
coin.keyframe_insert(data_path=\"location\")
coin.keyframe_insert(data_path=\"rotation_euler\")
bpy.context.object.rigid_body.kinematic = True
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Advance two frames and add translational and rotational motion
sce.frame_set(34)
# Translate up a little
coin.location.z = `z`
# Rotate coin predominantly around the x-axis
coin.rotation_euler.x = `rx`
coin.rotation_euler.y = `ry`
coin.rotation_euler.z = `rz`
# Set Keyframes
coin.keyframe_insert(data_path=\"location\")
coin.keyframe_insert(data_path=\"rotation_euler\")
bpy.context.object.rigid_body.kinematic = False
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Set frame to the end
sce.frame_set(250)
# Bake rigid body simulation
override = {'scene': bpy.context.scene,
'point_cache': \
bpy.context.scene.rigidbody_world.point_cache}
# bake to current frame
bpy.ops.ptcache.bake(override, bake=False)
# Get transformations
tr = coin.matrix_world.translation
eu = coin.matrix_world.to_euler()
print(\"\
X Y Z RX R\
Y RZ\")
print(tr.x, tr.y, tr.z, eu.x, eu.y, eu.z)
if eu.x > pi / 2.:
print(\"Coin flip result is heads\")
else:
print(\"Coin flip result is tails\")
"][<|"z" -> z, "rx" -> rx, "ry" -> ry, "rz" -> rz,
"friction" -> friction, "restitution" -> restitution|>]
What follows is a simple example of how to conduct additional post processing-processing on a Blender simulation in Mathematica.
createCoinFlipTransform[z_, rx_, ry_, rz_, friction_, restitution_] :=
StringTemplate["import bpy
from math import pi
# Read current Blender version
version = bpy.app.version
for o in bpy.data.objects:
if version < (2, 80, 0):
if o.type == 'MESH' or o.type == 'EMPTY':
o.select = True
else:
o.select = False
else:
o.select_set(o.type == 'EMPTY' or o.type == 'MESH')
# Delete all objects in the scene
bpy.ops.object.delete()
# Add the floor
if version < (2, 80, 0):
bpy.ops.mesh.primitive_cube_add(radius=5, location=(0, 0, 0))
else:
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 0))
bpy.ops.transform.resize(value=(1, 1, 0.1))
bpy.ops.rigidbody.objects_add(type='PASSIVE')
boxObj = bpy.context.active_object
boxObj.rigid_body.collision_shape = \"BOX\"
boxObj.name = \"Ground\"
# Add the Coin
bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=0.1, \
location=(0, 0, 3))
bpy.ops.rigidbody.objects_add(type='ACTIVE')
cylObj = bpy.context.active_object
cylObj.rigid_body.collision_shape = \"CYLINDER\"
bpy.context.object.rigid_body.friction = `friction`
bpy.context.object.rigid_body.restitution = `restitution`
cylObj.name = \"Coin\"
# Set reference to the coin
coin = bpy.data.objects[\"Coin\"]
# Set a reference to the scene
sce = bpy.context.scene
# Set first frame
sce.frame_set(1)
# Set Keyframes
coin.keyframe_insert(data_path=\"location\")
coin.keyframe_insert(data_path=\"rotation_euler\")
bpy.context.object.rigid_body.kinematic = True
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Advance two frames and add translational and rotational motion
sce.frame_set(34)
# Translate up a little
coin.location.z = `z`
# Rotate coin predominantly around the x-axis
coin.rotation_euler.x = `rx`
coin.rotation_euler.y = `ry`
coin.rotation_euler.z = `rz`
# Set Keyframes
coin.keyframe_insert(data_path=\"location\")
coin.keyframe_insert(data_path=\"rotation_euler\")
bpy.context.object.rigid_body.kinematic = False
bpy.context.object.keyframe_insert('rigid_body.kinematic')
# Set frame to the end
sce.frame_set(250)
# Bake rigid body simulation
override = {'scene': bpy.context.scene,
'point_cache': \
bpy.context.scene.rigidbody_world.point_cache}
# bake to current frame
bpy.ops.ptcache.bake(override, bake=False)
# Get transformations
tr = coin.matrix_world.translation
eu = coin.matrix_world.to_euler()
for i in range(250):
sce.frame_set(i)
tr = coin.matrix_world.translation
eu = coin.matrix_world.to_euler()
print(\"PosRot\",tr.x, tr.y, tr.z, eu.x , eu.y , eu.z )
"][<|"z" -> z, "rx" -> rx, "ry" -> ry, "rz" -> rz,
"friction" -> friction, "restitution" -> restitution|>]

