I'm trying to use blender to test the probabilities associated with different faces of non-cubic dice (some of you may even know why). I've already written a python script that randomly sets the objects position, rotation, initial linear velocity and initial angular velocity, then bakes the physics and jumps to a frame where the shape is likely in a settled position. The problem is that the rotation data at this frame seems to be the same as the frame right before the physics engine took over. It looks like the physics engine doesn't operate directly on object transformations. Is there a way to get this rotation information from python? Right now I can't even seem to find it in the UI.
To be able to read out transformation values from rigid body physics simulations, those simulations need to be baked to keyframes. Baking the Physics from the Physics tab is not enough. Find the Bake to Keyframes button here:
From Python you can access the function via this operator:
You will be asked to enter a framerange when using the interactive operator:
Again, in Python you can use these keyword arguments to achieve the same thing:
bpy.ops.rigidbody.bake_to_keyframes(frame_start=1, frame_end=250, step=1)
Before baking, you should set the rotation mode to either Quarternions or Euler, depending on what you need to read out later. After baking, you can see the generated keyframes in the Timeline:
Here are two comparison screenshots after the baking process, the first one is at frame 1, the other one at frame 250:
There's a fairly simple way to get the transformations of an object under a physics simulation without baking (which also applies to other situations such as parent-child relationships, constraints, etc).
To do this you simply need to access the object's world matrix property. Here are some examples you can test through the interactive python console:
# Accessing through the location property will give the initial value before the simulation starts >>> C.object.location Vector((0.0, 0.0, 14.47101879119873)) # The world matrix though gives the real, simulated location >>> C.object.matrix_world.to_translation() Vector((0.5996798276901245, 1.5040934085845947, 1.7373782396316528)) # Same goes for rotation >>> [ degrees(a) for a in C.object.rotation_euler ] [0.0, 0.0, 0.0] >>> [ degrees(a) for a in C.object.matrix_world.to_euler() ] [176.1907675277052, -11.192468775907107, -74.38630617504491] # To access quaternion rotation values, simply use "to_quaternion" >>> C.object.matrix_world.to_quaternion() Quaternion((0.17901940643787384, -0.7067779302597046, 0.6757702231407166, -0.10840550810098648))
You can also add an app handler that calculates and prints the simulated location and rotation after every frame change:
import bpy from math import degrees def print_simulated_loc_rot( scene ): C = bpy.context o = C.object simulated_loc = o.matrix_world.to_translation() simulated_rot = o.matrix_world.to_euler() sim_rot_degrees = [ degrees(v) for v in simulated_rot ] print_friendly_loc = [ round( v, 2 ) for v in simulated_loc ] print_friendly_rot = [ round( v, 2 ) for v in sim_rot_degrees ] print( "Location: ", print_friendly_loc ) print( "Rotation: ", print_friendly_rot ) bpy.app.handlers.frame_change_post.append( print_simulated_loc_rot )