
[Blender][script]標準ポーズライブラリより多くの情報を.jsonに保存、適用する
Blender標準のポーズライブラリはどうやらボーンのトランスフォームしか登録出来ない様で、かなり不便です。
その他のボーンプロパティやボーンコンストレインの情報も含め、jsonファイルに保存、適用するスクリプトです。
ポーズモードでボーンを選択して、ファイルメニューに追加されたコマンド(サムネ参照)を実行するとブラウザが開いて任意のファイル名で保存出来ます。
※.blendとついていますが、保存すると.blendが.jsonに置き換わります。

適用するする時は同じくファイルメニューから実行します。
import bpy
import json
import os
def save_pose_with_constraints(filepath):
# 拡張子が無ければ .json を付ける
if not filepath.endswith('.json'):
if not filepath.endswith('.blend'):
filepath += '.json'
else:
filepath = filepath.replace('.blend', '.json')
data = {}
obj = bpy.context.object
if not obj or obj.type != 'ARMATURE':
print("Active object is not an armature.")
return
data["bones"] = {}
for bone in obj.pose.bones:
bone_data = {
"location": list(bone.location),
"rotation_quaternion": list(bone.rotation_quaternion),
"rotation_euler": list(bone.rotation_euler),
"scale": list(bone.scale),
"constraints": [],
"bone_properties": { # ボーン固有のプロパティを追加
"inherit_rotation": bone.bone.use_inherit_rotation,
"inherit_scale": bone.bone.inherit_scale,
"use_local_location": bone.bone.use_local_location
}
}
for constraint in bone.constraints:
constraint_data = {}
for attr in dir(constraint):
if not attr.startswith("_") and not callable(getattr(constraint, attr)):
try:
value = getattr(constraint, attr)
if isinstance(value, (int, float, str, list, tuple, bool)):
constraint_data[attr] = value
except Exception as e:
print(f"Skipping attribute {attr}: {e}")
bone_data["constraints"].append({"type": constraint.type, "properties": constraint_data})
data["bones"][bone.name] = bone_data
with open(filepath, 'w') as f:
json.dump(data, f, indent=4)
print(f"Pose with constraints saved to {filepath}")
def load_pose_with_constraints(filepath):
obj = bpy.context.object
if not obj or obj.type != 'ARMATURE':
print("Active object is not an armature.")
return
with open(filepath, 'r') as f:
data = json.load(f)
for bone_name, bone_data in data["bones"].items():
if bone_name not in obj.pose.bones:
print(f"Bone {bone_name} not found in the armature. Skipping.")
continue
bone = obj.pose.bones[bone_name]
bone.location = bone_data["location"]
bone.rotation_quaternion = bone_data["rotation_quaternion"]
bone.rotation_euler = bone_data["rotation_euler"]
bone.scale = bone_data["scale"]
# ボーン固有プロパティを適用
bone.bone.use_inherit_rotation = bone_data["bone_properties"]["inherit_rotation"]
bone.bone.inherit_scale = bone_data["bone_properties"]["inherit_scale"]
bone.bone.use_local_location = bone_data["bone_properties"]["use_local_location"]
for constraint_data in bone_data["constraints"]:
constraint_type = constraint_data["type"]
existing_constraint = next((c for c in bone.constraints if c.type == constraint_type), None)
if not existing_constraint:
existing_constraint = bone.constraints.new(constraint_type)
for attr, value in constraint_data["properties"].items():
try:
setattr(existing_constraint, attr, value)
except Exception as e:
print(f"Skipping setting attribute {attr}: {e}")
print(f"Pose with constraints loaded from {filepath}")
class SavePoseOperator(bpy.types.Operator):
bl_idname = "pose.save_with_constraints"
bl_label = "Save Pose with Constraints"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
save_pose_with_constraints(self.filepath)
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
class LoadPoseOperator(bpy.types.Operator):
bl_idname = "pose.load_with_constraints"
bl_label = "Load Pose with Constraints"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
load_pose_with_constraints(self.filepath)
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def menu_func(self, context):
self.layout.operator(SavePoseOperator.bl_idname, text="Save Pose with Constraints")
self.layout.operator(LoadPoseOperator.bl_idname, text="Load Pose with Constraints")
def register():
bpy.utils.register_class(SavePoseOperator)
bpy.utils.register_class(LoadPoseOperator)
bpy.types.TOPBAR_MT_file.append(menu_func)
def unregister():
bpy.utils.unregister_class(SavePoseOperator)
bpy.utils.unregister_class(LoadPoseOperator)
bpy.types.TOPBAR_MT_file.remove(menu_func)
if __name__ == "__main__":
register()
必要なプロパティが漏れていたら追加します。