Files
2026-03-17 15:34:28 -06:00

133 lines
5.7 KiB
Python

import bpy
from mathutils import Matrix, Vector
from . import bones, utils, vars
def set_pose_bone_world_transform(arm, pose_bone: bpy.types.PoseBone, world_transform: dict, local_transform: dict):
if arm and pose_bone and world_transform and local_transform:
loc = utils.array_to_vector(world_transform["location"]) * 0.01
rot = utils.array_to_quaternion(world_transform["rotation"])
s = utils.array_to_vector(world_transform["scale"])
sca = Vector((utils.sign(s.x), utils.sign(s.y), utils.sign(s.z))) * arm.scale
M = utils.make_transform_matrix(loc, rot, sca)
pose_bone.matrix = M
def set_mesh_world_transform(arm, mesh_obj: bpy.types.Object, world_transform: dict, local_transform: dict):
if arm and mesh_obj and world_transform and local_transform:
loc = utils.array_to_vector(world_transform["location"]) * 0.01
rot = utils.array_to_quaternion(world_transform["rotation"])
s = utils.array_to_vector(world_transform["scale"])
sca = Vector((utils.sign(s.x), utils.sign(s.y), utils.sign(s.z))) * arm.scale
M = utils.make_transform_matrix(loc, rot, sca)
mesh_obj.matrix_world = M
def bone_name_match(rl_name, blender_name):
if rl_name == "_Object_Pivot_Node_":
rl_name = "CC_Base_Pivot"
export_name = bones.rl_export_bone_name(rl_name)
unduplicated_name = utils.strip_name(blender_name)
if rl_name == unduplicated_name or export_name == unduplicated_name:
return True
return False
def deduplicate_name(name, names: dict):
count = names[name] if name in names else 0
names[name] = count + 1
return f"{name}.{count:03d}" if count > 0 else name
def match_id_tree(rl_tree, arm=None,
pose_bone: bpy.types.PoseBone=None,
mesh_obj: bpy.types.Object=None,
parent_bone: bpy.types.PoseBone=None,
id_map=None,
names=None,
pose=False):
"""If supplying an armature, match the bone tree to the armature and return a mapping (by id)
to the armature bones. If no armature (i.e. for rigified avatars), then map the bones (by id)
to unduplicated bone names"""
if arm and not (pose_bone or mesh_obj):
pose_bone = arm.pose.bones[0]
if id_map is None:
id_map = {}
if names is None:
names = {}
name = pose_bone.name if pose_bone else mesh_obj.name if mesh_obj else deduplicate_name(rl_tree["name"], names)
# id_map is a dict of bones by ID, mapping the source skin_bone name to the armature bone or mesh
id_map[rl_tree["id"]] = {
"source": rl_tree["name"],
"name": name,
"mesh": mesh_obj is not None,
}
# id tree
id_tree = {
"name": name,
"id": rl_tree["id"],
"source": rl_tree["name"],
"children": []
}
world_transform_data = rl_tree.get("world_transform", None)
local_transform_data = rl_tree.get("local_transform", None)
if mesh_obj:
id_tree["mesh"] = True
if pose and world_transform_data:
set_mesh_world_transform(arm, mesh_obj, world_transform_data, local_transform_data)
else:
if pose and world_transform_data:
set_pose_bone_world_transform(arm, pose_bone, world_transform_data, local_transform_data)
#utils.log_detail(f"Bone: {bone.name} / Tree: {rl_tree['name']} {rl_tree['id']}")
for child_tree in rl_tree["children"]:
child_name = child_tree["name"]
#utils.log_detail(f"Trying: {child_name}")
if arm:
found = False
if not found and not child_tree["children"]:
for obj in arm.children:
if obj.parent and obj.parent_type == "BONE" and obj.parent_bone == pose_bone.name:
if bone_name_match(child_name, obj.name):
#utils.log_detail(f" - child_mesh: {obj.name} / child_tree: {child_name} - parented to: {obj.parent_bone}")
found = True
child_tree = match_id_tree(child_tree, arm=arm, mesh_obj=obj, parent_bone=pose_bone, id_map=id_map, pose=pose)[0]
if child_tree:
id_tree["children"].append(child_tree)
break
if not found:
for child_bone in pose_bone.children:
if bone_name_match(child_name, child_bone.name):
#utils.log_detail(f" - child_bone: {child_bone.name} / child_tree: {child_name}")
found = True
child_tree = match_id_tree(child_tree, arm=arm, pose_bone=child_bone, id_map=id_map, pose=pose)[0]
if child_tree:
id_tree["children"].append(child_tree)
break
else:
child_tree = match_id_tree(child_tree, id_map=id_map, names=names)[0]
if child_tree:
id_tree["children"].append(child_tree)
return id_tree, id_map
def confirm_bone_order(bones, ids, id_map: dict):
result = True
for id, id_def in id_map.items():
if id not in ids or id_def["source"] not in bones:
utils.log_warn(f"bone {id_def['source']} ({id}) not found in skin bones!")
result = False
if result and len(ids) < len(id_map):
utils.log_info("All bones present, but more bones found in id_tree!")
elif result:
utils.log_info("All bones present!")
return result
def convert_id_tree(arm, id_tree_root):
if not id_tree_root: return None
id_tree, id_map = match_id_tree(id_tree_root, arm=arm)
return id_tree, id_map