pyjallib 0.1.12__py3-none-any.whl → 0.1.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyjallib/__init__.py +1 -1
- pyjallib/max/__init__.py +8 -4
- pyjallib/max/anim.py +84 -0
- pyjallib/max/autoClavicle.py +1 -1
- pyjallib/max/bip.py +89 -79
- pyjallib/max/bone.py +84 -22
- pyjallib/max/boneChain.py +19 -22
- pyjallib/max/fbxHandler.py +215 -0
- pyjallib/max/groinBone.py +3 -3
- pyjallib/max/header.py +10 -13
- pyjallib/max/hip.py +4 -4
- pyjallib/max/kneeBone.py +44 -20
- pyjallib/max/layer.py +12 -6
- pyjallib/max/mocap.py +376 -0
- pyjallib/max/rootMotion.py +639 -0
- pyjallib/max/toolManager.py +92 -0
- pyjallib/max/twistBone.py +5 -1
- pyjallib/max/volumeBone.py +2 -1
- pyjallib/perforce.py +116 -4
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.14.dist-info}/METADATA +1 -1
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.14.dist-info}/RECORD +22 -18
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.14.dist-info}/WHEEL +0 -0
pyjallib/__init__.py
CHANGED
pyjallib/max/__init__.py
CHANGED
@@ -35,6 +35,11 @@ from pyjallib.max.volumeBone import VolumeBone
|
|
35
35
|
from pyjallib.max.kneeBone import KneeBone
|
36
36
|
from pyjallib.max.hip import Hip
|
37
37
|
|
38
|
+
from pyjallib.max.rootMotion import RootMotion
|
39
|
+
|
40
|
+
from pyjallib.max.fbxHandler import FBXHandler
|
41
|
+
from pyjallib.max.toolManager import ToolManager
|
42
|
+
|
38
43
|
from pyjallib.max.ui.Container import Container
|
39
44
|
|
40
45
|
# 모듈 내보내기
|
@@ -55,14 +60,13 @@ __all__ = [
|
|
55
60
|
'Morph',
|
56
61
|
'BoneChain',
|
57
62
|
'TwistBone',
|
58
|
-
'TwistBoneChain',
|
59
63
|
'GroinBone',
|
60
|
-
'GroinBoneChain',
|
61
64
|
'AutoClavicle',
|
62
|
-
'AutoClavicleChain',
|
63
65
|
'VolumeBone',
|
64
|
-
'VolumeBoneChain',
|
65
66
|
'KneeBone',
|
66
67
|
'Hip',
|
68
|
+
'RootMotion',
|
69
|
+
'FBXHandler',
|
70
|
+
'ToolManager',
|
67
71
|
'Container'
|
68
72
|
]
|
pyjallib/max/anim.py
CHANGED
@@ -477,6 +477,40 @@ class Anim:
|
|
477
477
|
"""
|
478
478
|
rt.deleteKeys(inObj, rt.Name('allKeys'))
|
479
479
|
|
480
|
+
def delete_keys_in_range(self, node, startFrame, endFrame):
|
481
|
+
"""
|
482
|
+
지정된 프레임 범위에서 노드의 모든 키를 삭제하는 함수
|
483
|
+
|
484
|
+
Args:
|
485
|
+
node: 키를 삭제할 노드
|
486
|
+
startFrame (int): 시작 프레임
|
487
|
+
endFrame (int): 끝 프레임
|
488
|
+
|
489
|
+
Returns:
|
490
|
+
bool: 성공 여부
|
491
|
+
"""
|
492
|
+
if not rt.isValidNode(node):
|
493
|
+
return False
|
494
|
+
|
495
|
+
try:
|
496
|
+
maxscriptCode = f"""
|
497
|
+
(
|
498
|
+
selectKeys $'{node.name}'.position.controller (interval {startFrame} {endFrame})
|
499
|
+
deleteKeys $'{node.name}'.position.controller #selection
|
500
|
+
|
501
|
+
selectKeys $'{node.name}'.rotation.controller (interval {startFrame} {endFrame})
|
502
|
+
deleteKeys $'{node.name}'.rotation.controller #selection
|
503
|
+
|
504
|
+
selectKeys $'{node.name}'.scale.controller (interval {startFrame} {endFrame})
|
505
|
+
deleteKeys $'{node.name}'.scale.controller #selection
|
506
|
+
)
|
507
|
+
"""
|
508
|
+
rt.execute(maxscriptCode)
|
509
|
+
return True
|
510
|
+
except Exception as e:
|
511
|
+
print(f"Error deleting keys in range: {e}")
|
512
|
+
return False
|
513
|
+
|
480
514
|
def is_node_animated(self, node):
|
481
515
|
"""
|
482
516
|
객체 및 그 하위 요소(애니메이션, 커스텀 속성 등)가 애니메이션 되었는지 재귀적으로 확인함.
|
@@ -574,6 +608,56 @@ class Anim:
|
|
574
608
|
|
575
609
|
return result
|
576
610
|
|
611
|
+
def save_animation(self, inObjs, inSaveFilePath):
|
612
|
+
"""
|
613
|
+
객체의 애니메이션을 저장함.
|
614
|
+
|
615
|
+
매개변수:
|
616
|
+
inObj : 애니메이션을 저장할 객체
|
617
|
+
"""
|
618
|
+
|
619
|
+
if not(len(inObjs) > 0):
|
620
|
+
return False
|
621
|
+
|
622
|
+
for obj in inObjs:
|
623
|
+
if not(rt.isValidNode(obj)):
|
624
|
+
return False
|
625
|
+
|
626
|
+
animatedNodes = self.find_animated_nodes(inObjs)
|
627
|
+
rt.LoadSaveAnimation.setUpAnimsForSave(animatedNodes, animatedTracks=True, includeContraints=True, keyable=True)
|
628
|
+
rt.LoadSaveAnimation.saveAnimation(
|
629
|
+
inSaveFilePath,
|
630
|
+
animatedNodes,
|
631
|
+
"tempVal",
|
632
|
+
"tempVal",
|
633
|
+
animatedTracks=True,
|
634
|
+
includeConstraints=True,
|
635
|
+
keyableTracks=False,
|
636
|
+
SaveSegment=True,
|
637
|
+
segInterval=rt.animationRange,
|
638
|
+
segKeyPerFrame=True
|
639
|
+
)
|
640
|
+
|
641
|
+
return True
|
642
|
+
|
643
|
+
def load_animation(self, inObjs, inLoadFilePath):
|
644
|
+
"""
|
645
|
+
애니메이션을 로드함.
|
646
|
+
|
647
|
+
매개변수:
|
648
|
+
inObjs : 애니메이션을 로드할 객체
|
649
|
+
inLoadFilePath : 애니메이션을 로드할 파일 경로
|
650
|
+
"""
|
651
|
+
|
652
|
+
if not(rt.doesFileExist(inLoadFilePath)):
|
653
|
+
return False
|
654
|
+
|
655
|
+
rt.LoadSaveAnimation.setUpAnimsForLoad(inObjs, includePB2s=True, stripLayers=True)
|
656
|
+
rt.LoadSaveAnimation.loadAnimation(inLoadFilePath, inObjs, insert=False, relative=False, insertTime=0, stripLayers=True)
|
657
|
+
|
658
|
+
return True
|
659
|
+
|
660
|
+
|
577
661
|
def save_xform(self, inObj):
|
578
662
|
"""
|
579
663
|
객체의 현재 변환 행렬(월드, 부모 스페이스)을 저장하여 복원을 가능하게 함.
|
pyjallib/max/autoClavicle.py
CHANGED
@@ -108,7 +108,7 @@ class AutoClavicle:
|
|
108
108
|
autoClavicleBone.transform = inClavicle.transform
|
109
109
|
self.anim.move_local(autoClavicleBone, clavicleLength/2.0, 0.0, 0.0)
|
110
110
|
autoClavicleBone.parent = inClavicle
|
111
|
-
genBones.
|
111
|
+
genBones.append(autoClavicleBone)
|
112
112
|
|
113
113
|
# 타겟 헬퍼 포인트 생성 (쇄골과 상완용)
|
114
114
|
rotTargetClavicle = self.helper.create_point(self.name.replace_name_part("Type", autoClavicleName, self.name.get_name_part_value_by_description("Type", "Target")))
|
pyjallib/max/bip.py
CHANGED
@@ -454,7 +454,7 @@ class Bip:
|
|
454
454
|
rt.biped.collapseAtLayer(inBipRoot.controller, 0)
|
455
455
|
layerNum = rt.biped.numLayers(inBipRoot.controller)
|
456
456
|
|
457
|
-
def save_bip_file(self, inBipRoot, inFile, inBakeAllKeys=True, inCollapseLayers=True, progress_callback=None):
|
457
|
+
def save_bip_file(self, inBipRoot, inFile, inBakeAllKeys=True, inCollapseLayers=True, inUseAnimationRangeOnly=True, progress_callback=None):
|
458
458
|
"""
|
459
459
|
Biped BIP 파일 저장
|
460
460
|
|
@@ -500,84 +500,105 @@ class Bip:
|
|
500
500
|
rt.biped.addNewKey(item.controller, frame)
|
501
501
|
if progress_callback:
|
502
502
|
progress_callback(frame - startFrame + 1, totalFrame)
|
503
|
+
|
504
|
+
minFrame = 0.0
|
505
|
+
maxFrame = 0.0
|
506
|
+
allTargetBipedObjs = self.get_nodes(inBipRoot)
|
507
|
+
for item in allTargetBipedObjs:
|
508
|
+
if item == item.controller.rootNode:
|
509
|
+
horizontalController = rt.getPropertyController(item.controller, "horizontal")
|
510
|
+
verticalController = rt.getPropertyController(item.controller, "vertical")
|
511
|
+
turningController = rt.getPropertyController(item.controller, "turning")
|
512
|
+
|
513
|
+
minFrame = min(float(horizontalController.keys[0].time), minFrame)
|
514
|
+
minFrame = min(float(verticalController.keys[0].time), minFrame)
|
515
|
+
minFrame = min(float(turningController.keys[0].time), minFrame)
|
516
|
+
|
517
|
+
maxFrame = max(float(horizontalController.keys[-1].time), maxFrame)
|
518
|
+
maxFrame = max(float(verticalController.keys[-1].time), maxFrame)
|
519
|
+
maxFrame = max(float(turningController.keys[-1].time), maxFrame)
|
520
|
+
else:
|
521
|
+
minFrame = min(float(item.controller.keys[0].time), minFrame)
|
522
|
+
maxFrame = max(float(item.controller.keys[-1].time), maxFrame)
|
523
|
+
|
524
|
+
bakeStartFrame = minFrame
|
525
|
+
bakeEndFrame = maxFrame
|
526
|
+
|
527
|
+
if inUseAnimationRangeOnly:
|
528
|
+
allTargetBipedObjs = self.get_nodes(inBipRoot)
|
529
|
+
startFrame = rt.execute("(animationRange.start as integer) / TicksPerFrame")
|
530
|
+
endFrame = rt.execute("(animationRange.end as integer) / TicksPerFrame")
|
531
|
+
bakeStartFrame = startFrame
|
532
|
+
bakeEndFrame = endFrame
|
533
|
+
|
534
|
+
if inBakeAllKeys:
|
535
|
+
rt.biped.saveBipFileSegment(inBipRoot.controller, inFile, bakeStartFrame, bakeEndFrame, rt.name("keyPerFrame"))
|
536
|
+
else:
|
537
|
+
rt.biped.saveBipFileSegment(inBipRoot.controller, inFile, bakeStartFrame, bakeEndFrame)
|
503
538
|
|
504
|
-
rt.biped.saveBipFile(inBipRoot.controller, inFile)
|
505
539
|
return True
|
506
540
|
|
507
541
|
def link_base_skeleton(self, skinBoneBaseName="b"):
|
508
542
|
"""
|
509
543
|
기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
|
510
544
|
"""
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
baseSkel = [None] * len(bipSkel)
|
515
|
-
|
516
|
-
for i in range(len(bipSkel)):
|
517
|
-
baseSkeletonName = self.name.replace_base(bipSkel[i].name, skinBoneBaseName)
|
518
|
-
baseSkeletonName = self.name.replace_filteringChar(baseSkeletonName, "_")
|
519
|
-
baseSkelObj = rt.getNodeByName(baseSkeletonName)
|
520
|
-
if rt.isValidObj(baseSkelObj):
|
521
|
-
baseSkel[i] = baseSkelObj
|
522
|
-
|
523
|
-
self.anim.save_xform(bipSkel[i])
|
524
|
-
self.anim.set_xform(bipSkel[i])
|
525
|
-
|
526
|
-
self.anim.save_xform(baseSkel[i])
|
527
|
-
self.anim.set_xform(baseSkel[i])
|
545
|
+
bipComs = self.get_coms()
|
546
|
+
if len(bipComs) != 1:
|
547
|
+
return False
|
528
548
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
549
|
+
bipCom = bipComs[0]
|
550
|
+
bipNodes = self.get_nodes(bipCom)
|
551
|
+
|
552
|
+
targetBones = [item for item in bipNodes
|
553
|
+
if (rt.classOf(item) == rt.Biped_Object)
|
554
|
+
and (not rt.matchPattern(item.name, pattern="*Twist*"))
|
555
|
+
and (item != item.controller.rootNode)]
|
556
|
+
sortedBipBones = self.bone.sort_bones_as_hierarchy(targetBones)
|
557
|
+
|
558
|
+
skinBones = []
|
559
|
+
for item in sortedBipBones:
|
560
|
+
skinBoneName = self.name.replace_name_part("Base", item.name, skinBoneBaseName)
|
561
|
+
skinBoneName = self.name.replace_filtering_char(skinBoneName, "_")
|
562
|
+
foundSkinBone = rt.getNodeByName(skinBoneName)
|
563
|
+
if rt.isValidObj(foundSkinBone):
|
564
|
+
skinBones.append(foundSkinBone)
|
565
|
+
else:
|
566
|
+
skinBones.append(None)
|
536
567
|
|
537
|
-
for i in
|
538
|
-
if
|
539
|
-
|
540
|
-
|
541
|
-
rt.setArrowCursor()
|
568
|
+
for i, item in enumerate(sortedBipBones):
|
569
|
+
if skinBones[i] is not None:
|
570
|
+
self.bone.link_skin_bone(skinBones[i], item)
|
542
571
|
|
543
572
|
def unlink_base_skeleton(self, skinBoneBaseName="b"):
|
544
573
|
"""
|
545
574
|
기본 스켈레톤 링크 해제
|
546
575
|
"""
|
547
|
-
rt.setWaitCursor()
|
548
|
-
|
549
576
|
bipComs = self.get_coms()
|
550
|
-
|
551
|
-
|
552
|
-
baseSkel = [None] * len(bipSkel)
|
553
|
-
|
554
|
-
for i in range(len(bipSkel)):
|
555
|
-
baseSkeletonName = self.name.replace_name_part("Base", bipSkel[i].name, skinBoneBaseName)
|
556
|
-
baseSkeletonName = self.name.replace_filtering_char(baseSkeletonName, "_")
|
557
|
-
print("baseSkeletonName", baseSkeletonName)
|
558
|
-
baseSkelObj = rt.getNodeByName(baseSkeletonName)
|
559
|
-
print("baseSkelObj", baseSkelObj)
|
560
|
-
if rt.isValidObj(baseSkelObj):
|
561
|
-
baseSkel[i] = baseSkelObj
|
562
|
-
|
563
|
-
self.anim.save_xform(bipSkel[i])
|
564
|
-
self.anim.set_xform(bipSkel[i])
|
565
|
-
|
566
|
-
self.anim.save_xform(baseSkel[i])
|
567
|
-
self.anim.set_xform(baseSkel[i])
|
568
|
-
|
569
|
-
for i in range(len(baseSkel)):
|
570
|
-
if baseSkel[i] is not None:
|
571
|
-
baseSkel[i].controller = rt.prs()
|
572
|
-
self.anim.set_xform([baseSkel[i]], space="World")
|
573
|
-
|
574
|
-
for i in range(len(baseSkel)):
|
575
|
-
if baseSkel[i] is not None:
|
576
|
-
baseSkel[i].boneEnable = True
|
577
|
-
|
578
|
-
rt.setArrowCursor()
|
577
|
+
if len(bipComs) != 1:
|
578
|
+
return False
|
579
579
|
|
580
|
-
|
580
|
+
bipCom = bipComs[0]
|
581
|
+
bipNodes = self.get_nodes(bipCom)
|
582
|
+
targetBones = [item for item in bipNodes
|
583
|
+
if (rt.classOf(item) == rt.Biped_Object)
|
584
|
+
and (not rt.matchPattern(item.name, pattern="*Twist*"))
|
585
|
+
and (item != item.controller.rootNode)]
|
586
|
+
sortedBipBones = self.bone.sort_bones_as_hierarchy(targetBones)
|
587
|
+
skinBones = []
|
588
|
+
for item in sortedBipBones:
|
589
|
+
skinBoneName = self.name.replace_name_part("Base", item.name, skinBoneBaseName)
|
590
|
+
skinBoneName = self.name.replace_filtering_char(skinBoneName, "_")
|
591
|
+
foundSkinBone = rt.getNodeByName(skinBoneName)
|
592
|
+
if rt.isValidObj(foundSkinBone):
|
593
|
+
skinBones.append(foundSkinBone)
|
594
|
+
else:
|
595
|
+
skinBones.append(None)
|
596
|
+
|
597
|
+
for item in skinBones:
|
598
|
+
if item is not None:
|
599
|
+
self.bone.unlink_skin_bone(item)
|
600
|
+
|
601
|
+
def convert_name_for_ue5(self, inBipRoot, inBipNameConfigFile, inRenameFingers=True):
|
581
602
|
"""
|
582
603
|
Biped 이름을 UE5에 맞게 변환
|
583
604
|
|
@@ -587,17 +608,11 @@ class Bip:
|
|
587
608
|
Returns:
|
588
609
|
변환된 Biped 객체
|
589
610
|
"""
|
590
|
-
bipComs = self.get_coms()
|
591
|
-
|
592
|
-
if len(bipComs) > 1:
|
593
|
-
rt.messageBox("Please select only one Biped object.")
|
594
|
-
return False
|
595
|
-
|
596
611
|
from pyjallib.max.name import Name
|
597
612
|
|
598
613
|
bipNameTool = Name(configPath=inBipNameConfigFile)
|
599
614
|
|
600
|
-
bipObj =
|
615
|
+
bipObj = inBipRoot
|
601
616
|
bipNodes = self.get_all(bipObj)
|
602
617
|
for bipNode in bipNodes:
|
603
618
|
if bipNode.name == bipObj.controller.rootName:
|
@@ -628,7 +643,7 @@ class Bip:
|
|
628
643
|
if newNameDict["RealName"] == "Forearm":
|
629
644
|
newNameDict["RealName"] = "Lowerarm"
|
630
645
|
|
631
|
-
if newNameDict["RealName"] == "Spine" or newNameDict["RealName"] == "Neck":
|
646
|
+
if newNameDict["RealName"] == "Spine" or newNameDict["RealName"] == "Neck" or newNameDict["RealName"] == "Tail":
|
632
647
|
if newNameDict["Index"] == "":
|
633
648
|
newNameDict["Index"] = str(int(1)).zfill(self.name.get_padding_num())
|
634
649
|
else:
|
@@ -639,13 +654,6 @@ class Bip:
|
|
639
654
|
bipNode.name = newBipName.lower()
|
640
655
|
|
641
656
|
# 손가락 바꾸는 부분
|
642
|
-
# 5개가 아닌 손가락은 지원하지 않음
|
643
|
-
# 손가락 하나의 최대 링크는 3개
|
644
|
-
indices = []
|
645
|
-
if bipObj.controller.knuckles:
|
646
|
-
pass
|
647
|
-
else:
|
648
|
-
indices = list(range(0, 15, 3))
|
649
657
|
|
650
658
|
fingerNum = bipObj.controller.fingers
|
651
659
|
fingerLinkNum = bipObj.controller.fingerLinks
|
@@ -668,7 +676,9 @@ class Bip:
|
|
668
676
|
fingers.append(fingerNode)
|
669
677
|
rFingersList.append(fingers)
|
670
678
|
|
671
|
-
fingerName = ["
|
679
|
+
fingerName = ["finger0", "finger1", "finger2", "finger3", "finger4"]
|
680
|
+
if inRenameFingers:
|
681
|
+
fingerName = ["thumb", "index", "middle", "ring", "pinky"]
|
672
682
|
|
673
683
|
for i, fingers in enumerate(lFingersList):
|
674
684
|
for j, item in enumerate(fingers):
|
pyjallib/max/bone.py
CHANGED
@@ -212,14 +212,14 @@ class Bone:
|
|
212
212
|
return False
|
213
213
|
return False
|
214
214
|
|
215
|
-
def create_nub_bone(self, inName, inSize):
|
215
|
+
def create_nub_bone(self, inName, inSize, inBoneScaleType=rt.Name("none")):
|
216
216
|
"""
|
217
217
|
Nub 뼈대 생성.
|
218
218
|
|
219
219
|
Args:
|
220
220
|
inName: 뼈대 이름
|
221
221
|
inSize: 뼈대 크기
|
222
|
-
|
222
|
+
inBoneScaleType: 뼈대 스케일 타입 (기본값: rt.Name("none"))
|
223
223
|
Returns:
|
224
224
|
생성된 Nub 뼈대
|
225
225
|
"""
|
@@ -242,6 +242,8 @@ class Bone:
|
|
242
242
|
nubBone.name = self.name.remove_name_part("Nub", nubBone.name)
|
243
243
|
nubBone.name = self.name.replace_name_part("Nub", nubBone.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
|
244
244
|
|
245
|
+
nubBone.boneScaleType = inBoneScaleType
|
246
|
+
|
245
247
|
# 화면 갱신 재개
|
246
248
|
rt.enableSceneRedraw()
|
247
249
|
rt.redrawViews()
|
@@ -751,6 +753,34 @@ class Bone:
|
|
751
753
|
|
752
754
|
return True
|
753
755
|
|
756
|
+
def unlink_skin_bone(self, inSkinBone):
|
757
|
+
"""
|
758
|
+
스킨 뼈대를 원본 뼈대에서 연결 해제.
|
759
|
+
|
760
|
+
Args:
|
761
|
+
inSkinBone: 연결 해제할 스킨 뼈대
|
762
|
+
"""
|
763
|
+
self.anim.save_xform(inSkinBone)
|
764
|
+
self.anim.set_xform(inSkinBone)
|
765
|
+
|
766
|
+
inSkinBone.controller = rt.prs()
|
767
|
+
self.anim.set_xform(inSkinBone, space="World")
|
768
|
+
|
769
|
+
return True
|
770
|
+
|
771
|
+
def unlink_skin_bones(self, inSkinBoneArray):
|
772
|
+
"""
|
773
|
+
스킨 뼈대 배열을 원본 뼈대에서 연결 해제.
|
774
|
+
|
775
|
+
Args:
|
776
|
+
inSkinBoneArray: 연결 해제할 스킨 뼈대 배열
|
777
|
+
"""
|
778
|
+
for item in inSkinBoneArray:
|
779
|
+
if rt.isValidObj(item):
|
780
|
+
self.unlink_skin_bone(item)
|
781
|
+
|
782
|
+
return True
|
783
|
+
|
754
784
|
def create_skin_bone(self, inBoneArray, skipNub=True, mesh=True, link=True, skinBoneBaseName=""):
|
755
785
|
"""
|
756
786
|
스킨 뼈대 생성.
|
@@ -1153,58 +1183,90 @@ class Bone:
|
|
1153
1183
|
if len(skinBones) == 0:
|
1154
1184
|
return False
|
1155
1185
|
|
1186
|
+
pelvisBone = None
|
1187
|
+
lastSpineBone = None
|
1188
|
+
|
1189
|
+
for item in skinBones:
|
1190
|
+
if rt.matchPattern(item.name.lower(), pattern="*pelvis*"):
|
1191
|
+
pelvisBone = item
|
1192
|
+
if rt.matchPattern(item.name.lower(), pattern="*spine*"):
|
1193
|
+
lastSpineBone = item
|
1194
|
+
foundChildren = self.get_every_children(item)
|
1195
|
+
for child in foundChildren:
|
1196
|
+
if rt.matchPattern(child.name.lower(), pattern="*spine*"):
|
1197
|
+
lastSpineBone = child
|
1198
|
+
|
1199
|
+
if pelvisBone is not None and lastSpineBone is not None:
|
1200
|
+
for item in skinBones:
|
1201
|
+
if rt.matchPattern(item.name.lower(), pattern="*thigh*"):
|
1202
|
+
item.parent = pelvisBone
|
1203
|
+
if rt.matchPattern(item.name.lower(), pattern="*clavicle*"):
|
1204
|
+
item.parent = lastSpineBone
|
1205
|
+
|
1156
1206
|
for item in skinBones:
|
1157
|
-
if rt.matchPattern(item.name, pattern="*pelvis*"):
|
1207
|
+
if rt.matchPattern(item.name.lower(), pattern="*pelvis*"):
|
1158
1208
|
self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
|
1159
|
-
if rt.matchPattern(item.name, pattern="*spine*"):
|
1209
|
+
if rt.matchPattern(item.name.lower(), pattern="*spine*"):
|
1160
1210
|
self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
|
1161
|
-
if rt.matchPattern(item.name, pattern="*neck*"):
|
1211
|
+
if rt.matchPattern(item.name.lower(), pattern="*neck*"):
|
1162
1212
|
self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
|
1163
|
-
if rt.matchPattern(item.name, pattern="*head*"):
|
1213
|
+
if rt.matchPattern(item.name.lower(), pattern="*head*"):
|
1164
1214
|
self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
|
1165
|
-
if rt.matchPattern(item.name, pattern="*thigh*l"):
|
1215
|
+
if rt.matchPattern(item.name.lower(), pattern="*thigh*l"):
|
1166
1216
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1167
|
-
if rt.matchPattern(item.name, pattern="*calf*l"):
|
1217
|
+
if rt.matchPattern(item.name.lower(), pattern="*calf*l"):
|
1168
1218
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1169
|
-
if rt.matchPattern(item.name, pattern="*foot*l"):
|
1219
|
+
if rt.matchPattern(item.name.lower(), pattern="*foot*l"):
|
1170
1220
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1171
|
-
if rt.matchPattern(item.name, pattern="*ball*r"):
|
1221
|
+
if rt.matchPattern(item.name.lower(), pattern="*ball*r"):
|
1172
1222
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1173
1223
|
|
1174
|
-
if rt.matchPattern(item.name, pattern="*clavicle*r"):
|
1224
|
+
if rt.matchPattern(item.name.lower(), pattern="*clavicle*r"):
|
1175
1225
|
self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
|
1176
|
-
if rt.matchPattern(item.name, pattern="*upperarm*r"):
|
1226
|
+
if rt.matchPattern(item.name.lower(), pattern="*upperarm*r"):
|
1177
1227
|
self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
|
1178
|
-
if rt.matchPattern(item.name, pattern="*lowerarm*r"):
|
1228
|
+
if rt.matchPattern(item.name.lower(), pattern="*lowerarm*r"):
|
1179
1229
|
self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
|
1180
|
-
if rt.matchPattern(item.name, pattern="*hand*r"):
|
1230
|
+
if rt.matchPattern(item.name.lower(), pattern="*hand*r"):
|
1181
1231
|
self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
|
1182
1232
|
|
1183
|
-
if rt.matchPattern(item.name, pattern="*thumb*r"):
|
1233
|
+
if rt.matchPattern(item.name.lower(), pattern="*thumb*r"):
|
1234
|
+
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1235
|
+
if rt.matchPattern(item.name.lower(), pattern="*index*r"):
|
1236
|
+
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1237
|
+
if rt.matchPattern(item.name.lower(), pattern="*middle*r"):
|
1238
|
+
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1239
|
+
if rt.matchPattern(item.name.lower(), pattern="*ring*r"):
|
1184
1240
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1185
|
-
if rt.matchPattern(item.name, pattern="*
|
1241
|
+
if rt.matchPattern(item.name.lower(), pattern="*pinky*r"):
|
1186
1242
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1187
|
-
|
1243
|
+
|
1244
|
+
if rt.matchPattern(item.name.lower(), pattern="*finger0*r"):
|
1245
|
+
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1246
|
+
if rt.matchPattern(item.name.lower(), pattern="*finger1*r"):
|
1188
1247
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1189
|
-
if rt.matchPattern(item.name, pattern="*
|
1248
|
+
if rt.matchPattern(item.name.lower(), pattern="*finger2*r"):
|
1190
1249
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1191
|
-
if rt.matchPattern(item.name, pattern="*
|
1250
|
+
if rt.matchPattern(item.name.lower(), pattern="*finger3*r"):
|
1251
|
+
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1252
|
+
if rt.matchPattern(item.name.lower(), pattern="*finger4*r"):
|
1192
1253
|
self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
|
1193
1254
|
|
1194
|
-
if rt.matchPattern(item.name, pattern="*metacarpal*"):
|
1255
|
+
if rt.matchPattern(item.name.lower(), pattern="*metacarpal*"):
|
1195
1256
|
tempArray = self.name._split_to_array(item.name)
|
1196
1257
|
item.name = self.name._combine(tempArray, inFilChar="_")
|
1197
1258
|
item.name = self.name.remove_name_part("Base", item.name)
|
1198
1259
|
|
1199
1260
|
self.anim.save_xform(item)
|
1200
1261
|
|
1201
|
-
|
1262
|
+
if isHuman:
|
1263
|
+
self.relink_missing_skin_bones_for_ue5manny(skinBones)
|
1202
1264
|
|
1203
1265
|
self.link_skin_bones(skinBones, sortedBipBones)
|
1204
1266
|
for item in skinBones:
|
1205
1267
|
self.anim.save_xform(item)
|
1206
1268
|
|
1207
|
-
return skinBones
|
1269
|
+
return (skinBones + missingBipBones)
|
1208
1270
|
|
1209
1271
|
def set_bone_on(self, inBone):
|
1210
1272
|
"""
|
pyjallib/max/boneChain.py
CHANGED
@@ -74,17 +74,20 @@ class BoneChain:
|
|
74
74
|
return False
|
75
75
|
|
76
76
|
try:
|
77
|
-
#
|
77
|
+
# 유효한 노드들을 모아서 한 번에 삭제
|
78
|
+
valid_nodes = []
|
79
|
+
|
80
|
+
# 유효한 뼈대 수집
|
78
81
|
if self.bones:
|
79
|
-
for bone in self.bones
|
80
|
-
if rt.isValidNode(bone):
|
81
|
-
rt.delete(bone)
|
82
|
+
valid_nodes.extend([bone for bone in self.bones if rt.isValidNode(bone)])
|
82
83
|
|
83
|
-
# 헬퍼
|
84
|
+
# 유효한 헬퍼 수집
|
84
85
|
if self.helpers:
|
85
|
-
for helper in self.helpers
|
86
|
-
|
87
|
-
|
86
|
+
valid_nodes.extend([helper for helper in self.helpers if rt.isValidNode(helper)])
|
87
|
+
|
88
|
+
# 수집된 노드가 있으면 한 번에 삭제
|
89
|
+
if valid_nodes:
|
90
|
+
rt.delete(valid_nodes)
|
88
91
|
|
89
92
|
self.bones = []
|
90
93
|
self.helpers = []
|
@@ -95,7 +98,7 @@ class BoneChain:
|
|
95
98
|
|
96
99
|
def delete_all(self):
|
97
100
|
"""
|
98
|
-
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서
|
101
|
+
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제하고 소스본과 파라미터도 초기화
|
99
102
|
|
100
103
|
Returns:
|
101
104
|
bool: 삭제 성공 여부
|
@@ -104,20 +107,14 @@ class BoneChain:
|
|
104
107
|
return False
|
105
108
|
|
106
109
|
try:
|
107
|
-
#
|
108
|
-
|
109
|
-
for bone in self.bones:
|
110
|
-
if rt.isValidNode(bone):
|
111
|
-
rt.delete(bone)
|
110
|
+
# delete 메소드를 재사용하여 뼈대와 헬퍼 삭제
|
111
|
+
result = self.delete()
|
112
112
|
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
self.clear()
|
120
|
-
return True
|
113
|
+
# 추가로 소스본과 파라미터 초기화
|
114
|
+
self.sourceBones = []
|
115
|
+
self.parameters = []
|
116
|
+
|
117
|
+
return result
|
121
118
|
except:
|
122
119
|
return False
|
123
120
|
|