I have been using obj.rotation_euler to manipulate objects’ orientations, but I would like to perform a rotation about a given global axis.
What’s the best way to do that?
I have been using obj.rotation_euler to manipulate objects’ orientations, but I would like to perform a rotation about a given global axis.
What’s the best way to do that?
you could e.g. do
obj.matrix_world *= Matrix.Rotation(radians(45), 4, ‘Z’)
I’m afraid I’m not having any luck with that either.
Let’s take a simple example. I want to take the default cube, rotate it 90 deg about the global z-axis, then 90 deg about the global y-axis.
Here is a script containing an attempt to do that (to be applied to a blender default file). Can you suggest ways to make it work?
Many thanks!
import bpy,math,mathutils
from math import *
from mathutils import *
obj = bpy.data.objects[‘Cube’]
bpy.context.scene.frame_set(frame=1)
obj.keyframe_insert(‘rotation_euler’)
bpy.context.scene.frame_set(frame=20)
obj.keyframe_insert(‘rotation_euler’)
bpy.context.scene.frame_set(frame=40)
obj.matrix_world *= Matrix.Rotation((math.pi/2.), 4, ‘Z’)
obj.keyframe_insert(‘rotation_euler’)
bpy.context.scene.frame_set(frame=60)
obj.keyframe_insert(‘rotation_euler’)
bpy.context.scene.frame_set(frame=80)
obj.matrix_world *= Matrix.Rotation((math.pi/2.), 4, ‘Y’)
obj.keyframe_insert(‘rotation_euler’)
see Gimbal Lock problem:
http://www.cs.princeton.edu/~gewang/projects/darth/stuff/quat_faq.html#Q34
After the first translation, local Y will be X axis. Since you multiply by the previous matrix_world, it’s doing relative transform. You can solve it e.g. like:
import bpy,math,mathutilsfrom math import *
from mathutils import *
obj = bpy.data.objects['Cube']
bpy.context.scene.frame_set(frame=1)
obj.keyframe_insert('rotation_euler')
bpy.context.scene.frame_set(frame=20)
obj.keyframe_insert('rotation_euler')
bpy.context.scene.frame_set(frame=40)
obj.matrix_world *= Matrix.Rotation((math.pi/2.), 4, 'Z')
obj.keyframe_insert('rotation_euler')
bpy.context.scene.frame_set(frame=60)
obj.keyframe_insert('rotation_euler')
bpy.context.scene.frame_set(frame=80)
obj.matrix_world *= Matrix.Rotation((math.pi/2.), 4, 'X')
obj.keyframe_insert('rotation_euler')
Maybe try to assign rotation_euler values, that seems reasonable here but won’t get rid of the Gimbal Lock problem i suppose.
If I understand correctly, you have simply substituted an x-rotation for the y-rotation. The problem is that I wish to program a long sequence of these operations, and in general I won’t know which of the object’s axes is aligned along the global axis I’m interested in.
Can that be done analytically? Can the script itself figure out that in this case it’s ‘x’ that’s needed instead of ‘y’? (For the project I’m working on, all the rotations will be multiples of 90 deg, so the local axes will always be aligned with global ones. The trick is to figure out within the program which ones point in which directions.)
Let me phrase it another way. After executing the sample script I gave before, if I jump to frame 60 in my timeline and use the keyboard to enter RY90<Enter>, the cube performs the rotation I want, regardless of how the local axes are oriented.
I want to accomplish the same thing with keyframes created by a script - specifying the global y-axis and letting the script figure out that it’s a local x-rotation that’s needed.
i see, maybe add to the rotation_euler components?
.rotation_euler[0] += radians(90)
If I’m understanding you correctly, .rotation_euler[0] refers explicitly to the x-axis, and my problem is that in general I won’t know which axis to use.
It’s frustrating, because blender must know the answer somewhere – the UI typically uses global axes, as in the RY90 example I gave above. Even though I ask for “Y” from the keyboard, the system knows that it’s the local x-axis that will accomplish the rotation I want. I’d like to access that knowledge by somehow specifying “Y” in the script and similarly relying on the system to make the conversion to a local axis.
I am working on a solution, but it’s terribly klugey. I am assigning each object its own rotation matrix, managed entirely by the script. Every time I rotate an object, the script updates the matrix, and I can use it in future rotations to determine where the axes are.
But that can’t be the best way. I’m sure I’m duplicating a built-in capability, but if there is one, I haven’t found it.
Does this help?
maestro3: not entirely sure, but .rotation_euler[0] should refer to global x, not local
I don’t think so, but of course it just scratches the surface of quaternion manipulation.
What I’m looking for is something that will make blender use the global rather than the local axes. If the video said something about that, I missed it.
hm ok, how about
mat = C.object.matrix_world.to_3x3().to_4x4() # remove translation part
C.object.matrix_world *= mat.inverted() * Matrix.Rotation(radians(-90), 4, ‘X’) * mat
That’s basically like putting the object into world space, adding rotation and turning it back into its previous space but with that rotation added.
Sorry, I’m not following the part about C.object.matrix_world.to_3x3().to_4x4(), and I can’t get the python console to accept it.
Where does the “C” come from? Do I need to import some module to make it work?
What would be most helpful – can you incorporate your suggestion into the script I posted above ( 28-May-13, 11:58)?
Thanks a lot.
I apologize in advance if this is completely wrong but I am a noob at scripting. Would it not be possible to set the Rotation of the cube’s transform panel to Quaternion and then run a script like this.
import bpy
import math
rot=90
rot=math.pi*rot/180
bpy.context.scene.frame_set(1)
bpy.ops.transform.rotate(value=0, axis=(0, 1, 1))
bpy.ops.anim.keyframe_insert(type=‘Rotation’)
bpy.context.scene.frame_set(10)
bpy.ops.transform.rotate(value=rot, axis=(0, 0, 1)) # z-axis
bpy.ops.anim.keyframe_insert(type=‘Rotation’)
bpy.context.scene.frame_set(20)
bpy.ops.transform.rotate(value=rot, axis=(1, 0, 0)) # x-axis
bpy.ops.anim.keyframe_insert(type=‘Rotation’)
Thanks, that definitely seems to have possibilities. (BTW, I don’t see difference in behavior from changing the Rotation mode between Euler and Quaternion.)
I’m somewhat of a noob myself, and I have been using syntax like “obj.rotation_euler =” and “obj.keyframe_insert(‘rotation_euler’, frame = fr)” which have two advantages for me – since my script cycles through many objects and frames, I’d like to be able to specify them explicitly in the methods rather than changing the current frame and the active object for each step.
Do you know, is it possible to reformulate it that way?
you might wanna read:
http://www.blender.org/documentation/blender_python_api_2_67_1/info_quickstart.html#animation
Sorry, I’m not following the part about C.object.matrix_world.to_3x3().to_4x4(), and I can’t get the python console to accept it.
Works fine here… to_3x3() turns a 4x4 matrix into a 3x3 matrix, which means, only the rotation part will remain. to_4x4() turns a 3x3 matrix into a 4x4 matrix, the added fields initialize with a value of zero.
>>> C.object.matrix_world
Matrix(((0.9890050292015076, -0.12407364696264267, -0.08046603202819824, 0.09756404161453247),
(0.10431120544672012, 0.9709978103637695, -0.2151334583759308, -1.7439045906066895),
(0.1048247367143631, 0.20437456667423248, 0.9732639789581299, -1.5941532850265503),
(0.0, 0.0, 0.0, 1.0)))
>>> C.object.matrix_world.to_3x3()
Matrix(((0.9890050292015076, -0.12407364696264267, -0.08046603202819824),
(0.10431120544672012, 0.9709978103637695, -0.2151334583759308),
(0.1048247367143631, 0.20437456667423248, 0.9732639789581299)))
>>> C.object.matrix_world.to_3x3().to_4x4()
Matrix(((0.9890050292015076, -0.12407364696264267, -0.08046603202819824, 0.0),
(0.10431120544672012, 0.9709978103637695, -0.2151334583759308, 0.0),
(0.1048247367143631, 0.20437456667423248, 0.9732639789581299, 0.0),
(0.0, 0.0, 0.0, 1.0)))
This just gets rid of the translation part (right-most column), as this seems to influence the result.
Where does the “C” come from? Do I need to import some module to make it work?
C is an alias for bpy.context, if you wanna be able to use that C outside of the pyconsole, you can import it like:
from bpy import context as C
but you can also substitute it with bpy.context (if needed at all, if you got your object reference, then just use that)