2025-07-01
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
|
||||
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user