diff --git a/3ds max script/MixamoToBiped_Converter - y up.ms b/3ds max script/MixamoToBiped_Converter - y up.ms deleted file mode 100644 index 8ce647a9..00000000 --- a/3ds max script/MixamoToBiped_Converter - y up.ms +++ /dev/null @@ -1,1759 +0,0 @@ -/* - 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()) -) \ No newline at end of file diff --git a/3ds max script/MixamoToBiped_Converter - z up.ms b/3ds max script/MixamoToBiped_Converter - z up.ms deleted file mode 100644 index 2db28d50..00000000 --- a/3ds max script/MixamoToBiped_Converter - z up.ms +++ /dev/null @@ -1,1759 +0,0 @@ -/* - 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 0 10 -30) false - if isvalidNode BipNodes[2] do - biped.setTransform BipNodes[2] #rotation (eulerAngles 0 10 -30) false - if isvalidNode BipNodes[3] do - biped.setTransform BipNodes[3] #rotation (eulerAngles 0 10 -30) false - ) - -- === 오른손 엄지 === - else if isThumb and not isLeftHand then ( - if isvalidNode BipNodes[1] do - biped.setTransform BipNodes[1] #rotation (eulerAngles 0 10 -150) false - if isvalidNode BipNodes[2] do - biped.setTransform BipNodes[2] #rotation (eulerAngles 0 10 -150) false - if isvalidNode BipNodes[3] do - biped.setTransform BipNodes[3] #rotation (eulerAngles 0 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()) -) \ No newline at end of file diff --git a/Assets/Motion/T-Pose_20250726_024111.json b/Assets/Motion/T-Pose_20250726_024111.json deleted file mode 100644 index c5fd37de..00000000 --- a/Assets/Motion/T-Pose_20250726_024111.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91a93fa75768dea987ffabb5f8a3d200cdf390d7f5db7e1bca4fe51b65b7ed27 -size 26266 diff --git a/Assets/Motion/T-Pose_20250726_024111.json.meta b/Assets/Motion/T-Pose_20250726_024111.json.meta deleted file mode 100644 index 45d0f0fb..00000000 --- a/Assets/Motion/T-Pose_20250726_024111.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 963c54edbba1b4b45bed1b3a96506b08 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/test.meta b/Assets/test.meta deleted file mode 100644 index ee9567be..00000000 --- a/Assets/test.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8382ddc88ecd80645a30b123757143bd -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: