mGearのAuto Fit Guideを実機で確認したところ、Guide生成とEmbedまでは触れましたが、Match Guides の内部で Smart Adjust 系の処理が TransformationMatrix.rotateTo で失敗し、自動サイズ合わせまでは通りませんでした。なので、機能の存在だけでなく、どの処理単位で止まるかまで切り分けて確認しました。
Auto Fitは便利ですが、版差やAPI差異の影響を受けやすい処理だと感じました。現場では完全自動前提ではなく、テンプレGuideと手動補正を併用する運用の方が堅いと考えています。
# -*- coding: utf-8 -*-
import maya.cmds as cmds
# これは 3点から pole vector 用の位置を出す やつ。
import maya.cmds as cmds
import math
def vec_sub(a, b):
return [a[0]-b[0], a[1]-b[1], a[2]-b[2]]
def vec_add(a, b):
return [a[0]+b[0], a[1]+b[1], a[2]+b[2]]
def vec_mul(a, s):
return [a[0]*s, a[1]*s, a[2]*s]
def vec_len(a):
return math.sqrt(a[0]**2 + a[1]**2 + a[2]**2)
def vec_norm(a):
l = vec_len(a)
if l < 1e-8:
return [0.0, 0.0, 0.0]
return [a[0]/l, a[1]/l, a[2]/l]
def vec_dot(a, b):
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
def vec_cross(a, b):
return [
a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0]
]
def get_pole_vector_position(start, mid, end, distance_scale=1.0):
"""
start, mid, end: world position [x,y,z]
"""
start_to_end = vec_sub(end, start)
start_to_mid = vec_sub(mid, start)
line_dir = vec_norm(start_to_end)
proj_len = vec_dot(start_to_mid, line_dir)
proj = vec_add(start, vec_mul(line_dir, proj_len))
arrow = vec_sub(mid, proj)
arrow_len = vec_len(arrow)
if arrow_len < 1e-6:
# ほぼ一直線なら適当な補助方向
fallback = vec_cross(line_dir, [0, 1, 0])
if vec_len(fallback) < 1e-6:
fallback = vec_cross(line_dir, [1, 0, 0])
arrow = vec_norm(fallback)
chain_len = vec_len(vec_sub(mid, start)) + vec_len(vec_sub(end, mid))
return vec_add(mid, vec_mul(arrow, chain_len * 0.5 * distance_scale))
arrow_dir = vec_norm(arrow)
chain_len = vec_len(vec_sub(mid, start)) + vec_len(vec_sub(end, mid))
return vec_add(mid, vec_mul(arrow_dir, chain_len * 0.5 * distance_scale))
#upv を置く処理
def move_upv_guide(guide_name, start_joint, mid_joint, end_joint, distance_scale=1.0):
guide = first_existing([guide_name])
s = find_unique_by_short_name(start_joint)
m = find_unique_by_short_name(mid_joint)
e = find_unique_by_short_name(end_joint)
if not guide:
print("[SKIP] upv guide missing:", guide_name)
return
if not (s and m and e):
print("[SKIP] joints missing for upv:", guide_name, start_joint, mid_joint, end_joint)
return
pos = get_pole_vector_position(ws_pos(s), ws_pos(m), ws_pos(e), distance_scale)
set_ws_pos(guide, pos)
print("[OK] moved upv:", guide_name)
# 安全な aim のやり方
# Mayaで transform を向けたいだけならこういう感じ。
def aim_node_to_target(node, target, aim=(1,0,0), up=(0,1,0), world_up=(0,1,0)):
tmp = cmds.aimConstraint(
target,
node,
aimVector=aim,
upVector=up,
worldUpType="vector",
worldUpVector=world_up
)
cmds.delete(tmp)
# まずは位置合わせ追加版
def align_finger_chain(guide_prefix, joint_names):
"""
例:
guide_prefix = "index_L0"
joint_names = ["index_metacarpal_l", "index_01_l", "index_02_l", "index_03_l"]
"""
root_g = first_existing([guide_prefix + "_root"])
loc0_g = first_existing([guide_prefix + "_0_loc"])
loc1_g = first_existing([guide_prefix + "_1_loc"])
loc2_g = first_existing([guide_prefix + "_2_loc"])
src0 = find_unique_by_short_name(joint_names[0])
src1 = find_unique_by_short_name(joint_names[1])
src2 = find_unique_by_short_name(joint_names[2])
src3 = find_unique_by_short_name(joint_names[3])
pairs = [
(root_g, src0, guide_prefix + "_root"),
(loc0_g, src1, guide_prefix + "_0_loc"),
(loc1_g, src2, guide_prefix + "_1_loc"),
(loc2_g, src3, guide_prefix + "_2_loc"),
]
for g, j, label in pairs:
if not g:
print("[SKIP] guide missing:", label)
continue
if not j:
print("[SKIP] joint missing for:", label)
continue
set_ws_pos(g, ws_pos(j))
print("[OK] moved", label)
# 指はこれが大事。
# *_blade を root → 先端方向 に向ける。
def aim_blade_from_chain(blade_name, root_joint, next_joint, world_up=(0, 1, 0)):
blade = first_existing([blade_name])
j0 = find_unique_by_short_name(root_joint)
j1 = find_unique_by_short_name(next_joint)
if not blade:
print("[SKIP] blade missing:", blade_name)
return
if not (j0 and j1):
print("[SKIP] joints missing for blade:", blade_name, root_joint, next_joint)
return
# bladeをroot位置へ
set_ws_pos(blade, ws_pos(j0))
# 一時ロケータを先端位置に置いてaim
temp = cmds.spaceLocator(name="tmpAim_loc#")[0]
set_ws_pos(temp, ws_pos(j1))
try:
con = cmds.aimConstraint(
temp,
blade,
aimVector=(1, 0, 0),
upVector=(0, 1, 0),
worldUpType="vector",
worldUpVector=world_up
)
cmds.delete(con)
except Exception as e:
print("[ERROR] aim failed:", blade_name, e)
cmds.delete(temp)
print("[OK] aimed blade:", blade_name)
# =========================================================
# Utility
# =========================================================
def _short_name(node):
return node.split("|")[-1]
def find_unique_by_short_name(short_name):
"""Scene内から short name 一致ノードを1つだけ返す"""
matches = cmds.ls("*|" + short_name, long=True) or []
root_matches = cmds.ls(short_name, long=True) or []
all_matches = list(set(matches + root_matches))
if not all_matches:
return None
if len(all_matches) > 1:
print("[WARN] multiple nodes found for short name: {} -> {}".format(short_name, all_matches))
return all_matches[0]
return all_matches[0]
def first_existing(candidates):
"""候補名の中で最初に存在するノードを返す"""
for c in candidates:
node = find_unique_by_short_name(c)
if node:
return node
return None
def ws_pos(node):
return cmds.xform(node, q=True, ws=True, t=True)
def set_ws_pos(node, pos):
cmds.xform(node, ws=True, t=pos)
def avg_pos(nodes):
pts = [ws_pos(n) for n in nodes]
count = float(len(pts))
return [
sum(p[0] for p in pts) / count,
sum(p[1] for p in pts) / count,
sum(p[2] for p in pts) / count,
]
def lerp_pos(a, b, t=0.5):
pa = ws_pos(a)
pb = ws_pos(b)
return [
pa[0] + (pb[0] - pa[0]) * t,
pa[1] + (pb[1] - pa[1]) * t,
pa[2] + (pb[2] - pa[2]) * t,
]
def list_guide_nodes():
"""guide配下のノード確認用"""
guide = find_unique_by_short_name("guide")
if not guide:
print("[ERROR] guide root not found")
return
nodes = cmds.listRelatives(guide, ad=True, f=True) or []
nodes = sorted(nodes, key=lambda x: _short_name(x))
print("=" * 60)
print("Guide nodes:")
for n in nodes:
print(_short_name(n))
print("=" * 60)
# =========================================================
# Guide candidate names
# EPIC_mannequin_y_up を想定しつつ、少し候補を広めに持つ
# 見つからない場合は list_guide_nodes() で確認して差し替えてください
# =========================================================
GUIDE_CANDIDATES = {
# Center
"pelvis": ["body_C0_root", "spine_C0_root"],
"spine1": [
"spine_C0_spineBase"
],
"spine2": [
"spine_C0_spineTop"
],
"chest": [
"spine_C0_chest"
],
"neck": [
"neck_C0_neck", "neck_C0_root"
],
"head": [
"neck_C0_head"
],
# Left arm
"clav_l": ["clavicle_L0_root"],
"elbow_l": ["arm_L0_elbow"],
"wrist_l": ["arm_L0_wrist"],
# Right arm
"clav_r": ["clavicle_R0_root"],
"elbow_r": ["arm_R0_elbow"],
"wrist_r": ["arm_R0_wrist"],
# Left leg
"thigh_l": ["leg_L0_root"],
"knee_l": ["leg_L0_knee"],
"ankle_l": ["leg_L0_ankle"],
# Right leg
"thigh_r": ["leg_R0_root"],
"knee_r": ["leg_R0_knee"],
"ankle_r": ["leg_R0_ankle"],
}
# =========================================================
# Source joints
# 今回あなたのジョイント名に合わせている
# =========================================================
JOINTS = {
"root": "root",
"pelvis": "pelvis",
"spine_01": "spine_01",
"spine_02": "spine_02",
"spine_03": "spine_03",
"spine_04": "spine_04",
"spine_05": "spine_05",
"neck_01": "neck_01",
"neck_02": "neck_02",
"clavicle_l": "clavicle_l",
"upperarm_l": "upperarm_l",
"lowerarm_l": "lowerarm_l",
"hand_l": "hand_l",
"clavicle_r": "clavicle_r",
"upperarm_r": "upperarm_r",
"lowerarm_r": "lowerarm_r",
"hand_r": "hand_r",
"thigh_l": "thigh_l",
"calf_l": "calf_l",
"foot_l": "foot_l",
"thigh_r": "thigh_r",
"calf_r": "calf_r",
"foot_r": "foot_r",
}
# =========================================================
# Core
# =========================================================
def resolve_guides():
resolved = {}
for key, candidates in GUIDE_CANDIDATES.items():
node = first_existing(candidates)
if not node:
print("[WARN] guide not found for {} candidates={}".format(key, candidates))
resolved[key] = node
return resolved
def resolve_joints():
resolved = {}
for key, short_name in JOINTS.items():
node = find_unique_by_short_name(short_name)
if not node:
print("[WARN] joint not found: {}".format(short_name))
resolved[key] = node
return resolved
def safe_move(guide_node, pos, label):
if not guide_node:
print("[SKIP] guide missing: {}".format(label))
return
try:
set_ws_pos(guide_node, pos)
print("[OK] moved {}".format(label))
except Exception as e:
print("[ERROR] failed move {} : {}".format(label, e))
def align_mgear_guides_to_epic():
guides = resolve_guides()
joints = resolve_joints()
print("=" * 60)
print("Start align")
print("=" * 60)
# -----------------------------------------------------
# Center
# -----------------------------------------------------
if joints["pelvis"]:
safe_move(guides["pelvis"], ws_pos(joints["pelvis"]), "pelvis")
# spine guide locators が複数ある場合に順番に置く
# spine
if joints["spine_01"] and joints["spine_03"]:
safe_move(guides["spine1"], avg_pos([joints["spine_01"], joints["spine_03"]]), "spine1")
if joints["spine_03"] and joints["spine_05"]:
safe_move(guides["spine2"], avg_pos([joints["spine_03"], joints["spine_05"]]), "spine2")
if joints["spine_05"]:
safe_move(guides["chest"], ws_pos(joints["spine_05"]), "chest")
# neck / head
if joints["neck_01"]:
safe_move(guides["neck"], ws_pos(joints["neck_01"]), "neck")
if joints["neck_02"]:
safe_move(guides["head"], ws_pos(joints["neck_02"]), "head")
# -----------------------------------------------------
# Left arm
# -----------------------------------------------------
if joints["clavicle_l"]:
safe_move(guides["clav_l"], ws_pos(joints["clavicle_l"]), "clav_l")
if joints["lowerarm_l"]:
safe_move(guides["elbow_l"], ws_pos(joints["lowerarm_l"]), "elbow_l")
if joints["hand_l"]:
safe_move(guides["wrist_l"], ws_pos(joints["hand_l"]), "wrist_l")
# -----------------------------------------------------
# Right arm
# -----------------------------------------------------
if joints["clavicle_r"]:
safe_move(guides["clav_r"], ws_pos(joints["clavicle_r"]), "clav_r")
if joints["lowerarm_r"]:
safe_move(guides["elbow_r"], ws_pos(joints["lowerarm_r"]), "elbow_r")
if joints["hand_r"]:
safe_move(guides["wrist_r"], ws_pos(joints["hand_r"]), "wrist_r")
# -----------------------------------------------------
# Left leg
# -----------------------------------------------------
if joints["thigh_l"]:
safe_move(guides["thigh_l"], ws_pos(joints["thigh_l"]), "thigh_l")
if joints["calf_l"]:
safe_move(guides["knee_l"], ws_pos(joints["calf_l"]), "knee_l")
if joints["foot_l"]:
safe_move(guides["ankle_l"], ws_pos(joints["foot_l"]), "ankle_l")
# -----------------------------------------------------
# Right leg
# -----------------------------------------------------
if joints["thigh_r"]:
safe_move(guides["thigh_r"], ws_pos(joints["thigh_r"]), "thigh_r")
if joints["calf_r"]:
safe_move(guides["knee_r"], ws_pos(joints["calf_r"]), "knee_r")
if joints["foot_r"]:
safe_move(guides["ankle_r"], ws_pos(joints["foot_r"]), "ankle_r")
print("=" * 60)
print("Done")
print("=" * 60)
# align_mgear_guides_to_epic() の最後あたりに追加。
# Arm upv
move_upv_guide("arm_L0_upv", "upperarm_l", "lowerarm_l", "hand_l", 1.0)
move_upv_guide("arm_R0_upv", "upperarm_r", "lowerarm_r", "hand_r", 1.0)
# Leg upv
move_upv_guide("leg_L0_upv", "thigh_l", "calf_l", "foot_l", 1.0)
move_upv_guide("leg_R0_upv", "thigh_r", "calf_r", "foot_r", 1.0)
# 指を足す
# -----------------------------
# Left fingers
# -----------------------------
align_finger_chain("thumb_L0", ["thumb_01_l", "thumb_02_l", "thumb_03_l", "thumb_03_l"])
align_finger_chain("index_L0", ["index_metacarpal_l", "index_01_l", "index_02_l", "index_03_l"])
align_finger_chain("middle_L0", ["middle_metacarpal_l", "middle_01_l", "middle_02_l", "middle_03_l"])
align_finger_chain("ring_L0", ["ring_metacarpal_l", "ring_01_l", "ring_02_l", "ring_03_l"])
align_finger_chain("pinky_L0", ["pinky_metacarpal_l", "pinky_01_l", "pinky_02_l", "pinky_03_l"])
aim_blade_from_chain("thumb_L0_blade", "thumb_01_l", "thumb_02_l")
aim_blade_from_chain("index_L0_blade", "index_metacarpal_l", "index_01_l")
aim_blade_from_chain("middle_L0_blade", "middle_metacarpal_l", "middle_01_l")
aim_blade_from_chain("ring_L0_blade", "ring_metacarpal_l", "ring_01_l")
aim_blade_from_chain("pinky_L0_blade", "pinky_metacarpal_l", "pinky_01_l")
# -----------------------------
# Right fingers
# -----------------------------
align_finger_chain("thumb_R0", ["thumb_01_r", "thumb_02_r", "thumb_03_r", "thumb_03_r"])
align_finger_chain("index_R0", ["index_metacarpal_r", "index_01_r", "index_02_r", "index_03_r"])
align_finger_chain("middle_R0", ["middle_metacarpal_r", "middle_01_r", "middle_02_r", "middle_03_r"])
align_finger_chain("ring_R0", ["ring_metacarpal_r", "ring_01_r", "ring_02_r", "ring_03_r"])
align_finger_chain("pinky_R0", ["pinky_metacarpal_r", "pinky_01_r", "pinky_02_r", "pinky_03_r"])
aim_blade_from_chain("thumb_R0_blade", "thumb_01_r", "thumb_02_r")
aim_blade_from_chain("index_R0_blade", "index_metacarpal_r", "index_01_r")
aim_blade_from_chain("middle_R0_blade", "middle_metacarpal_r", "middle_01_r")
aim_blade_from_chain("ring_R0_blade", "ring_metacarpal_r", "ring_01_r")
aim_blade_from_chain("pinky_R0_blade", "pinky_metacarpal_r", "pinky_01_r")
# 実行
align_mgear_guides_to_epic()
# -*- coding: utf-8 -*-
# =========================================================
# mGear Guide Auto Align Script (Comment-rich version)
# ---------------------------------------------------------
# 目的:
# 既存ジョイント(UE系など)から mGear Guide を
# 「位置+PoleVector+指のblade方向」まで自動配置する
#
# 設計思想:
# ・まず位置を100%合わせる
# ・IKの安定に重要な PoleVector を自動配置
# ・指は回転ではなく blade(方向ガイド)で補正
# ・回転は“必要な箇所だけ”後処理(やりすぎない)
#
# 注意:
# ・GuideのTransformは直接Freezeしない(mGear構造破壊)
# ・Mesh / Joint 側は Scale=1 を前提
# =========================================================
import maya.cmds as cmds
import math
# =========================================================
# 基本ユーティリティ
# =========================================================
def _short_name(node):
"""フルパスから短い名前だけ取る"""
return node.split("|")[-1]
def find_unique_by_short_name(short_name):
"""
シーン内から短い名前一致ノードを取得
※複数ある場合は最初の1つを使う(警告出す)
"""
matches = cmds.ls("*|" + short_name, long=True) or []
root_matches = cmds.ls(short_name, long=True) or []
all_matches = list(set(matches + root_matches))
if not all_matches:
return None
if len(all_matches) > 1:
print("[WARN] multiple nodes found:", short_name)
return all_matches[0]
return all_matches[0]
def first_existing(candidates):
"""
候補リストの中から存在するノードを返す
※Guide名の版差対策
"""
for c in candidates:
node = find_unique_by_short_name(c)
if node:
return node
return None
def ws_pos(node):
"""ワールド座標取得"""
return cmds.xform(node, q=True, ws=True, t=True)
def set_ws_pos(node, pos):
"""ワールド座標で移動"""
cmds.xform(node, ws=True, t=pos)
def avg_pos(nodes):
"""複数ノードの平均位置(spineなどで使用)"""
pts = [ws_pos(n) for n in nodes]
c = float(len(pts))
return [
sum(p[0] for p in pts)/c,
sum(p[1] for p in pts)/c,
sum(p[2] for p in pts)/c
]
# =========================================================
# ベクトル計算(PoleVector用)
# =========================================================
def vec_sub(a, b): return [a[0]-b[0], a[1]-b[1], a[2]-b[2]]
def vec_add(a, b): return [a[0]+b[0], a[1]+b[1], a[2]+b[2]]
def vec_mul(a, s): return [a[0]*s, a[1]*s, a[2]*s]
def vec_len(a):
return math.sqrt(a[0]**2 + a[1]**2 + a[2]**2)
def vec_norm(a):
l = vec_len(a)
if l < 1e-8:
return [0,0,0]
return [a[0]/l, a[1]/l, a[2]/l]
def vec_dot(a,b):
return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
def vec_cross(a,b):
return [
a[1]*b[2]-a[2]*b[1],
a[2]*b[0]-a[0]*b[2],
a[0]*b[1]-a[1]*b[0]
]
# =========================================================
# PoleVector(IK安定の核心)
# =========================================================
def get_pole_vector_position(start, mid, end):
"""
3点からIK平面を作り、適切なPoleVector位置を算出
"""
line = vec_norm(vec_sub(end, start))
proj_len = vec_dot(vec_sub(mid, start), line)
proj = vec_add(start, vec_mul(line, proj_len))
arrow = vec_sub(mid, proj)
if vec_len(arrow) < 1e-6:
arrow = vec_cross(line, [0,1,0])
arrow = vec_norm(arrow)
length = vec_len(vec_sub(mid,start)) + vec_len(vec_sub(end,mid))
return vec_add(mid, vec_mul(arrow, length*0.5))
def move_upv(guide, j0, j1, j2):
"""PoleVector Guide配置"""
g = first_existing([guide])
a = find_unique_by_short_name(j0)
b = find_unique_by_short_name(j1)
c = find_unique_by_short_name(j2)
if not (g and a and b and c):
print("[SKIP upv]", guide)
return
pos = get_pole_vector_position(ws_pos(a), ws_pos(b), ws_pos(c))
set_ws_pos(g, pos)
print("[OK upv]", guide)
# =========================================================
# 指チェーン
# =========================================================
def align_finger(prefix, joints):
"""
指は単純に位置を順に合わせる
"""
guides = [
first_existing([prefix+"_root"]),
first_existing([prefix+"_0_loc"]),
first_existing([prefix+"_1_loc"]),
first_existing([prefix+"_2_loc"])
]
src = [find_unique_by_short_name(j) for j in joints]
for g, j in zip(guides, src):
if g and j:
set_ws_pos(g, ws_pos(j))
print("[OK finger]", g)
def aim_blade(blade, j0, j1, up=(0,1,0)):
"""
指の向きはbladeで制御
→ root→先端方向に向ける
"""
b = first_existing([blade])
a = find_unique_by_short_name(j0)
c = find_unique_by_short_name(j1)
if not (b and a and c):
print("[SKIP blade]", blade)
return
set_ws_pos(b, ws_pos(a))
tmp = cmds.spaceLocator()[0]
set_ws_pos(tmp, ws_pos(c))
con = cmds.aimConstraint(tmp, b,
aimVector=(1,0,0),
upVector=(0,1,0),
worldUpType="vector",
worldUpVector=up
)
cmds.delete(con)
cmds.delete(tmp)
print("[OK blade]", blade)
# =========================================================
# メイン処理
# =========================================================
def run():
print("="*50)
print("Start mGear Guide Align")
print("="*50)
# -------------------------
# 中心
# -------------------------
set_ws_pos(first_existing(["body_C0_root"]), ws_pos(find_unique_by_short_name("pelvis")))
set_ws_pos(first_existing(["spine_C0_spineBase"]),
avg_pos([find_unique_by_short_name("spine_01"),
find_unique_by_short_name("spine_03")]))
set_ws_pos(first_existing(["spine_C0_spineTop"]),
avg_pos([find_unique_by_short_name("spine_03"),
find_unique_by_short_name("spine_05")]))
set_ws_pos(first_existing(["spine_C0_chest"]),
ws_pos(find_unique_by_short_name("spine_05")))
set_ws_pos(first_existing(["neck_C0_neck"]),
ws_pos(find_unique_by_short_name("neck_01")))
set_ws_pos(first_existing(["neck_C0_head"]),
ws_pos(find_unique_by_short_name("neck_02")))
# -------------------------
# 腕
# -------------------------
set_ws_pos(first_existing(["clavicle_L0_root"]), ws_pos(find_unique_by_short_name("clavicle_l")))
set_ws_pos(first_existing(["arm_L0_elbow"]), ws_pos(find_unique_by_short_name("lowerarm_l")))
set_ws_pos(first_existing(["arm_L0_wrist"]), ws_pos(find_unique_by_short_name("hand_l")))
set_ws_pos(first_existing(["clavicle_R0_root"]), ws_pos(find_unique_by_short_name("clavicle_r")))
set_ws_pos(first_existing(["arm_R0_elbow"]), ws_pos(find_unique_by_short_name("lowerarm_r")))
set_ws_pos(first_existing(["arm_R0_wrist"]), ws_pos(find_unique_by_short_name("hand_r")))
# -------------------------
# 脚
# -------------------------
set_ws_pos(first_existing(["leg_L0_root"]), ws_pos(find_unique_by_short_name("thigh_l")))
set_ws_pos(first_existing(["leg_L0_knee"]), ws_pos(find_unique_by_short_name("calf_l")))
set_ws_pos(first_existing(["leg_L0_ankle"]), ws_pos(find_unique_by_short_name("foot_l")))
set_ws_pos(first_existing(["leg_R0_root"]), ws_pos(find_unique_by_short_name("thigh_r")))
set_ws_pos(first_existing(["leg_R0_knee"]), ws_pos(find_unique_by_short_name("calf_r")))
set_ws_pos(first_existing(["leg_R0_ankle"]), ws_pos(find_unique_by_short_name("foot_r")))
# -------------------------
# PoleVector
# -------------------------
move_upv("arm_L0_upv","upperarm_l","lowerarm_l","hand_l")
move_upv("arm_R0_upv","upperarm_r","lowerarm_r","hand_r")
move_upv("leg_L0_upv","thigh_l","calf_l","foot_l")
move_upv("leg_R0_upv","thigh_r","calf_r","foot_r")
# -------------------------
# 指
# -------------------------
align_finger("index_L0",["index_metacarpal_l","index_01_l","index_02_l","index_03_l"])
align_finger("index_R0",["index_metacarpal_r","index_01_r","index_02_r","index_03_r"])
# blade
aim_blade("index_L0_blade","index_metacarpal_l","index_01_l")
aim_blade("index_R0_blade","index_metacarpal_r","index_01_r")
print("="*50)
print("Done")
print("="*50)
# 実行
run()
mGearのAuto Fit Guideを検証した際に、内部処理に依存する部分で不安定になるケースがあったため、既存ジョイントからGuideを配置するスクリプトを自作しました。
その上で、IKの安定性を確保するために、腕と脚は3点から平面を計算し、Pole Vectorを自動配置する処理を入れています。これにより、ビルド後のIKの破綻を抑えています。