-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
166 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
## Get object or posebone world position + 3 axis direction vectors | ||
|
||
def get_position_and_axis_direction(target): | ||
'''Get world position and local direction axis of object or bone | ||
return 4 vector: loc, x, y, z (1 position, 3 for axis directions)''' | ||
if isinstance(target, bpy.types.PoseBone): | ||
armature = target.id_data | ||
matrix = armature.matrix_world @ target.matrix | ||
else: | ||
matrix = target.matrix_world | ||
x = Vector((1,0,0)) | ||
y = Vector((0,1,0)) | ||
z = Vector((0,0,1)) | ||
x.rotate(matrix) | ||
y.rotate(matrix) | ||
z.rotate(matrix) | ||
return matrix.to_translation(), (x, y, z) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
## Insert keyframe on object or posebone, automatically determine rotation data_path with axis choice | ||
|
||
# Mapping of rotation modes to data_path | ||
ROTATION_MODE_MAP = { | ||
'ZYX': 'rotation_euler', | ||
'ZXY': 'rotation_euler', | ||
'YZX': 'rotation_euler', | ||
'YXZ': 'rotation_euler', | ||
'XZY': 'rotation_euler', | ||
'XYZ': 'rotation_euler', | ||
'QUATERNION': 'rotation_quaternion', | ||
'AXIS_ANGLE': 'rotation_axis_angle' | ||
} | ||
|
||
def set_keyframe_on_axis(target, transform, frame, use_x=True, use_y=True, use_z=True): | ||
'''simple keyframe insertion, pass object or bone, a transform channel name and a frame number | ||
transform (str): 'location', 'rotation' or 'scale' | ||
frame: frame number where to add the keyframes | ||
use_x, use_y, use_z (bool): enable keyframe insertion per axis (always takes all for quaternion and axis-angle rotation) | ||
''' | ||
|
||
## Define axis to key (always all 4 in quaternion and axis_angle mode) | ||
use_axes = [use_x, use_y, use_z] | ||
if all(use_axes) or (transform == 'rotation' and target.rotation_mode in ('QUATERNION', 'AXIS_ANGLE')): | ||
axis_indices = [-1] | ||
else: | ||
# Get the indices of enabled axes | ||
axis_indices = [i for i, use_axis in enumerate(use_axes) if use_axis] | ||
|
||
## define data_path to keyframe (adjust data_path for rotation) | ||
data_path = ROTATION_MODE_MAP[target.rotation_mode] if transform == 'rotation' else transform | ||
|
||
# if isinstance(target, bpy.types.PoseBone): | ||
for axis_index in axis_indices: | ||
## set group and options ? (probably not needed) | ||
target.keyframe_insert(data_path, index=axis_index, frame=frame) # , keytype='KEYFRAME' | ||
|
||
## Find and return newly created keyframes | ||
## (/!\ Using other Ops within operator sometimes void reference to keyframes) | ||
# path_prefix = '' | ||
# if isinstance(target, bpy.types.PoseBone): | ||
# path_prefix = f'pose.bones["{target.name}"].' | ||
# return [k for fc in target.id_data.animation_data.action.fcurves | ||
# if fc.data_path == f'{path_prefix}{data_path}' and (axis_indices[0] == -1 or fc.array_index in axis_indices) | ||
# for k in fc.keyframe_points if k.co.x == frame] | ||
|
||
|
||
## usage: key rotation on active object at frame 10 | ||
set_keyframe_on_axis(bpy.context.object, 'rotation', 10) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
## Translate object or bone in world space by a given vector | ||
|
||
def add_translation_vector(target, translation_vector): | ||
"""Apply world space translation vector to an object or a bone | ||
Usage: | ||
Add 1 unit up: | ||
add_translation_vector(bpy.context.object, Vector((0,0,1))) | ||
""" | ||
if isinstance(target, bpy.types.PoseBone): | ||
armature = target.id_data # Get the armature object | ||
# Switch to armature space | ||
current_world_position = (armature.matrix_world @ target.matrix).to_translation() | ||
new_world_position = current_world_position + translation_vector | ||
new_local_position = armature.matrix_world.inverted() @ new_world_position | ||
target.matrix.translation = new_local_position | ||
# return new_local_position | ||
return | ||
|
||
## Add directly to object matrix | ||
target.matrix_world.translation += translation_vector | ||
# return target.matrix_world.to_translation() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
## Apply quaternion rotation on object or pose bone | ||
|
||
from mathutils import Matrix | ||
|
||
def apply_quaternion_to_pose_bone(posebone, quaternion): | ||
"""Rotate pose bone in world space by given quaternion""" | ||
# Create rotation matrix from quaternion | ||
rot_mat = quaternion.to_matrix().to_4x4() | ||
|
||
# Get bone current world matrix | ||
armature = posebone.id_data | ||
world_mat = (armature.matrix_world @ posebone.matrix).copy() | ||
|
||
# Get world pivot point | ||
world_pivot = armature.matrix_world @ posebone.head | ||
|
||
# Apply the rotation on the copy of world matrix | ||
# remove pivot vector, apply rotation, re-apply pivot | ||
new_world_mat = Matrix.Translation(world_pivot) @ \ | ||
rot_mat @ \ | ||
Matrix.Translation(-world_pivot) @ \ | ||
world_mat | ||
|
||
## Assign matrix to posebone in armature space | ||
posebone.matrix = armature.matrix_world.inverted() @ new_world_mat | ||
|
||
def apply_quaternion_to_object(obj, quaternion): | ||
"""Rotate pose bone in world space by given quaternion""" | ||
# Create rotation matrix from quaternion | ||
rotation_matrix = quaternion.to_matrix().to_4x4() | ||
|
||
# Get object's current world matrix | ||
world_matrix = obj.matrix_world | ||
|
||
# Get object's pivot point (origin) in world space | ||
pivot = world_matrix.to_translation() | ||
|
||
# Create translation matrices | ||
to_pivot = mathutils.Matrix.Translation(-pivot) | ||
from_pivot = mathutils.Matrix.Translation(pivot) | ||
|
||
# Apply rotation around pivot: | ||
# 1. Move to center, 2. Apply rotation, 3. Move back to initail position | ||
obj.matrix_world = from_pivot @ rotation_matrix @ to_pivot @ world_matrix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
## Apply a Euler to rotate an object in worldspace (not much use case) | ||
|
||
import bpy | ||
from mathutils import Matrix, Euler | ||
from math import radians | ||
|
||
def apply_world_euler_rotation_object(obj, rotation_euler): | ||
# Get current world matrix | ||
world_matrix = obj.matrix_world | ||
|
||
# Get pivot point | ||
pivot = world_matrix.to_translation() | ||
|
||
# Create rotation matrix from Euler | ||
rotation_matrix = rotation_euler.to_matrix().to_4x4() | ||
|
||
# Create translation matrices | ||
to_pivot = Matrix.Translation(-pivot) | ||
from_pivot = Matrix.Translation(pivot) | ||
|
||
# Apply rotation around pivot | ||
new_matrix = from_pivot @ rotation_matrix @ to_pivot @ world_matrix | ||
|
||
# Extract Euler angles from the new matrix while maintaining rotation mode | ||
rot_mode = obj.rotation_mode | ||
new_euler = new_matrix.to_euler(rot_mode) | ||
|
||
# Make compatible with previous rotation to avoid discontinuities | ||
new_euler.make_compatible(obj.rotation_euler) | ||
|
||
# Update object's rotation | ||
obj.rotation_euler = new_euler | ||
|
||
# Example, rotate active object by 45 degrees on World X axis | ||
apply_world_euler_rotation_object(bpy.context.object, Euler((radians(45),0,0))) |