1
$\begingroup$

I am trying to rotate an object over it local axis in 2pi using python. Basically I want to animate this, so the problem in rotating the object in 2pi is that it returns to the original position (and not to the position + 2pi), so there is no way I can animate this.

If I rotate the object using the cursor it works. This means, I select the object, press R to rotate, two times Z so I rotate it with the mouse until I reach 360 degrees on the rotation box value. If I follow this procedure I can animate the object rotating over its own axis (not all coordinates, only x and z, since y also returns to the initial position it is not rotated).

Python code for the rotation:

import bpy
import numpy as np
from mathutils import Euler

def set_loc_rotation(obj, value):
    rot = Euler(value, 'ZYX')
    obj.rotation_euler = (obj.rotation_euler.to_matrix() @ rot.to_matrix()).to_euler(obj.rotation_mode)


# add then cubes and add as child of the previous axis
i = 0
bpy.ops.mesh.primitive_cube_add(
    size=1,
    enter_editmode=False,
    align='WORLD',
    location=(0, 0, 0),
    scale=(1, 1, 1))
bpy.context.object.name = f'cube_{i}'

# make axis parent of cube
bpy.ops.object.empty_add(type='PLAIN_AXES', align='WORLD', location=(0, 0, 0))
bpy.context.object.name = f'e_{i}'
bpy.data.objects[f'e_{i}'].rotation_euler = (np.radians(30), 0, 0)
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[f'cube_{i}'].select_set(True)
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)

# rotate the object around the axis in and add keyframes
bpy.context.scene.frame_end = 100
scene = bpy.data.scenes["Scene"]
scene.frame_current = 1
set_loc_rotation(bpy.data.objects[f'e_{i}'], (0, 0, np.radians(0)))
bpy.data.objects[f'e_{i}'].keyframe_insert(data_path='rotation_euler', frame=1)

scene.frame_current = 100
set_loc_rotation(bpy.data.objects[f'e_{i}'], (0, 0, np.radians(360)))
bpy.data.objects[f'e_{i}'].keyframe_insert(data_path='rotation_euler', frame=100)

Result: enter image description here The position is basically the same and I can't animate this.

So how can I do this with python so the axis actually rotates?

$\endgroup$
2
  • 1
    $\begingroup$ Does this answer your question? How to rotate an object? $\endgroup$
    – Lauloque
    Commented Jul 25, 2023 at 0:31
  • $\begingroup$ @L0Lock thank you for the reference, but following that I still have the same problem... Seems that these operations involve the sine and cosine of angles so rotating multiples of 2pi is the same as doing nothing, I am missing the 'phase'. $\endgroup$
    – Iván
    Commented Jul 25, 2023 at 1:16

1 Answer 1

2
$\begingroup$

It's the same(almost the same) value when you do matrix multiplication (360d), so the keyframe values are the same, to fix this,

Change to:

import bpy
import numpy as np
from mathutils import Euler

dic = {
    'X': 0,
    'Y': 1,
    'Z': 2,
}

def set_loc_rotation(obj, value, mode='ZYX'):
    old_mode = obj.rotation_mode

    obj.rotation_mode = "QUATERNION"
    obj.rotation_mode = mode
    obj.rotation_euler[dic[mode[0]]] += value[0]
    obj.rotation_euler[dic[mode[1]]] += value[1]
    obj.rotation_euler[dic[mode[2]]] += value[2]
    return mode, old_mode


# add then cubes and add as child of the previous axis
i = 0
bpy.ops.mesh.primitive_cube_add(
    size=1,
    enter_editmode=False,
    align='WORLD',
    location=(0, 0, 0),
    scale=(1, 1, 1))
bpy.context.object.name = f'cube_{i}'

# make axis parent of cube
bpy.ops.object.empty_add(type='PLAIN_AXES', align='WORLD', location=(0, 0, 0))
bpy.context.object.name = f'e_{i}'
bpy.data.objects[f'e_{i}'].rotation_euler = (np.radians(30), 0, 0)
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[f'cube_{i}'].select_set(True)
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)

# rotate the object around the axis in and add keyframes
bpy.context.scene.frame_end = 100
scene = bpy.data.scenes["Scene"]
scene.frame_current = 1
# set_loc_rotation(bpy.data.objects[f'e_{i}'], (0, 0, np.radians(0)))
bpy.data.objects[f'e_{i}'].keyframe_insert(data_path='rotation_euler', frame=1)

scene.frame_current = 100
obj = bpy.data.objects[f'e_{i}']
mode, old_mode = set_loc_rotation(obj, (0, 0, np.radians(360)))
obj.keyframe_insert(data_path='rotation_euler', frame=100)
obj.rotation_mode = "QUATERNION"
obj.rotation_mode = old_mode

Animate the object by axis-angle

This example shows the cube rotating 360d around the arrow enter image description here

import bpy, math
from mathutils import Vector, Euler

# Add cube
bpy.ops.mesh.primitive_cube_add(location=(1,1,1), rotation=(1,1,1))
cube0 = bpy.context.object

# Add empty and make parent
bpy.ops.object.empty_add(radius=2, location=cube0.location)
empty = bpy.context.object
cube0.location = 0,0,0
cube0.parent = empty

# Add a direction reference
bpy.ops.object.empty_add(type="SINGLE_ARROW", radius=4, location=empty.location, rotation=(2,2,2))
ref = bpy.context.object

# Animate the empty
bpy.context.scene.frame_current = 1
empty.rotation_mode = "AXIS_ANGLE"
loc, rot, sca = ref.matrix_world.decompose()
up = rot @ Vector((0,0,1))
empty.rotation_axis_angle[1:] = up
empty.keyframe_insert(data_path="rotation_axis_angle")
bpy.context.scene.frame_current = 100
empty.rotation_axis_angle[0] += math.tau # 2*pi
empty.keyframe_insert(data_path="rotation_axis_angle")


# Add a copy to check if it's true
loc = 7,1,1
bpy.context.scene.frame_current = 1
bpy.ops.mesh.primitive_cube_add(location=loc, rotation=(1,1,1))
cube1 = bpy.context.object
bpy.ops.object.empty_add(type="SINGLE_ARROW", radius=4, location=loc, rotation=(2,2,2))
empty1 = bpy.context.object
cube1.parent = empty1
cube1.matrix_parent_inverse = empty1.matrix_world.inverted_safe()
$\endgroup$
3
  • $\begingroup$ Thanks for your answer. The problem with this approaxh is that I am rotating over global axis and not local axis. And I want to rotate it using local axis coordinates, thats why I was trying to use the rotation matrix. $\endgroup$
    – Iván
    Commented Jul 25, 2023 at 22:30
  • $\begingroup$ If you want to animate an object by rotating around a given axis, you can keyframe the rotation_axis_angle instead of rotation_euler. Because the axis of rotation_euler does not increase linearly, It can be increased several times in both directions (positive and negative) $\endgroup$
    – X Y
    Commented Jul 26, 2023 at 3:59
  • $\begingroup$ So the key here is to define the single arrow to define the orientation and keep only the z projection. Thanks! $\endgroup$
    – Iván
    Commented Jul 30, 2023 at 14:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .