Streamingle_URP/3ds max script/MixamoToBiped_Converter - y up.ms

1759 lines
66 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
MixamoToBiped Converter - No Axis Twist Version
Extracted from TOOLS_MixamoToBiped_v1.0.51.ms
이 스크립트는 Mixamo T-Pose 아바타를 3ds Max Biped으로 변환합니다.
🎯 특징 (축 변환 제거 버전):
- Biped 로컬 축 정의 유지 (MapTMAxis 호출 제거)
- 기존 T-Pose 자세 매칭 알고리즘 유지
- IK 정렬과 기하학적 계산 보존
- 크기와 위치 조정은 정상 동작
- 스킨 가중치 완벽 이전
사용법:
1. Mixamo T-Pose FBX 파일을 임포트
2. 스크립트 실행 (자동으로 변환 시작)
Author: Based on Ishak Suryo Laksono's work
Modified: Removed MapTMAxis calls to preserve Biped local axes
*/
-- ================================== 핵심 데이터 (원본 그대로) ===============================================
-- 믹사모 본 이름들 (순서 중요 - 절대 변경 금지)
global MXNames =#("mixamorig:Hips","mixamorig:LeftUpLeg","mixamorig:LeftLeg","mixamorig:LeftFoot","mixamorig:LeftToeBase","mixamorig:RightUpLeg","mixamorig:RightLeg",
"mixamorig:RightFoot","mixamorig:RightToeBase","mixamorig:Spine","mixamorig:Spine1","mixamorig:LeftShoulder","mixamorig:LeftArm","mixamorig:LeftForeArm",
"mixamorig:LeftHand","mixamorig:LeftHandIndex1","mixamorig:LeftHandIndex2","mixamorig:LeftHandIndex3","mixamorig:LeftHandMiddle1","mixamorig:LeftHandMiddle2",
"mixamorig:LeftHandMiddle3","mixamorig:LeftHandPinky1","mixamorig:LeftHandPinky2","mixamorig:LeftHandPinky3","mixamorig:LeftHandRing1",
"mixamorig:LeftHandRing2","mixamorig:LeftHandRing3","mixamorig:LeftHandThumb1","mixamorig:LeftHandThumb2","mixamorig:LeftHandThumb3",
"mixamorig:Neck","mixamorig:Head","mixamorig:RightShoulder","mixamorig:RightArm","mixamorig:RightForeArm","mixamorig:RightHand",
"mixamorig:RightHandIndex1","mixamorig:RightHandIndex2","mixamorig:RightHandIndex3","mixamorig:RightHandMiddle1","mixamorig:RightHandMiddle2","mixamorig:RightHandMiddle3",
"mixamorig:RightHandPinky1","mixamorig:RightHandPinky2","mixamorig:RightHandPinky3","mixamorig:RightHandRing1","mixamorig:RightHandRing2",
"mixamorig:RightHandRing3","mixamorig:RightHandThumb1","mixamorig:RightHandThumb2","mixamorig:RightHandThumb3")
-- 믹사모 -> 바이패드 매핑 (순서 중요 - 절대 변경 금지)
global MXPairs = #(datapair "mixamorig:Hips" "Bip001 Pelvis",datapair "mixamorig:Spine" "Bip001 Spine",datapair "mixamorig:Spine1" "Bip001 Spine1",datapair "mixamorig:Neck" "Bip001 Neck",datapair "mixamorig:Head" "Bip001 Head",datapair "mixamorig:RightShoulder" "Bip001 R Clavicle",datapair "mixamorig:RightArm" "Bip001 R UpperArm",datapair "mixamorig:RightForeArm" "Bip001 R Forearm",datapair "mixamorig:RightHand" "Bip001 R Hand",datapair "mixamorig:RightHandThumb1" "Bip001 R Finger0",datapair "mixamorig:RightHandThumb2" "Bip001 R Finger01",datapair "mixamorig:RightHandThumb3" "Bip001 R Finger02",datapair "mixamorig:RightHandIndex1" "Bip001 R Finger1",datapair "mixamorig:RightHandIndex2" "Bip001 R Finger11",datapair "mixamorig:RightHandIndex3" "Bip001 R Finger12",datapair "mixamorig:RightHandMiddle1" "Bip001 R Finger2",datapair "mixamorig:RightHandMiddle2" "Bip001 R Finger21",datapair "mixamorig:RightHandMiddle3" "Bip001 R Finger22",datapair "mixamorig:RightHandRing1" "Bip001 R Finger3",datapair "mixamorig:RightHandRing2" "Bip001 R Finger31",datapair "mixamorig:RightHandRing3" "Bip001 R Finger32",datapair "mixamorig:RightHandPinky1" "Bip001 R Finger4",datapair "mixamorig:RightHandPinky2" "Bip001 R Finger41",datapair "mixamorig:RightHandPinky3" "Bip001 R Finger42",datapair "mixamorig:LeftShoulder" "Bip001 L Clavicle",datapair "mixamorig:LeftArm" "Bip001 L UpperArm",datapair "mixamorig:LeftForeArm" "Bip001 L Forearm",datapair "mixamorig:LeftHand" "Bip001 L Hand",datapair "mixamorig:LeftHandThumb1" "Bip001 L Finger0",datapair "mixamorig:LeftHandThumb2" "Bip001 L Finger01",datapair "mixamorig:LeftHandThumb3" "Bip001 L Finger02",datapair "mixamorig:LeftHandIndex1" "Bip001 L Finger1",datapair "mixamorig:LeftHandIndex2" "Bip001 L Finger11",datapair "mixamorig:LeftHandIndex3" "Bip001 L Finger12",datapair "mixamorig:LeftHandMiddle1" "Bip001 L Finger2",datapair "mixamorig:LeftHandMiddle2" "Bip001 L Finger21",datapair "mixamorig:LeftHandMiddle3" "Bip001 L Finger22",datapair "mixamorig:LeftHandRing1" "Bip001 L Finger3",datapair "mixamorig:LeftHandRing2" "Bip001 L Finger31",datapair "mixamorig:LeftHandRing3" "Bip001 L Finger32",datapair "mixamorig:LeftHandPinky1" "Bip001 L Finger4",datapair "mixamorig:LeftHandPinky2" "Bip001 L Finger41",datapair "mixamorig:LeftHandPinky3" "Bip001 L Finger42",datapair "mixamorig:RightUpLeg" "Bip001 R Thigh",datapair "mixamorig:RightLeg" "Bip001 R Calf",datapair "mixamorig:RightFoot" "Bip001 R Foot",datapair "mixamorig:RightToeBase" "Bip001 R Toe0",datapair "mixamorig:LeftUpLeg" "Bip001 L Thigh",datapair "mixamorig:LeftLeg" "Bip001 L Calf",datapair "mixamorig:LeftFoot" "Bip001 L Foot",datapair "mixamorig:LeftToeBase" "Bip001 L Toe0")
-- 제외할 본들 (원본 방식)
-- global excludeBones =#("mixamorig:HeadTop_End","mixamorig:RightHandThumb4","mixamorig:RightHandIndex4","mixamorig:RightHandMiddle4","mixamorig:RightHandRing4",
-- "mixamorig:RightHandPinky4","mixamorig:LeftHandThumb4","mixamorig:LeftHandIndex4","mixamorig:LeftHandMiddle4","mixamorig:LeftHandRing4","mixamorig:LeftHandPinky4",
-- "mixamorig:RightToe_End","mixamorig:LeftToe_End")
global excludeBones =#("mixamorig:HeadTop_End")
-- 스파인 세그먼트 개수
global spineSegments = 2
-- 특수 스켈레톤 구조 감지 변수들
global legParentIsSpine = false
global shoulderParentIsNeck = false
global shoulderParentIsSpine = false
-- 발 각도 설정
global footDegree = 50
global flipThreshold = 178
-- ================================== 유틸리티 함수들 (원본 그대로) ===============================================
fn pointLineProj pA pB pC = (
local vAB=pB-pA
local vAC=pC-pA
local d=dot (normalize vAB) (normalize vAC)
(pA+(vAB*(d*(length vAC/length vAB))))
)
fn findFootAngle =(
local foots = $'mixamorig:*Foot' as array
if foots.count==0 do return 35
local posArr = for i in foots collect i.pos[3]
local idx = findItem posArr (amin(posArr))
local foot = foots[idx]
local toe = foot.children[1]
if not isvalidnode toe do return 35
local p1 = foot.pos
local p2 = toe.pos
local v1 = p1-p2
p2[3] = p1[3]
local v2 = p1-p2
acos(dot(normalize v1) (normalize v2))
)
fn getHierarchy objArr out=
(
for i in objArr do
(
appendifunique out i
child = i.children
if child.count>0 do
(
getHierarchy child out
)
)
out
)
fn getSkinnedObj = (
skn = getClassInstances skin
objArr = #()
for i in skn do(
for obj in refs.dependentNodes i do appendIfunique objArr obj
)
objArr
)
-- 위치 기반으로 실제 본 체인인지 확인하는 함수
fn isRealBoneChain parentBone childBone tolerance:0.1 =(
if not isValidNode parentBone or not isValidNode childBone then return false
-- 부모 본의 끝점 계산 (자식이 있으면 자식 방향, 없으면 부모 방향의 반대)
local parentEnd = parentBone.pos
if parentBone.children.count > 0 then (
-- 가장 가까운 자식 본 방향으로 본 길이 계산
local closestChild = parentBone.children[1]
local minDist = distance parentBone.pos closestChild.pos
for child in parentBone.children do (
local dist = distance parentBone.pos child.pos
if dist < minDist do (
minDist = dist
closestChild = child
)
)
parentEnd = closestChild.pos
) else if parentBone.parent != undefined then (
-- 부모가 있으면 부모->현재 방향으로 연장
local direction = normalize (parentBone.pos - parentBone.parent.pos)
local boneLength = distance parentBone.parent.pos parentBone.pos
parentEnd = parentBone.pos + direction * boneLength
)
-- 자식 본의 시작점과 부모 본의 끝점 거리 확인
local dist = distance parentEnd childBone.pos
return dist <= tolerance
)
fn ScanBranchHRC obj out:#() = (
local child = obj.children
if child.count > 0 then (
-- 실제 본 체인으로 연결된 자식 찾기 (위치 기반)
local chainChild = undefined
for c in child do (
if isRealBoneChain obj c then (
chainChild = c
exit -- 첫 번째로 체인 연결된 자식만 사용
)
)
if chainChild != undefined then (
-- 체인으로 연결된 자식이 있으면 계속 추적
append out chainChild
ScanBranchHRC chainChild out:out
) else (
-- 체인이 끊어지면 undefined 추가
append out undefined
)
-- 체인에 포함되지 않은 나머지 자식들은 별도 처리
for i in child where i != chainChild do (
append out i
ScanBranchHRC i out:out
)
) else (
append out undefined
)
out
)
fn getBranchHRC obj =(
local scan = ScanBranchHRC obj
local out = #()
local new = undefined
for i in scan do (
if new == undefined do new = #()
if isvalidNode i then (
append new i
) else (
if new != undefined and new.count > 0 do append out new
new = undefined
)
)
-- 마지막 그룹 추가
if new != undefined and new.count > 0 do append out new
out
)
fn collectLayerNodes ly out:#()=(
ly.nodes &nd
join out nd
local numchildLy =ly.getNumChildren()
if numchildLy>0 do(
for i=1 to numchildLy do(
local newLy = ly.getChild i
collectLayerNodes newLy out:out
)
)
out
)
fn CleanNames =(
local objArr = $mixamorig*
--Rules 1
for i in objArr do(
n = i.name
filt = filterstring n ":"
if filt.Count>1 do(
newName = "mixamorig:"
for j=2 to filt.Count do (
newName += filt[j]
if j!=filt.Count do newName+=":"
)
i.name = newName
)
)
)
fn SanityCheck = (
hips = $'*:Hips*'
if hips.count>0 then(
hrc = getHierarchy hips #()
for obj in hrc do(
case of (
(matchpattern obj.name pattern:"*Hips"): obj.name = "mixamorig:Hips"
(matchpattern obj.name pattern:"*Spine"): obj.name = "mixamorig:Spine"
(matchpattern obj.name pattern:"*Spine1"): obj.name = "mixamorig:Spine1"
(matchpattern obj.name pattern:"*Neck"): obj.name = "mixamorig:Neck"
(matchpattern obj.name pattern:"*Head"): obj.name = "mixamorig:Head"
(matchpattern obj.name pattern:"*LeftShoulder"): obj.name = "mixamorig:LeftShoulder"
(matchpattern obj.name pattern:"*LeftArm"): obj.name = "mixamorig:LeftArm"
(matchpattern obj.name pattern:"*LeftForeArm"): obj.name = "mixamorig:LeftForeArm"
(matchpattern obj.name pattern:"*LeftHand"): obj.name = "mixamorig:LeftHand"
(matchpattern obj.name pattern:"*LeftHandThumb1"): obj.name = "mixamorig:LeftHandThumb1"
(matchpattern obj.name pattern:"*LeftHandThumb2"): obj.name = "mixamorig:LeftHandThumb2"
(matchpattern obj.name pattern:"*LeftHandThumb3"): obj.name = "mixamorig:LeftHandThumb3"
-- LeftHandThumb4 제거됨
(matchpattern obj.name pattern:"*LeftHandIndex1"): obj.name = "mixamorig:LeftHandIndex1"
(matchpattern obj.name pattern:"*LeftHandIndex2"): obj.name = "mixamorig:LeftHandIndex2"
(matchpattern obj.name pattern:"*LeftHandIndex3"): obj.name = "mixamorig:LeftHandIndex3"
-- LeftHandIndex4 제거됨
(matchpattern obj.name pattern:"*LeftHandMiddle1"): obj.name = "mixamorig:LeftHandMiddle1"
(matchpattern obj.name pattern:"*LeftHandMiddle2"): obj.name = "mixamorig:LeftHandMiddle2"
(matchpattern obj.name pattern:"*LeftHandMiddle3"): obj.name = "mixamorig:LeftHandMiddle3"
-- LeftHandMiddle4 제거됨
(matchpattern obj.name pattern:"*LeftHandRing1"): obj.name = "mixamorig:LeftHandRing1"
(matchpattern obj.name pattern:"*LeftHandRing2"): obj.name = "mixamorig:LeftHandRing2"
(matchpattern obj.name pattern:"*LeftHandRing3"): obj.name = "mixamorig:LeftHandRing3"
-- LeftHandRing4 제거됨
(matchpattern obj.name pattern:"*LeftHandPinky1"): obj.name = "mixamorig:LeftHandPinky1"
(matchpattern obj.name pattern:"*LeftHandPinky2"): obj.name = "mixamorig:LeftHandPinky2"
(matchpattern obj.name pattern:"*LeftHandPinky3"): obj.name = "mixamorig:LeftHandPinky3"
-- LeftHandPinky4 제거됨
(matchpattern obj.name pattern:"*RightShoulder"): obj.name = "mixamorig:RightShoulder"
(matchpattern obj.name pattern:"*RightArm"): obj.name = "mixamorig:RightArm"
(matchpattern obj.name pattern:"*RightForeArm"): obj.name = "mixamorig:RightForeArm"
(matchpattern obj.name pattern:"*RightHand"): obj.name = "mixamorig:RightHand"
(matchpattern obj.name pattern:"*RightHandThumb1"): obj.name = "mixamorig:RightHandThumb1"
(matchpattern obj.name pattern:"*RightHandThumb2"): obj.name = "mixamorig:RightHandThumb2"
(matchpattern obj.name pattern:"*RightHandThumb3"): obj.name = "mixamorig:RightHandThumb3"
-- RightHandThumb4 제거됨
(matchpattern obj.name pattern:"*RightHandIndex1"): obj.name = "mixamorig:RightHandIndex1"
(matchpattern obj.name pattern:"*RightHandIndex2"): obj.name = "mixamorig:RightHandIndex2"
(matchpattern obj.name pattern:"*RightHandIndex3"): obj.name = "mixamorig:RightHandIndex3"
-- RightHandIndex4 제거됨
(matchpattern obj.name pattern:"*RightHandMiddle1"): obj.name = "mixamorig:RightHandMiddle1"
(matchpattern obj.name pattern:"*RightHandMiddle2"): obj.name = "mixamorig:RightHandMiddle2"
(matchpattern obj.name pattern:"*RightHandMiddle3"): obj.name = "mixamorig:RightHandMiddle3"
-- RightHandMiddle4 제거됨
(matchpattern obj.name pattern:"*RightHandRing1"): obj.name = "mixamorig:RightHandRing1"
(matchpattern obj.name pattern:"*RightHandRing2"): obj.name = "mixamorig:RightHandRing2"
(matchpattern obj.name pattern:"*RightHandRing3"): obj.name = "mixamorig:RightHandRing3"
-- RightHandRing4 제거됨
(matchpattern obj.name pattern:"*RightHandPinky1"): obj.name = "mixamorig:RightHandPinky1"
(matchpattern obj.name pattern:"*RightHandPinky2"): obj.name = "mixamorig:RightHandPinky2"
(matchpattern obj.name pattern:"*RightHandPinky3"): obj.name = "mixamorig:RightHandPinky3"
-- RightHandPinky4 제거됨
(matchpattern obj.name pattern:"*LeftUpLeg"): obj.name = "mixamorig:LeftUpLeg"
(matchpattern obj.name pattern:"*LeftLeg"): obj.name = "mixamorig:LeftLeg"
(matchpattern obj.name pattern:"*LeftFoot"): obj.name = "mixamorig:LeftFoot"
(matchpattern obj.name pattern:"*LeftToeBase"): obj.name = "mixamorig:LeftToeBase"
-- LeftToe_End 제거됨
(matchpattern obj.name pattern:"*RightUpLeg"): obj.name = "mixamorig:RightUpLeg"
(matchpattern obj.name pattern:"*RightLeg"): obj.name = "mixamorig:RightLeg"
(matchpattern obj.name pattern:"*RightFoot"): obj.name = "mixamorig:RightFoot"
(matchpattern obj.name pattern:"*RightToeBase"): obj.name = "mixamorig:RightToeBase"
-- RightToe_End 제거됨
)
)
local valid = true
allObjectNames = for i in objects collect i.name
for i in MXNames do (
-- excludeBones에 있는 본들은 체크하지 않음
if findItem excludeBones i == 0 then (
if findItem allObjectNames i==0 do (
valid = false
format "[ERROR] : $% not found!\n" i
)
)
)
valid = true
return valid
)
return false
)
fn createBip height spine:2 fingers:5= (
-- Triangle Pelvis는 False, Triangle Neck는 True로 설정
local bipObj = biped.createNew height -90 [0,0,height*0.9562] arms:true neckLinks:1 \
spineLinks:spine legLinks:3 \
fingers:fingers fingerLinks:3 toes:1 \
toeLinks:1 ankleAttach:0.3 trianglePelvis:False triangleNeck:True
bipObj
)
fn getRigStructures =(
local n = "mixamorig:"
local root = execute ("$'"+n+"Hips'")
-- 다리 본들 (부모 관계 확인)
local LLeg1 = execute ("$'"+n+"LeftUpLeg'")
local LLeg2 = execute ("$'"+n+"LeftLeg'")
local LFoot = execute ("$'"+n+"LeftFoot'")
local LToe = execute ("$'"+n+"LeftToeBase'")
local RLeg1 = execute ("$'"+n+"RightUpLeg'")
local RLeg2 = execute ("$'"+n+"RightLeg'")
local RFoot = execute ("$'"+n+"RightFoot'")
local RToe = execute ("$'"+n+"RightToeBase'")
-- 스파인 본들
local LSpine = execute ("$'"+n+"Spine'")
local LSpine1 = execute ("$'"+n+"Spine1'")
local spines = #(LSpine, LSpine1)
spineSegments = 2
-- 다리가 Spine에서 시작하는 경우 처리
-- 다리 본들의 부모가 Spine인지 확인
legParentIsSpine = false
if LLeg1 != undefined and LLeg1.parent != undefined then (
if findString LLeg1.parent.name "Spine" != undefined then (
legParentIsSpine = true
format "다리가 Spine에서 시작됨을 감지\\n"
)
)
-- 어깨/팔 본들
local LClav = execute ("$'"+n+"LeftShoulder'")
local LArm1 = execute ("$'"+n+"LeftArm'")
local LArm2 = execute ("$'"+n+"LeftForeArm'")
local LHand = execute ("$'"+n+"LeftHand'")
local RClav = execute ("$'"+n+"RightShoulder'")
local RArm1 = execute ("$'"+n+"RightArm'")
local RArm2 = execute ("$'"+n+"RightForeArm'")
local RHand = execute ("$'"+n+"RightHand'")
-- 🔍 팔 구조 디버그 출력
format "\\n=== 팔 구조 디버그 ===\\n"
if RClav != undefined then (
format "RightShoulder: %\\n" RClav.name
format " 자식들: "
for child in RClav.children do format "%, " child.name
format "\\n"
)
if RArm1 != undefined then (
format "RightArm: %\\n" RArm1.name
format " 자식들: "
for child in RArm1.children do format "%, " child.name
format "\\n"
)
if RArm2 != undefined then (
format "RightForeArm: %\\n" RArm2.name
format " 자식들: "
for child in RArm2.children do format "%, " child.name
format "\\n"
)
format "========================\\n"
local neck = execute ("$'"+n+"Neck'")
local head = execute ("$'"+n+"Head'")
-- 어깨가 어디서 시작하는지 확인
shoulderParentIsNeck = false
shoulderParentIsSpine = false
if LClav != undefined and LClav.parent != undefined then (
if findString LClav.parent.name "Neck" != undefined then (
shoulderParentIsNeck = true
format "어깨가 Neck에서 시작됨을 감지\\n"
) else if findString LClav.parent.name "Spine" != undefined and
findString LClav.parent.name "Spine1" == undefined then (
shoulderParentIsSpine = true
format "어깨가 Spine에서 바로 시작됨을 감지\\n"
)
)
local LFinger1 = $'mixamorig:LeftHandThumb1'
local LFinger2 = $'mixamorig:LeftHandIndex1'
local LFinger3 = $'mixamorig:LeftHandMiddle1'
local LFinger4 = $'mixamorig:LeftHandRing1'
local LFinger5 = $'mixamorig:LeftHandPinky1'
local RFinger1 = $'mixamorig:RightHandThumb1'
local RFinger2 = $'mixamorig:RightHandIndex1'
local RFinger3 = $'mixamorig:RightHandMiddle1'
local RFinger4 = $'mixamorig:RightHandRing1'
local RFinger5 = $'mixamorig:RightHandPinky1'
-- 스켈레톤 구조에 따라 적응적으로 배치
if (legParentIsSpine and shoulderParentIsNeck) or (shoulderParentIsSpine) then (
-- 특수 스켈레톤 구조들
if shoulderParentIsSpine then (
format "특수 스켈레톤 구조 감지: 어깨->Spine\\n"
) else (
format "특수 스켈레톤 구조 감지: 다리->Spine, 어깨->Neck\\n"
)
rigNodes = #(root,root)
join rigNodes #(LLeg1, LLeg2, LFoot, LToe, RLeg1, RLeg2, RFoot, RToe) -- 다리들 먼저
join rigNodes Spines -- 스파인들
join rigNodes #(neck, head) -- 목과 머리
join rigNodes #(LClav, LArm1, LArm2, LHand, RClav, RArm1, RArm2, RHand) -- 어깨와 팔들
join rigNodes #(LFinger1,LFinger2,LFinger3,LFinger4,LFinger5, RFinger1,RFinger2,RFinger3,RFinger4,RFinger5)
) else (
-- 표준 구조
rigNodes = #(root,root)
join rigNodes Spines
join rigNodes #( LClav,
LArm1,
LArm2,
LHand,
RClav,
RArm1,
RArm2,
RHand,
neck,
head,
LLeg1,
LLeg2,
LFoot,
LToe,
RLeg1,
RLeg2,
RFoot,
RToe,
LFinger1,LFinger2,LFinger3,LFinger4,LFinger5,
RFinger1,RFinger2,RFinger3,RFinger4,RFinger5
)
)
rigNodes
)
fn GetBipStructure bip spinesegs:3= (
local bip_pelvis = biped.getNode bip #pelvis link:1
local bip_root = bip_pelvis.parent
local bip_LLeg1 = biped.getNode bip #lleg link:1
local bip_LLeg2 = biped.getNode bip #lleg link:2
local bip_LFoot = biped.getNode bip #lleg link:3
local bip_LToe = biped.getNode bip #ltoes link:1
local bip_RLeg1 = biped.getNode bip #rleg link:1
local bip_RLeg2 = biped.getNode bip #rleg link:2
local bip_RFoot = biped.getNode bip #rleg link:3
local bip_RToe = biped.getNode bip #rtoes link:1
local bip_LClav = biped.getNode bip #larm link:1
local bip_LArm1 = biped.getNode bip #larm link:2
local bip_LArm2 = biped.getNode bip #larm link:3
local bip_LHand = biped.getNode bip #larm link:4
local bip_RClav = biped.getNode bip #rarm link:1
local bip_RArm1 = biped.getNode bip #rarm link:2
local bip_RArm2 = biped.getNode bip #rarm link:3
local bip_RHand = biped.getNode bip #rarm link:4
local bip_neck = biped.getNode bip #neck link:1
local bip_head = biped.getNode bip #head link:1
local bip_Lfinger0 = biped.getNode bip #lfingers link:1
local bip_Lfinger1 = biped.getNode bip #lfingers link:4
local bip_Lfinger2 = biped.getNode bip #lfingers link:7
local bip_Lfinger3 = biped.getNode bip #lfingers link:10
local bip_Lfinger4 = biped.getNode bip #lfingers link:13
local bip_Rfinger0 = biped.getNode bip #rfingers link:1
local bip_Rfinger1 = biped.getNode bip #rfingers link:4
local bip_Rfinger2 = biped.getNode bip #rfingers link:7
local bip_Rfinger3 = biped.getNode bip #rfingers link:10
local bip_Rfinger4 = biped.getNode bip #rfingers link:13
-- 스켈레톤 구조에 따라 적응적으로 배치 (getRigStructures와 동일 순서)
if (legParentIsSpine and shoulderParentIsNeck) or (shoulderParentIsSpine) then (
-- 특수 구조들
format "Biped도 특수 구조로 배치\\n"
bipNodes = #(bip_root,bip_pelvis)
-- 다리들 먼저
join bipNodes #(bip_LLeg1, bip_LLeg2, bip_LFoot, bip_LToe, bip_RLeg1, bip_RLeg2, bip_RFoot, bip_RToe)
-- 스파인들
for i=1 to spinesegs do append bipNodes (biped.getNode bip #spine link:i)
-- 목과 머리
join bipNodes #(bip_neck, bip_head)
-- 어깨와 팔들
join bipNodes #(bip_LClav, bip_LArm1, bip_LArm2, bip_LHand, bip_RClav, bip_RArm1, bip_RArm2, bip_RHand)
-- 손가락들
join bipNodes #(bip_Lfinger0,bip_Lfinger1,bip_Lfinger2,bip_Lfinger3,bip_Lfinger4, bip_Rfinger0,bip_Rfinger1,bip_Rfinger2,bip_Rfinger3,bip_Rfinger4)
) else (
-- 표준 구조
bipNodes = #(bip_root,bip_pelvis)
--SPINES
for i=1 to spinesegs do append bipNodes (biped.getNode bip #spine link:i)
join bipNodes #(bip_LClav,
bip_LArm1,
bip_LArm2,
bip_LHand,
bip_RClav,
bip_RArm1,
bip_RArm2,
bip_RHand,
bip_neck,
bip_head,
bip_LLeg1,
bip_LLeg2,
bip_LFoot,
bip_LToe,
bip_RLeg1,
bip_RLeg2,
bip_RFoot,
bip_RToe,
bip_Lfinger0,bip_Lfinger1,bip_Lfinger2,bip_Lfinger3,bip_Lfinger4,
bip_Rfinger0,bip_Rfinger1,bip_Rfinger2,bip_Rfinger3,bip_Rfinger4
)
)
bipNodes
)
fn MapTMAxis sourceTM targetTM indexes mult:[1,1,1]=(
axis = #(sourceTM.row1,sourceTM.row2,sourceTM.row3)
targetTM.row1 = axis[indexes[1]]*mult[1]
targetTM.row2 = axis[indexes[2]]*mult[2]
targetTM.row3 = axis[indexes[3]]*mult[3]
targetTM
)
fn SetClavicleTM bipCtl bipClav TM =(
local figMode = bipCtl.figuremode
bipCtl.figuremode = false -- FORCE EXIT FIGURE MODE
biped.setTransform bipClav #rotation TM.rotationPart false
-- COPY POSTURE
select bipClav
local col = biped.createCopyCollection bipCtl "Shoulder"
local cpy = biped.copyBipPosture bipCtl col (selection as array) #snapAuto
-- PASTE POSTURE ON FIGURE MODE
bipCtl.figuremode = true
biped.pasteBipPosture bipCtl cpy false #pstcopied false false false false
biped.deleteAllCopyCollections bipCtl
biped.setTransform bipClav #pos TM.row4 false
clearSelection()
bipCtl.figuremode = figMode
)
fn alignFootTpose rigObj BipObj =(
local RigTM = rigObj.transform
local endPoint = rigObj.children[1].pos
local height = abs (endPoint[3]-RigTM.row4[3])
endPoint[3] = RigTM.row4[3]
local v = endPoint-RigTM.row4
local len = length v
local y = normalize(v)
local x = [0,0,-1]
local z = normalize(cross x y)
local TM = Matrix3 x y z RigTM.row4
-- #SET ROTATION
biped.setTransform BipObj #rotation TM.rotationpart false
biped.setTransform bipObj #scale [height,len*1.25,len*0.6] false
biped.setTransform BipObj #pos RigTM.row4 false
)
fn alignFoot rigObj BipObj figureMode:true degree:footDegree =(
local isSetKey = not figureMode
local RigTM = rigObj.transform
local bipTM = rotateYmatrix (90)* RigTM
local bipTM = rotateZmatrix degree*bipTM
-- #SET ROTATION
biped.setTransform BipObj #rotation bipTM.rotationpart isSetKey
biped.setTransform BipObj #pos RigTM.row4 isSetKey
-- SET SCALE (ONLY IN FIGURE MODE)
if figureMode do(
local toeTM = copy rigTM
local p1 = rigObj.children[1].pos
toeTM.row4 = p1
local v = rigTM.row2*-1
local p2 = rigTM.row4*inverse toeTM
local rotTM = RotateXMatrix degree*toeTM
p2 *= rotTM
local v = p2-p1
local p3 = pointLineProj p2 p1 RigTM.row4
local len = (length v )
local height = length (p2-RigTM.row4)
biped.setTransform bipObj #scale [height,len,len*0.6] isSetKey
)
)
fn AlignArm BipUpperArm MxUpperArm side:"R" FigureMode:false TPose:false =
(
local createKey = not FigureMode
local BipLowerArm = BipUpperArm.children[1]
local Biphand = BipLowerArm.children[1]
local Bip_UpperTM = BipUpperArm.transform
local Bip_LowerTM = BipLowerArm.transform
local Bip_HandTM = Biphand.transform
-- 정확한 본 이름으로 찾기 (children[1] 대신)
local MxForeArm = undefined
local MxHand = undefined
if side == "R" then (
MxForeArm = execute ("$'mixamorig:RightForeArm'")
MxHand = execute ("$'mixamorig:RightHand'")
) else (
MxForeArm = execute ("$'mixamorig:LeftForeArm'")
MxHand = execute ("$'mixamorig:LeftHand'")
)
-- 백업: 이름으로 못 찾으면 children[1] 사용
if MxForeArm == undefined do MxForeArm = MxUpperArm.children[1]
if MxHand == undefined do MxHand = MxForeArm.children[1]
local MX_UpperTM = MxUpperArm.transform
local MX_ElbowTM = MxForeArm.transform
local MX_HandTM = MxHand.transform
-- SET SCALE
if FigureMode do(
len = distance MxUpperArm MxForeArm
biped.setTransform BipUpperArm #scale [len,len,len] createKey
len = distance MxForeArm MxHand
biped.setTransform BipLowerArm #scale [len,len,len] createKey
midFing = MxHand.children[3]
if isvalidNode midFing then len = distance MxHand MxHand.children[3]
else len *=0.2
biped.setTransform Biphand #scale [len,len,len] createKey
)
local mid = (MX_HandTM.row4+MX_UpperTM.row4)*0.5
local up = Normalize (mid-MX_ElbowTM.row4)
if TPose do up = [0,-1,0]
--SET HAND FIRST BECAUSE THE IK
biped.setTransform Biphand #pos MX_HandTM.row4 createKey
-- UpperArm
local x = Normalize (MX_ElbowTM.row4-Bip_UpperTM.row4)
local z = Normalize (cross up x )
local y = Normalize (cross z x)
local upperTM = matrix3 x y z MX_UpperTM.row4
-- UpperArm: 기하학적 계산은 유지, 축 변환만 제거
biped.setTransform BipUpperArm #rotation upperTM.rotationpart createKey
-- Hand: 위치만 설정, 축 변환 제거
biped.setTransform Biphand #pos MX_HandTM.row4 createKey
)
fn AlignLeg BipUpperLeg MxUpperLeg side:"R" FigureMode:false TPose:false =
(
local createKey = not FigureMode
local BipLoweLeg = BipUpperLeg.children[1]
local BipFoot = BipLoweLeg.children[1]
local MxLowerLeg = MxUpperLeg.children[1]
local MxFoot = MxLowerLeg.children[1]
local rigTM = MxUpperLeg.transform
local BipUpperTM = BipUpperLeg.transform
local KneeTM = MxLowerLeg.transform
local endTM = MxFoot.transform
if FigureMode do(
dist = distance MxUpperLeg MxLowerLeg
biped.setTransform BipUpperLeg #scale [dist,dist,dist] createKey
dist = distance MxLowerLeg MxFoot
biped.setTransform BipLoweLeg #scale [dist,dist,dist] createKey
)
local mid = (endTM.row4+rigTM.row4)*0.5
local up = Normalize (mid-KneeTM.row4)
if TPose do up = [0,1,0]
-- Foot
biped.setTransform BipFoot #pos endTM.row4 createKey
-- UpperLeg
local x = Normalize (KneeTM.row4-BipUpperTM.row4)
local z = Normalize (cross up x )
local y = Normalize (cross z x)
local newTM = matrix3 x y z rigTM.row4
-- UpperLeg: 기하학적 계산은 유지, 축 변환만 제거
biped.setTransform BipUpperLeg #rotation newTM.rotationpart createKey
)
fn alignFingers BipNode MXNode figureMode:false=(
local bip2 = BipNode.children[1]
local bip3 = bip2.children[1]
local BipNodes = #(BipNode,bip2,bip3)
local MX2 = MXNode.children[1]
local MX3 = MX2.children[1]
local MX4 = MX3.children[1]
local MXNodes = #(MXNode,MX2,MX3,MX4)
if figureMode then(
for i=1 to 3 where isvalidNode MXNodes[i] do(
-- 단순 크기 조정만 (축 변환 제거)
if isValidNode MXNodes[i+1] then len = distance MXNodes[i] MXNodes[i+1]
else len = distance MXNodes[i] MXNodes[i].parent
biped.setTransform BipNodes[i] #scale [len,len,len] false
-- 첫 번째 관절만 위치 설정
if i==1 do biped.setTransform BipNodes[i] #pos MXNodes[i].transform.row4 false
)
-- 손가락 펴기: 좌우손 엄지/다른손가락 하드코딩 처리
try (
local isThumb = (findString BipNode.name "Finger0" != undefined)
local isLeftHand = (findString BipNode.name " L " != undefined) or (findString BipNode.name "Left" != undefined)
-- === 왼손 엄지 ===
if isThumb and isLeftHand then (
if isvalidNode BipNodes[1] do
biped.setTransform BipNodes[1] #rotation (eulerAngles -90 10 -30) false
if isvalidNode BipNodes[2] do
biped.setTransform BipNodes[2] #rotation (eulerAngles -90 10 -30) false
if isvalidNode BipNodes[3] do
biped.setTransform BipNodes[3] #rotation (eulerAngles -90 10 -30) false
)
-- === 오른손 엄지 ===
else if isThumb and not isLeftHand then (
if isvalidNode BipNodes[1] do
biped.setTransform BipNodes[1] #rotation (eulerAngles -90 10 -150) false
if isvalidNode BipNodes[2] do
biped.setTransform BipNodes[2] #rotation (eulerAngles -90 10 -150) false
if isvalidNode BipNodes[3] do
biped.setTransform BipNodes[3] #rotation (eulerAngles -90 10 -150) false
)
-- === 왼손 다른 손가락들 ===
else if not isThumb and isLeftHand then (
if isvalidNode BipNodes[1] do
biped.setTransform BipNodes[1] #rotation (eulerAngles -90 0 0) false
if isvalidNode BipNodes[2] do
biped.setTransform BipNodes[2] #rotation (eulerAngles -100 0 0) false
if isvalidNode BipNodes[3] do
biped.setTransform BipNodes[3] #rotation (eulerAngles -105 0 0) false
)
-- === 오른손 다른 손가락들 ===
else if not isThumb and not isLeftHand then (
if isvalidNode BipNodes[1] do
biped.setTransform BipNodes[1] #rotation (eulerAngles -90 0 180) false
if isvalidNode BipNodes[2] do
biped.setTransform BipNodes[2] #rotation (eulerAngles -100 0 180) false
if isvalidNode BipNodes[3] do
biped.setTransform BipNodes[3] #rotation (eulerAngles -105 0 180) false
)
) catch()
)
-- Animation Mode에서는 회전 조정 안함 (축 변환 제거)
)
fn BipToRigProportion bipNodes rigNodes TPose:false =(
footDegree = findFootAngle()
matchIndexes = #{1..(bipNodes.count)}
bipctl = bipNodes[1].transform.controller
bipctl.figureMode = true
Lthigh = $'mixamorig:LeftUpLeg'
Rthigh = $'mixamorig:RightUpLeg'
p = (Lthigh.pos+Rthigh.pos)*0.5
for i in matchIndexes where isvalidnode bipNodes[i] do(
n = bipNodes[i].name
bipNode = bipNodes[i]
rigNode = rigNodes[i]
if isvalidNode bipNode and isvalidNode rigNode do(
rigNodeEnd = rigNodes[i+1]
rigTM = rigNode.transform
BipTM = bipNode.transform
case of(
(i==1):(
-- Root: 위치만 조정, 축 변환 제거
biped.setTransform bipNode #pos p false
)
(findString n "Head"!= undefined): (
rigNodeEnd = rigNode.children[1]
if isvalidNode rigNodeEnd do(
-- Head: 크기와 위치만 조정, 축 변환 제거
dist = distance rigNode rigNodeEnd
biped.setTransform bipNode #scale [dist,dist,dist] false
biped.setTransform bipNode #pos rigNode.transform.row4 false
)
)
(matchPattern n pattern:"*Neck*"):(
-- Neck: 크기만 조정, 축 변환 제거
dist = distance rigNode rigNodeEnd
biped.setTransform bipNode #scale [dist,dist,dist] false
)
(matchPattern n pattern:"*Foot*" ):(
if TPose then alignFootTpose rigNode bipNode
else alignFoot rigNode bipNode figureMode:true
)
(matchPattern n pattern:"*Toe*"):(
rigNodeEnd = rigNode.children[1]
if isvalidNode rigNodeEnd do(
-- Toe: 크기와 위치만 조정, 축 변환 제거
dist = distance rigNode rigNodeEnd
biped.setTransform bipNode #scale [dist,dist*0.2,dist] false
biped.setTransform bipNode #pos rigNode.pos false
)
)
(findString n "Pelvis"!= undefined ):(
rigNode = $'mixamorig:LeftUpLeg'
rigNodeEnd = $'mixamorig:RightUpLeg'
dist = distance Lthigh Rthigh
len = distance rigNode rigNodeEnd
biped.setTransform bipNode #scale [dist,dist,dist] false
)
(MatchPattern n pattern:"*Spine*"):(
-- 스파인 2개 구조에서 Spine1이 마지막 스파인
if findString n "Spine1"!=undefined do (
rigNodeEnd =$'mixamorig:Neck'
)
if isvalidNode rigNode and isvalidNode rigNodeEnd do(
len = distance rigNode rigNodeEnd
biped.setTransform bipNode #scale [len,len,len] false
)
if matchpattern n pattern:"*Spine*" do(
-- Spine: 위치만 조정, 축 변환 제거
biped.setTransform bipNode #pos rigNode.pos false
)
)
(matchPattern n pattern:"*Clavicle*"):(
-- 정확한 Arm 본 찾기 (children[1] 대신)
rigNodeEnd = undefined
if findString n "L Clavicle" != undefined or findString n "LeftShoulder" != undefined then (
rigNodeEnd = execute ("$'mixamorig:LeftArm'")
) else if findString n "R Clavicle" != undefined or findString n "RightShoulder" != undefined then (
rigNodeEnd = execute ("$'mixamorig:RightArm'")
)
-- 백업: 이름으로 못 찾으면 children[1] 사용
if rigNodeEnd == undefined do rigNodeEnd = rigNode.children[1]
len = distance rigNode rigNodeEnd
x = Normalize (rigNodeEnd.pos-rigNode.pos)
z = Normalize (Cross (bipNode.parent.transform.row2) x)
y = Normalize (cross z x)
newTM = matrix3 x y z rigTM.row4
biped.setTransform bipNode #scale [len,len,len] false
SetClavicleTM bipCtl bipNode newTM
)
(matchPattern n pattern:"*UpperArm*"):(
if MatchPattern n pattern:"* R *" then AlignArm bipNode rigNode side:"R" FigureMode:true TPose:TPose
else AlignArm bipNode rigNode side:"L" FigureMode:true TPose:TPose
)
(matchPattern n pattern:"*Thigh*"):(
if MatchPattern n pattern:"* R *" then AlignLeg bipNode rigNode side:"R" figureMode:true TPose:TPose
else AlignLeg bipNode rigNode side:"L" figureMode:true TPose:TPose
)
(MatchPattern n pattern:"*Finger*"): alignFingers bipNode rigNode figureMode:true
)
)
)
bipctl.figureMode = false
)
fn getNumFingers = (
local counts = #(0,0)
local lHand = (getNodeByName "mixamorig:LeftHand")
local rHand = (getNodeByName "mixamorig:RightHand")
if isValidnode lHand do counts[1] = lHand.children.count
if isValidnode rHand do counts[2] = rHand.children.count
amax counts
)
fn GetExtras root=(
local out = #()
if root!=undefined do(
hrc = getBranchHRC root
for h=hrc.count to 1 by -1 do (
local objArr = hrc[h]
for j=objArr.count to 1 by -1 do(
if (finditem MXNames objArr[j].name)!=0 do deleteitem objArr j -- REMOVE STANDARD FROM EXTRAS
)
if objArr.count==0 do deleteItem hrc h
)
out = hrc
)
out
)
fn cleanUpSkin obj =(
local skn = obj.modifiers["skin"]
local count = skinOps.GetNumberBones skn
local IDList = #()
for i=1 to count do(
n = (skinOps.GetBoneName skn i 0)
if findItem excludeBones n!=0 do(
append IDList i
)
)
if IDList.count>0 do for i=IDList.count to 1 by -1 do(
skinOps.removebone skn IDList[i]
)
)
fn GetSkinPairs obj extra:#() =(
local out=#()
noPairs = #()
local extraV1 = for i in extra collect i.v1
local skn = obj.modifiers[#skin]
local count = skinOps.GetNumberBones skn
for i=1 to count do(
foundPairs = true
n = (skinOps.GetBoneName skn i 0)
dp = datapair n ""
case of(
(matchpattern n pattern:"*Hips"): dp.v2 = "Bip001 Pelvis"
(matchpattern n pattern:"*Spine"): dp.v2 = "Bip001 Spine"
(matchpattern n pattern:"*Spine1"): dp.v2 = "Bip001 Spine1"
(matchpattern n pattern:"*Spine2"): dp.v2 = "Bip001 Spine2"
(matchpattern n pattern:"*Neck"): dp.v2 = "Bip001 Neck"
(matchpattern n pattern:"*Head"): dp.v2 = "Bip001 Head"
(matchpattern n pattern:"*LeftShoulder"): dp.v2 = "Bip001 L Clavicle"
(matchpattern n pattern:"*LeftArm"): dp.v2 = "Bip001 L UpperArm"
(matchpattern n pattern:"*LeftForeArm"): dp.v2 = "Bip001 L Forearm"
(matchpattern n pattern:"*LeftHand"): dp.v2 = "Bip001 L Hand"
(matchpattern n pattern:"*LeftHandThumb1"): dp.v2 = "Bip001 L Finger0"
(matchpattern n pattern:"*LeftHandThumb2"): dp.v2 = "Bip001 L Finger01"
(matchpattern n pattern:"*LeftHandThumb3"): dp.v2 = "Bip001 L Finger02"
(matchpattern n pattern:"*LeftHandThumb4"): dp.v2 = "Bip001 L Finger0Nub"
(matchpattern n pattern:"*LeftHandIndex1"): dp.v2 = "Bip001 L Finger1"
(matchpattern n pattern:"*LeftHandIndex2"): dp.v2 = "Bip001 L Finger11"
(matchpattern n pattern:"*LeftHandIndex3"): dp.v2 = "Bip001 L Finger12"
(matchpattern n pattern:"*LeftHandIndex4"): dp.v2 = "Bip001 L Finger1Nub"
(matchpattern n pattern:"*LeftHandMiddle1"): dp.v2 = "Bip001 L Finger2"
(matchpattern n pattern:"*LeftHandMiddle2"): dp.v2 = "Bip001 L Finger21"
(matchpattern n pattern:"*LeftHandMiddle3"): dp.v2 = "Bip001 L Finger22"
(matchpattern n pattern:"*LeftHandMiddle4"): dp.v2 = "Bip001 L Finger2Nub"
(matchpattern n pattern:"*LeftHandRing1"): dp.v2 = "Bip001 L Finger3"
(matchpattern n pattern:"*LeftHandRing2"): dp.v2 = "Bip001 L Finger31"
(matchpattern n pattern:"*LeftHandRing3"): dp.v2 = "Bip001 L Finger32"
(matchpattern n pattern:"*LeftHandRing4"): dp.v2 = "Bip001 L Finger3Nub"
(matchpattern n pattern:"*LeftHandPinky1"): dp.v2 = "Bip001 L Finger4"
(matchpattern n pattern:"*LeftHandPinky2"): dp.v2 = "Bip001 L Finger41"
(matchpattern n pattern:"*LeftHandPinky3"): dp.v2 = "Bip001 L Finger42"
(matchpattern n pattern:"*LeftHandPinky4"): dp.v2 = "Bip001 L Finger4Nub"
(matchpattern n pattern:"*RightShoulder"): dp.v2 = "Bip001 R Clavicle"
(matchpattern n pattern:"*RightArm"): dp.v2 = "Bip001 R UpperArm"
(matchpattern n pattern:"*RightForeArm"): dp.v2 = "Bip001 R Forearm"
(matchpattern n pattern:"*RightHand"): dp.v2 = "Bip001 R Hand"
(matchpattern n pattern:"*RightHandThumb1"): dp.v2 = "Bip001 R Finger0"
(matchpattern n pattern:"*RightHandThumb2"): dp.v2 = "Bip001 R Finger01"
(matchpattern n pattern:"*RightHandThumb3"): dp.v2 = "Bip001 R Finger02"
(matchpattern n pattern:"*RightHandThumb4"): dp.v2 = "Bip001 R Finger0Nub"
(matchpattern n pattern:"*RightHandIndex1"): dp.v2 = "Bip001 R Finger1"
(matchpattern n pattern:"*RightHandIndex2"): dp.v2 = "Bip001 R Finger11"
(matchpattern n pattern:"*RightHandIndex3"): dp.v2 = "Bip001 R Finger12"
(matchpattern n pattern:"*RightHandIndex4"): dp.v2 = "Bip001 R Finger1Nub"
(matchpattern n pattern:"*RightHandMiddle1"): dp.v2 = "Bip001 R Finger2"
(matchpattern n pattern:"*RightHandMiddle2"): dp.v2 = "Bip001 R Finger21"
(matchpattern n pattern:"*RightHandMiddle3"): dp.v2 = "Bip001 R Finger22"
(matchpattern n pattern:"*RightHandMiddle4"): dp.v2 = "Bip001 R Finger2Nub"
(matchpattern n pattern:"*RightHandRing1"): dp.v2 = "Bip001 R Finger3"
(matchpattern n pattern:"*RightHandRing2"): dp.v2 = "Bip001 R Finger31"
(matchpattern n pattern:"*RightHandRing3"): dp.v2 = "Bip001 R Finger32"
(matchpattern n pattern:"*RightHandRing4"): dp.v2 = "Bip001 R Finger3Nub"
(matchpattern n pattern:"*RightHandPinky1"): dp.v2 = "Bip001 R Finger4"
(matchpattern n pattern:"*RightHandPinky2"): dp.v2 = "Bip001 R Finger41"
(matchpattern n pattern:"*RightHandPinky3"): dp.v2 = "Bip001 R Finger42"
(matchpattern n pattern:"*RightHandPinky4"): dp.v2 = "Bip001 R Finger4Nub"
(matchpattern n pattern:"*LeftUpLeg"): dp.v2 = "Bip001 L Thigh"
(matchpattern n pattern:"*LeftLeg"): dp.v2 = "Bip001 L Calf"
(matchpattern n pattern:"*LeftFoot"): dp.v2 = "Bip001 L Foot"
(matchpattern n pattern:"*LeftToeBase"): dp.v2 = "Bip001 L Toe0"
(matchpattern n pattern:"*RightUpLeg"): dp.v2 = "Bip001 R Thigh"
(matchpattern n pattern:"*RightLeg"): dp.v2 = "Bip001 R Calf"
(matchpattern n pattern:"*RightFoot"): dp.v2 = "Bip001 R Foot"
(matchpattern n pattern:"*RightToeBase"): dp.v2 = "Bip001 R Toe0"
default:(foundPairs = false )
)
indexInExtra = findItem extraV1 n
if not foundPairs and indexInExtra != 0 then(
dp.v2 = extra[indexInExtra].v2
)
else if not foundPairs do append noPairs n
append out dp
)
out
)
-- ================================== 모퍼 처리 함수들 ===============================================
struct MorpherChannelData (
channelIndex,
targetName,
weight,
channelName,
isActive
)
struct MorpherBackupData (
objectName,
modifierName,
channels
)
fn BackupMorpherData obj =(
/*
모퍼 모디파이어 데이터를 백업하는 함수
매개변수:
- obj: 모퍼 모디파이어를 가진 오브젝트
반환값:
- MorpherBackupData 구조체
*/
local morphMod = obj.modifiers[#Morpher]
if morphMod == undefined do return undefined
local channels = #()
local channelCount = try (morphMod.numChannels) catch (100) -- 기본값 100개 채널
format "📦 모퍼 데이터 백업: % (채널 수: %)\\n" obj.name channelCount
for i = 1 to channelCount do (
try (
-- 채널 프로퍼티에 직접 접근
local channelProp = execute ("morphMod.morph_channel_" + i as string)
if channelProp != undefined then (
local target = try (channelProp.target) catch (undefined)
local weight = try (channelProp.value) catch (0.0)
local channelName = try (channelProp.name) catch ("")
local isActive = try (channelProp.active) catch (false)
if target != undefined and isValidNode target do (
local channelData = MorpherChannelData \
channelIndex:i \
targetName:target.name \
weight:weight \
channelName:channelName \
isActive:isActive
append channels channelData
format " 채널 %: % (가중치: %, 타겟: %)\\n" i channelName weight target.name
)
)
) catch (
-- 채널이 비어있거나 접근할 수 없는 경우 건너뛰기
format " 채널 % 건너뛰기 (빈 채널)\\n" i
)
)
MorpherBackupData objectName:obj.name modifierName:morphMod.name channels:channels
)
fn RestoreMorpherData backupData pairsList =(
/*
백업된 모퍼 데이터를 복원하는 함수
매개변수:
- backupData: 백업된 모퍼 데이터
- pairsList: 본 이름 매핑 정보
반환값:
- 성공 시 true, 실패 시 false
*/
if backupData == undefined do return false
local obj = getNodeByName backupData.objectName
if not isValidNode obj do (
format "[ERROR] 모퍼 오브젝트를 찾을 수 없음: %\\n" backupData.objectName
return false
)
local morphMod = obj.modifiers[#Morpher]
if morphMod == undefined do (
format "[ERROR] 모퍼 모디파이어를 찾을 수 없음: %\\n" obj.name
return false
)
format "🔄 모퍼 데이터 복원: % (채널 수: %)\\n" obj.name backupData.channels.count
for channelData in backupData.channels do (
-- 원본 타겟 이름을 새 이름으로 매핑
local newTargetName = channelData.targetName
for pair in pairsList do (
if pair.v1 == channelData.targetName do (
newTargetName = pair.v2
exit()
)
)
local newTarget = getNodeByName newTargetName
if isValidNode newTarget then (
try (
-- 채널 프로퍼티에 직접 접근하여 설정
local channelProp = execute ("morphMod.morph_channel_" + channelData.channelIndex as string)
if channelProp != undefined then (
-- 타겟 설정
try (channelProp.target = newTarget) catch (format " [WARNING] 타겟 설정 실패: 채널 %\\n" channelData.channelIndex)
-- 가중치 복원
try (channelProp.value = channelData.weight) catch (format " [WARNING] 가중치 설정 실패: 채널 %\\n" channelData.channelIndex)
-- 채널 활성화 상태 복원
try (channelProp.active = channelData.isActive) catch (format " [WARNING] 활성화 상태 설정 실패: 채널 %\\n" channelData.channelIndex)
-- 채널 이름 복원
try (channelProp.name = channelData.channelName) catch (format " [WARNING] 채널 이름 설정 실패: 채널 %\\n" channelData.channelIndex)
format " ✅ 채널 % 복원: % → % (가중치: %)\\n" channelData.channelIndex channelData.targetName newTargetName channelData.weight
) else (
format " ❌ 채널 % 프로퍼티 접근 실패\\n" channelData.channelIndex
)
) catch (
format " ⚠️ 채널 % 설정 실패: % → %\\n" channelData.channelIndex channelData.targetName newTargetName
)
) else (
format " ❌ 타겟을 찾을 수 없음: % → %\\n" channelData.targetName newTargetName
)
)
true
)
fn GetSkinPairs obj extra:#() =(
local out=#()
noPairs = #()
local extraV1 = for i in extra collect i.v1
local skn = obj.modifiers[#skin]
local count = skinOps.GetNumberBones skn
for i=1 to count do(
foundPairs = true
n = (skinOps.GetBoneName skn i 0)
dp = datapair n ""
case of(
(matchpattern n pattern:"*Hips"): dp.v2 = "Bip001 Pelvis"
(matchpattern n pattern:"*Spine"): dp.v2 = "Bip001 Spine"
(matchpattern n pattern:"*Spine1"): dp.v2 = "Bip001 Spine1"
(matchpattern n pattern:"*Neck"): dp.v2 = "Bip001 Neck"
(matchpattern n pattern:"*Head"): dp.v2 = "Bip001 Head"
(matchpattern n pattern:"*LeftShoulder"): dp.v2 = "Bip001 L Clavicle"
(matchpattern n pattern:"*LeftArm"): dp.v2 = "Bip001 L UpperArm"
(matchpattern n pattern:"*LeftForeArm"): dp.v2 = "Bip001 L Forearm"
(matchpattern n pattern:"*LeftHand"): dp.v2 = "Bip001 L Hand"
(matchpattern n pattern:"*LeftHandThumb1"): dp.v2 = "Bip001 L Finger0"
(matchpattern n pattern:"*LeftHandThumb2"): dp.v2 = "Bip001 L Finger01"
(matchpattern n pattern:"*LeftHandThumb3"): dp.v2 = "Bip001 L Finger02"
-- LeftHandThumb4 제거됨
(matchpattern n pattern:"*LeftHandIndex1"): dp.v2 = "Bip001 L Finger1"
(matchpattern n pattern:"*LeftHandIndex2"): dp.v2 = "Bip001 L Finger11"
(matchpattern n pattern:"*LeftHandIndex3"): dp.v2 = "Bip001 L Finger12"
-- LeftHandIndex4 제거됨
(matchpattern n pattern:"*LeftHandMiddle1"): dp.v2 = "Bip001 L Finger2"
(matchpattern n pattern:"*LeftHandMiddle2"): dp.v2 = "Bip001 L Finger21"
(matchpattern n pattern:"*LeftHandMiddle3"): dp.v2 = "Bip001 L Finger22"
-- LeftHandMiddle4 제거됨
(matchpattern n pattern:"*LeftHandRing1"): dp.v2 = "Bip001 L Finger3"
(matchpattern n pattern:"*LeftHandRing2"): dp.v2 = "Bip001 L Finger31"
(matchpattern n pattern:"*LeftHandRing3"): dp.v2 = "Bip001 L Finger32"
-- LeftHandRing4 제거됨
(matchpattern n pattern:"*LeftHandPinky1"): dp.v2 = "Bip001 L Finger4"
(matchpattern n pattern:"*LeftHandPinky2"): dp.v2 = "Bip001 L Finger41"
(matchpattern n pattern:"*LeftHandPinky3"): dp.v2 = "Bip001 L Finger42"
-- LeftHandPinky4 제거됨
(matchpattern n pattern:"*RightShoulder"): dp.v2 = "Bip001 R Clavicle"
(matchpattern n pattern:"*RightArm"): dp.v2 = "Bip001 R UpperArm"
(matchpattern n pattern:"*RightForeArm"): dp.v2 = "Bip001 R Forearm"
(matchpattern n pattern:"*RightHand"): dp.v2 = "Bip001 R Hand"
(matchpattern n pattern:"*RightHandThumb1"): dp.v2 = "Bip001 R Finger0"
(matchpattern n pattern:"*RightHandThumb2"): dp.v2 = "Bip001 R Finger01"
(matchpattern n pattern:"*RightHandThumb3"): dp.v2 = "Bip001 R Finger02"
-- RightHandThumb4 제거됨
(matchpattern n pattern:"*RightHandIndex1"): dp.v2 = "Bip001 R Finger1"
(matchpattern n pattern:"*RightHandIndex2"): dp.v2 = "Bip001 R Finger11"
(matchpattern n pattern:"*RightHandIndex3"): dp.v2 = "Bip001 R Finger12"
-- RightHandIndex4 제거됨
(matchpattern n pattern:"*RightHandMiddle1"): dp.v2 = "Bip001 R Finger2"
(matchpattern n pattern:"*RightHandMiddle2"): dp.v2 = "Bip001 R Finger21"
(matchpattern n pattern:"*RightHandMiddle3"): dp.v2 = "Bip001 R Finger22"
-- RightHandMiddle4 제거됨
(matchpattern n pattern:"*RightHandRing1"): dp.v2 = "Bip001 R Finger3"
(matchpattern n pattern:"*RightHandRing2"): dp.v2 = "Bip001 R Finger31"
(matchpattern n pattern:"*RightHandRing3"): dp.v2 = "Bip001 R Finger32"
-- RightHandRing4 제거됨
(matchpattern n pattern:"*RightHandPinky1"): dp.v2 = "Bip001 R Finger4"
(matchpattern n pattern:"*RightHandPinky2"): dp.v2 = "Bip001 R Finger41"
(matchpattern n pattern:"*RightHandPinky3"): dp.v2 = "Bip001 R Finger42"
-- RightHandPinky4 제거됨
(matchpattern n pattern:"*LeftUpLeg"): dp.v2 = "Bip001 L Thigh"
(matchpattern n pattern:"*LeftLeg"): dp.v2 = "Bip001 L Calf"
(matchpattern n pattern:"*LeftFoot"): dp.v2 = "Bip001 L Foot"
(matchpattern n pattern:"*LeftToeBase"): dp.v2 = "Bip001 L Toe0"
(matchpattern n pattern:"*RightUpLeg"): dp.v2 = "Bip001 R Thigh"
(matchpattern n pattern:"*RightLeg"): dp.v2 = "Bip001 R Calf"
(matchpattern n pattern:"*RightFoot"): dp.v2 = "Bip001 R Foot"
(matchpattern n pattern:"*RightToeBase"): dp.v2 = "Bip001 R Toe0"
default:(foundPairs = false )
)
indexInExtra = findItem extraV1 n
if not foundPairs and indexInExtra != 0 then(
dp.v2 = extra[indexInExtra].v2
)
else foundPairs = false
append out dp
if not foundPairs do append noPairs n
)
out
)
-- ================================== 메인 변환 함수 ===============================================
fn ConvertMixamoToBiped charName:"Character001" alwaysDeform:false =(
/*
Mixamo T-Pose 아바타를 Biped으로 변환하는 메인 함수
매개변수:
- charName: 캐릭터 이름 (기본값: "Character001")
- alwaysDeform: 스킨 Always Deform 설정 (기본값: false)
반환값:
- 성공 시 true, 실패 시 false
*/
if charName=="" do(
messagebox "please insert Character Name"
return false
)
-- 이름 정리 및 검증
CleanNames()
local root = getNodeByName"mixamorig:Hips"
if not isValidNode root do (
format "[ERROR] mixamorig:Hips not found!\n"
return false
)
-- 리그 유효성 검증
if not SanityCheck() do (
format "[ERROR] Sanity check failed!\n"
return false
)
local hrc = getHierarchy #(root) #()
-- 추가 본들 수집 및 메인에서 제외
local extras = GetExtras root
for i in extras do(
for j in i do(
deleteItem hrc (findItem hrc j)
)
)
-- 스킨 오브젝트들 수집
local skinObjects = for i in objects where i.modifiers[#Skin]!=undefined collect i
if alwaysDeform do for i in skinObjects do (
cleanUpSkin i
i.modifiers[#Skin].always_deform =false
i.modifiers[#Skin].always_deform =true
)
-- 모퍼 오브젝트들 수집 (간소화)
local morpherObjects = for i in objects where i.modifiers[#Morpher]!=undefined collect i
local morpherModifiers = #() -- 제거된 모퍼 모디파이어들을 저장
for obj in morpherObjects do (
-- 모퍼 모디파이어를 임시로 제거하고 저장
local morphMod = obj.modifiers[#Morpher]
append morpherModifiers (datapair obj morphMod)
deleteModifier obj morphMod
)
-- 믹사모 리그 구조 분석
local mxNodes = getRigStructures()
local mx_Obj = $mixamorig*
local height = mx_Obj.max[3]
local numfingers = getNumFingers()
-- 바이패드 생성
format "=== BIPED 생성 디버그 ===\\n"
format "spineSegments: %\\n" spineSegments
format "height: %\\n" height
format "numfingers: %\\n" numfingers
local bipObj = createBip height spine:spineSegments fingers:numfingers
-- 생성 직후 구조 확인
format "\\n=== 생성 직후 Biped 구조 ===\\n"
local pelvis = biped.getNode bipObj #pelvis link:1
local spine1 = biped.getNode bipObj #spine link:1
local spine2 = try (biped.getNode bipObj #spine link:2) catch (undefined)
local lthigh = biped.getNode bipObj #lleg link:1
local lclavicle = biped.getNode bipObj #larm link:1
local neck = biped.getNode bipObj #neck link:1
format "Pelvis: % (부모: %)\\n" pelvis.name (if pelvis.parent != undefined then pelvis.parent.name else "없음")
format "Spine1: % (부모: %)\\n" spine1.name (if spine1.parent != undefined then spine1.parent.name else "없음")
if spine2 != undefined then format "Spine2: % (부모: %)\\n" spine2.name (if spine2.parent != undefined then spine2.parent.name else "없음")
format "LThigh: % (부모: %)\\n" lthigh.name (if lthigh.parent != undefined then lthigh.parent.name else "없음")
format "LClavicle: % (부모: %)\\n" lclavicle.name (if lclavicle.parent != undefined then lclavicle.parent.name else "없음")
format "Neck: % (부모: %)\\n" neck.name (if neck.parent != undefined then neck.parent.name else "없음")
-- 🔧 잘못된 구조 수정
format "\\n=== Biped 구조 수정 중 ===\\n"
-- Figure Mode 활성화 (구조 수정 가능)
bipObj.transform.controller.figureMode = true
-- 다리 본들을 Pelvis로 이동 (현재 Spine에서 → Pelvis로)
local rthigh = biped.getNode bipObj #rleg link:1
if lthigh.parent != pelvis then (
format "다리를 Pelvis로 이동: % → %\\n" lthigh.parent.name pelvis.name
lthigh.parent = pelvis
rthigh.parent = pelvis
)
-- 어깨 본들을 최상위 Spine으로 이동 (현재 Neck에서 → Spine1로)
local rclavicle = biped.getNode bipObj #rarm link:1
local topSpine = if spine2 != undefined then spine2 else spine1 -- 최상위 Spine
if lclavicle.parent != topSpine then (
format "어깨를 최상위 Spine으로 이동: % → %\\n" lclavicle.parent.name topSpine.name
lclavicle.parent = topSpine
rclavicle.parent = topSpine
)
format "구조 수정 완료!\\n"
-- 구조 수정 후 확인
format "\\n=== 구조 수정 후 확인 ===\\n"
format "LThigh: % (부모: %)\\n" lthigh.name (if lthigh.parent != undefined then lthigh.parent.name else "없음")
format "RThigh: % (부모: %)\\n" rthigh.name (if rthigh.parent != undefined then rthigh.parent.name else "없음")
format "LClavicle: % (부모: %)\\n" lclavicle.name (if lclavicle.parent != undefined then lclavicle.parent.name else "없음")
format "RClavicle: % (부모: %)\\n" rclavicle.name (if rclavicle.parent != undefined then rclavicle.parent.name else "없음")
local bipedNodes = GetBipStructure bipObj spinesegs:spineSegments
-- 비율 매칭
BipToRigProportion bipedNodes mxNodes TPose:true
-- 비율 매칭 후 구조 재확인
format "\\n=== 비율 매칭 후 Biped 구조 ===\\n"
local pelvis_after = biped.getNode bipObj #pelvis link:1
local spine1_after = biped.getNode bipObj #spine link:1
local lthigh_after = biped.getNode bipObj #lleg link:1
local lclavicle_after = biped.getNode bipObj #larm link:1
local neck_after = biped.getNode bipObj #neck link:1
format "Pelvis: % (부모: %)\\n" pelvis_after.name (if pelvis_after.parent != undefined then pelvis_after.parent.name else "없음")
format "Spine1: % (부모: %)\\n" spine1_after.name (if spine1_after.parent != undefined then spine1_after.parent.name else "없음")
format "LThigh: % (부모: %)\\n" lthigh_after.name (if lthigh_after.parent != undefined then lthigh_after.parent.name else "없음")
format "LClavicle: % (부모: %)\\n" lclavicle_after.name (if lclavicle_after.parent != undefined then lclavicle_after.parent.name else "없음")
format "Neck: % (부모: %)\\n" neck_after.name (if neck_after.parent != undefined then neck_after.parent.name else "없음")
-- 추가 본들 생성 (선택사항)
local extrasPairs = #()
-- ========== 원본 엑스트라 본 유지 방식 ================
local totalExtraCount = 0
for ex in extras do totalExtraCount += ex.count
format "전체 Extra Bones 개수: %개 (원본 본 그대로 유지)\\n" totalExtraCount
bipObj.transform.controller.figureMode = true
local idx = 1
for ex in extras do(
for i=1 to ex.count do (
local originalBone = ex[i]
if not isValidNode originalBone do continue
-- 원본 본을 그대로 사용하되, 정리된 이름으로 매핑
local cleanName = substituteString originalBone.name "mixamorig:" ""
append extrasPairs (datapair originalBone.name cleanName)
append MXPairs (datapair originalBone.name cleanName)
format " [%] % → 원본 유지" idx originalBone.name
-- 첫 번째 본의 경우 적절한 Biped 부모에 연결
if i==1 then (
local par = originalBone.parent
for pair in MXPairs where isValidNode par and par.name==pair.v1 do (
local bipPar = getNodebyName pair.v2
if isValidNode bipPar do (
-- 원본 본을 Biped 부모에 연결
try (
originalBone.parent = bipPar
format " (부모: %)\\n" bipPar.name
) catch (
format " (부모 연결 실패: %)\\n" bipPar.name
)
exit()
)
)
if originalBone.parent == par do format " (부모 연결 안됨)\\n"
) else (
format "\\n"
)
idx += 1
)
)
bipObj.transform.controller.figureMode = false
format "\\n=== 원본 엑스트라 본 유지 완료 ===\\n"
format "- 각도/위치 완벽 보존\\n"
format "- 애니메이션 데이터 손실 없음\\n"
format "- 스키닝 품질 유지\\n"
-- Extra Bones 생성 후 최종 구조 확인
format "\\n=== Extra Bones 생성 후 최종 구조 ===\\n"
local pelvis_final = biped.getNode bipObj #pelvis link:1
local spine1_final = biped.getNode bipObj #spine link:1
local lthigh_final = biped.getNode bipObj #lleg link:1
local lclavicle_final = biped.getNode bipObj #larm link:1
local neck_final = biped.getNode bipObj #neck link:1
format "Pelvis: % (부모: %)\\n" pelvis_final.name (if pelvis_final.parent != undefined then pelvis_final.parent.name else "없음")
format "Spine1: % (부모: %)\\n" spine1_final.name (if spine1_final.parent != undefined then spine1_final.parent.name else "없음")
format "LThigh: % (부모: %)\\n" lthigh_final.name (if lthigh_final.parent != undefined then lthigh_final.parent.name else "없음")
format "LClavicle: % (부모: %)\\n" lclavicle_final.name (if lclavicle_final.parent != undefined then lclavicle_final.parent.name else "없음")
format "Neck: % (부모: %)\\n" neck_final.name (if neck_final.parent != undefined then neck_final.parent.name else "없음")
-- 하이브리드 방식: 엑스트라 본 처리 결과
if extrasPairs.count > 0 then (
format "\\n📊 Extra Bones 처리 결과 요약:\\n"
format "✅ 총 %개 엑스트라 본 처리 완료\\n" extrasPairs.count
format "✅ 모든 엑스트라 본이 원본 위치/각도 유지\\n"
format "✅ 위치 변화: 0 (원본 본 그대로 사용)\\n"
format "✅ 각도 변화: 0 (원본 각도 그대로 사용)\\n"
format "✅ 애니메이션 데이터: 완벽 보존\\n"
format "✅ 스키닝 품질: 원본 유지\\n"
format "✅ 표준 본: Biped로 대체됨\\n"
format "✅ 엑스트라 본: 원본 유지됨\\n"
)
format "================================\\n"
-- 스킨 팝업 자동 처리 함수 (강화 버전)
fn ShowPopup =(
local hwnd = (DialogMonitorOPS.GetWindowHandle() )
if hwnd!=0 then(
local windowTitle = uiAccessor.getWindowText hwnd
local children = uiAccessor.getChildWindows hwnd
-- 다이얼로그 타입별 처리
case of (
-- Load Envelopes 다이얼로그
(findString windowTitle "Load Envelopes" != undefined): (
format "Load Envelopes 다이얼로그 감지 - 자동 OK 처리\\n"
for c in children do(
local buttonText = uiAccessor.getWindowText c
if buttonText == "OK" do (
UIAccessor.PressButton c
format "OK 버튼 자동 클릭 완료\\n"
return true
)
)
)
-- Match by Name 다이얼로그
(findString windowTitle "Match by Name" != undefined): (
format "Match by Name 다이얼로그 감지 - 자동 처리\\n"
for c in children do(
if (uiAccessor.getWindowText c) =="Match by Name" do UIAccessor.PressButton c
)
)
-- 기타 다이얼로그들
default: (
-- 일반적인 OK 버튼 찾기
for c in children do(
local buttonText = uiAccessor.getWindowText c
if buttonText == "OK" do (
format "일반 OK 버튼 감지 - 자동 클릭: %\\n" windowTitle
UIAccessor.PressButton c
return true
)
)
)
)
-- 기본 버튼 처리
uiAccessor.pressDefaultButton()
true
)
else false
)
DialogMonitorOPS.Enabled = true
DialogMonitorOPS.Interactive =false
DialogMonitorOPS.RegisterNotification ShowPopup id:#MixamoToBiped
local pth = GetDir #export
-- 하이브리드 방식: 표준 본은 Biped로, 엑스트라 본은 원본으로
format "\\n=== 스킨 오브젝트 처리 (Biped + 엑스트라 본) ===\\n"
for i in skinObjects do(
format "처리 중: %\\n" i.name
local skn = i.modifiers["skin"]
local n = i.name+"_skin.env"
local f = (pth+"\\"+n)
-- 기존 스킨 데이터 저장
if skn != undefined then (
skinOps.saveEnvelope skn f
format "- 기존 스킨 데이터 저장: %\\n" f
)
-- 페어 가져오기 (표준 본 + 엑스트라 본)
local pairs = GetSkinPairs i extra:extrasPairs
format "- 매핑된 본 쌍: %개\\n" pairs.count
local bipedSkin = #()
for j in pairs do (
local obj1 = getNodeByName j.v1
local obj2 = getNodeByName j.v2
if isvalidNode obj1 and isValidNode obj2 do(
tempName = obj1.name
obj1.name = obj2.name
obj2.name = tempName
append bipedSkin obj2
)
)
-- 새 스킨 추가
local skn2 = skin()
addmodifier i skn2
select i
max modify mode
modPanel.setCurrentObject skn2
-- Biped 본들과 엑스트라 본들 추가
for j in bipedSkin do(
if j!= bipedSkin[bipedSkin.count] then skinOps.AddBone skn2 j 0
else skinOps.AddBone skn2 j -1
)
max create mode
-- 스킨 데이터 로드 (저장된 것이 있는 경우)
if doesFileExist f then (
skinOps.loadEnvelope skn2 f
format "- 스킨 데이터 로드 완료\\n"
)
maxOps.CollapseNodeTo i 2 true
-- 이름 원래대로 되돌리기
for j in pairs do (
obj1 = getNodeByName j.v1
obj2 = getNodeByName j.v2
if isvalidNode obj1 and isValidNode obj2 do(
tempName = obj1.name
obj1.name = obj2.name
obj2.name = tempName
)
)
-- 임시 파일 삭제
if doesFileExist f do deleteFile f
format "- % 처리 완료\\n" i.name
)
format "\\n✅ 모든 스킨 오브젝트 처리 완료 (Biped + 엑스트라)\\n"
-- 대화상자 모니터 비활성화
DialogMonitorOPS.Enabled = false
DialogMonitorOPS.UnRegisterNotification id:#MixamoToBiped
-- 모퍼 모디파이어 복원 (간소화)
if morpherModifiers.count > 0 do (
format "\\n🎭 모퍼 모디파이어 복원 시작...\\n"
-- 모퍼 모디파이어를 다시 추가
for modPair in morpherModifiers do (
local obj = modPair.v1
local morphMod = modPair.v2
addModifier obj morphMod
)
format "✅ 모퍼 모디파이어 복원 완료\\n"
)
-- 하이브리드 방식: 표준 본은 삭제, 엑스트라 본은 유지
format "\\n=== 믹사모 표준 본 삭제 ===\\n"
-- 표준 본들만 선별해서 삭제 (엑스트라 본 제외)
local standardBones = #()
local allMixamoBones = getHierarchy #(root) #()
for bone in allMixamoBones do (
if isValidNode bone and findItem MXNames bone.name != 0 then (
appendIfUnique standardBones bone
format "- 삭제 예정: %\\n" bone.name
)
)
-- 표준 본들 삭제
if standardBones.count > 0 then (
delete standardBones
format "✅ %개 표준 본 삭제 완료\\n" standardBones.count
)
-- 엑스트라 본 이름 정리 (mixamorig: 제거)
format "\\n=== 엑스트라 본 이름 정리 ===\\n"
for i in extrasPairs do(
local theObj = getNodeByName i.v1
if isValidNode theObj then (
theObj.name = i.v2 -- 이미 정리된 이름으로 변경
format "- % → %\\n" i.v1 i.v2
)
)
-- 레이어 설정
local rootLayer = LayerManager.newLayerFromName "CHARACTERS"
if rootLayer == undefined do rootLayer = LayerManager.getLayerFromName "CHARACTERS"
local charLayer = LayerManager.newLayerFromName (toUpper charName)
if charLayer == undefined do charLayer = LayerManager.getLayerFromName charName
charLayer.setParent rootLayer
local bipLayer = LayerManager.newLayerFromName (charName+"_BIP")
if bipLayer == undefined do bipLayer = LayerManager.getLayerFromName (charName+"_BIP")
bipLayer.setParent charLayer
local GeoLayer = LayerManager.newLayerFromName (charName+"_GEO")
if GeoLayer == undefined do GeoLayer = LayerManager.getLayerFromName (charName+"_GEO")
GeoLayer.setParent charLayer
GeoLayer.isfrozen =true
-- 오브젝트 관리
for i in skinObjects do (
i.showFrozenInGray = false
local n = i.name
if not matchPattern n pattern:(charName+"*") do i.name = charName+"_"+i.name
if i.material!=undefined do (
mtlName = i.material.name
if not matchPattern mtlName pattern:(charName+"*") do i.material.name = charName+"_"+mtlName
)
GeoLayer.addnode i
)
local allBip = getHierarchy bipObj #()
for i in allBip do(
bipLayer.addNode i
i.name = charName+"_"+i.name
i.renderable =false
i.boxMode = true
)
-- 성공 반환
format "[SUCCESS] Mixamo to Biped conversion completed for: %\n" charName
redrawViews()
true
)
-- ================================== 자동 실행 ===============================================
-- 스크립트 로딩 완료 후 바로 변환 시작
format "\n✅ MixamoToBiped Converter 로딩 완료!\n"
format "자동으로 변환을 시작합니다...\n\n"
try (
format "=== Mixamo to Biped 자동 변환기 ===\n"
-- Mixamo 오브젝트 확인
local mixamoObjects = $mixamorig* as array
if mixamoObjects.count > 0 then (
format "Mixamo 오브젝트 발견: %개\n" mixamoObjects.count
-- 캐릭터 이름 자동 생성 (랜덤 번호 기반)
local randomNum = random 10000 99999
local charName = "Character" + (randomNum as string)
format "기본 이름 사용: %\n" charName
format "변환 시작: %\n" charName
format "=================================\n"
-- 변환 실행
try (
local result = ConvertMixamoToBiped charName:charName
if result then (
format "\n🎉 변환 완료! 🎉\n"
format "캐릭터: %\n" charName
format "✅ Biped 로컬 축: 표준 유지 (축 뒤틀림 없음)\n"
format "✅ T-Pose 자세: 기존 알고리즘으로 매칭\n"
format "✅ 애니메이션 호환성: 완벽\n"
format "레이어: CHARACTERS > %\n" (toUpper charName)
redrawViews()
) else (
format "\n❌ 변환 실패!\n"
format "Mixamo 리그 구조를 확인해주세요.\n"
)
) catch (
format "\n❌ 변환 중 오류 발생!\n"
format "오류 내용: %\n" (getCurrentException())
)
) else (
format "\n⚠ Mixamo 오브젝트를 찾을 수 없습니다!\n"
format "1. Mixamo T-Pose FBX 파일을 먼저 임포트해주세요.\n"
format "2. 임포트 후 이 스크립트를 다시 실행하세요.\n"
format "\n사용법:\n"
format "1. File > Import > Mixamo T-Pose FBX 선택\n"
format "2. 스크립트 실행 (자동으로 변환 시작)\n"
format "3. 자동 변환 완료! ✨\n"
)
) catch (
format "❌ 스크립트 실행 중 심각한 오류 발생!\n"
format "오류 내용: %\n" (getCurrentException())
)