import math import sys import bpy def keyframe_rotation(target, frame, degrees): target.rotation_euler[2] = math.radians(degrees) target.keyframe_insert(data_path="rotation_euler", index=2, frame=frame) def main(output_path: str) -> None: bpy.ops.wm.read_factory_settings(use_empty=True) mesh = bpy.data.meshes.new("PoseConstraintMesh") mesh.from_pydata( [(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)], [], [(0, 1, 2), (0, 2, 3)], ) mesh.update() mesh_object = bpy.data.objects.new("PoseConstraintMeshObject", mesh) bpy.context.collection.objects.link(mesh_object) mesh_object.location = (0.35, -0.2, 0.15) mesh_object.rotation_euler = (math.radians(7.0), math.radians(-11.0), math.radians(5.0)) mesh_object.scale = (1.2, 0.8, 1.1) scaled_parent = bpy.data.objects.new("ScaledRigParent", None) bpy.context.collection.objects.link(scaled_parent) scaled_parent.location = (-0.25, 0.3, 0.1) scaled_parent.rotation_euler = (math.radians(4.0), math.radians(3.0), math.radians(-8.0)) scaled_parent.scale = (1.35, 0.7, 1.15) armature_data = bpy.data.armatures.new("PoseConstraintArmature") armature_object = bpy.data.objects.new("PoseConstraintArmatureObject", armature_data) bpy.context.collection.objects.link(armature_object) armature_object.parent = scaled_parent armature_object.location = (-0.15, 0.1, -0.05) armature_object.rotation_euler = (math.radians(-6.0), math.radians(9.0), math.radians(12.0)) armature_object.scale = (0.9, 1.1, 1.05) bpy.context.view_layer.objects.active = armature_object armature_object.select_set(True) bpy.ops.object.mode_set(mode="EDIT") root = armature_data.edit_bones.new("Root") root.head = (0.0, 0.0, 0.0) root.tail = (0.0, 0.0, 1.0) mid = armature_data.edit_bones.new("Mid") mid.head = (0.0, 0.0, 1.0) mid.tail = (0.0, 0.0, 2.0) mid.parent = root mid.use_connect = True tip = armature_data.edit_bones.new("Tip") tip.head = (0.0, 0.0, 2.0) tip.tail = (0.0, 0.0, 3.0) tip.parent = mid tip.use_connect = True auxiliary = armature_data.edit_bones.new("Auxiliary") auxiliary.head = (0.5, 0.0, 0.5) auxiliary.tail = (0.5, 0.0, 1.25) auxiliary.parent = root bpy.ops.object.mode_set(mode="POSE") pose_root = armature_object.pose.bones["Root"] pose_root.rotation_mode = "XYZ" for frame, degrees in ((1, 0.0), (5, 18.0), (10, 36.0), (15, 48.0)): pose_root.rotation_euler[1] = math.radians(degrees) pose_root.keyframe_insert(data_path="rotation_euler", index=1, frame=frame) bpy.ops.object.mode_set(mode="OBJECT") target = bpy.data.objects.new("PoseConstraintTarget", None) bpy.context.collection.objects.link(target) target.location = (0.0, 0.0, 1.0) for frame, degrees in ((1, 0.0), (5, 35.0), (10, 70.0), (15, 95.0)): keyframe_rotation(target, frame, degrees) copy_rotation = armature_object.pose.bones["Auxiliary"].constraints.new("COPY_ROTATION") copy_rotation.name = "Animated Copy Rotation" copy_rotation.target = target copy_rotation.owner_space = "WORLD" copy_rotation.target_space = "WORLD" copy_rotation.use_x = False copy_rotation.use_y = False copy_rotation.use_z = True copy_rotation.influence = 0.75 ik_target = bpy.data.objects.new("PoseIKTarget", None) bpy.context.collection.objects.link(ik_target) pole_target = bpy.data.objects.new("PoseIKPole", None) bpy.context.collection.objects.link(pole_target) pole_target.location = (1.5, -1.0, 1.25) for frame, location in ( (1, (0.35, 0.15, 2.65)), (5, (0.75, 0.25, 2.45)), (10, (1.0, -0.15, 2.15)), (15, (0.45, -0.55, 2.55)), ): ik_target.location = location ik_target.keyframe_insert(data_path="location", frame=frame) ik = armature_object.pose.bones["Tip"].constraints.new("IK") ik.name = "Animated Two Bone IK" ik.target = ik_target ik.pole_target = pole_target ik.chain_count = 2 ik.iterations = 128 ik.influence = 0.85 root_group = mesh_object.vertex_groups.new(name="Root") mid_group = mesh_object.vertex_groups.new(name="Mid") tip_group = mesh_object.vertex_groups.new(name="Tip") auxiliary_group = mesh_object.vertex_groups.new(name="Auxiliary") root_group.add([0, 1], 1.0, "REPLACE") root_group.add([2, 3], 0.25, "REPLACE") mid_group.add([0, 1], 0.0, "REPLACE") mid_group.add([2, 3], 0.35, "REPLACE") tip_group.add([0, 1], 0.0, "REPLACE") tip_group.add([2, 3], 0.40, "REPLACE") auxiliary_group.add([0, 1, 2, 3], 0.05, "REPLACE") armature_modifier = mesh_object.modifiers.new(name="Armature Deform", type="ARMATURE") armature_modifier.object = armature_object scene = bpy.context.scene scene.frame_start = 1 scene.frame_end = 15 scene.frame_set(1) bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True) if __name__ == "__main__": if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 1: raise SystemExit("usage: blender -b --python generate-pose-constraint-fixture.py -- output.blend") main(sys.argv[sys.argv.index("--") + 1])