133 lines
5.7 KiB
Python
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 |