pyjallib 0.1.11__py3-none-any.whl → 0.1.13__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 +152 -77
- 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/naming.py +26 -1
- pyjallib/namingConfig.py +3 -2
- pyjallib/perforce.py +396 -0
- {pyjallib-0.1.11.dist-info → pyjallib-0.1.13.dist-info}/METADATA +1 -1
- {pyjallib-0.1.11.dist-info → pyjallib-0.1.13.dist-info}/RECORD +24 -20
- {pyjallib-0.1.11.dist-info → pyjallib-0.1.13.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
@@ -439,80 +439,166 @@ class Bip:
|
|
439
439
|
if colNum > 0:
|
440
440
|
rt.biped.deleteAllCopyCollections(inBipRoot.controller)
|
441
441
|
|
442
|
-
def
|
443
|
-
"""
|
444
|
-
기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
|
442
|
+
def collapse_layers(self, inBipRoot):
|
445
443
|
"""
|
446
|
-
|
444
|
+
Biped 레이어 병합
|
447
445
|
|
448
|
-
|
449
|
-
|
446
|
+
Args:
|
447
|
+
inBipRoot: 대상 Biped 객체
|
448
|
+
"""
|
449
|
+
if not self.is_biped_object(inBipRoot):
|
450
|
+
return False
|
450
451
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
452
|
+
layerNum = rt.biped.numLayers(inBipRoot.controller)
|
453
|
+
while layerNum > 0:
|
454
|
+
rt.biped.collapseAtLayer(inBipRoot.controller, 0)
|
455
|
+
layerNum = rt.biped.numLayers(inBipRoot.controller)
|
456
|
+
|
457
|
+
def save_bip_file(self, inBipRoot, inFile, inBakeAllKeys=True, inCollapseLayers=True, inUseAnimationRangeOnly=True, progress_callback=None):
|
458
|
+
"""
|
459
|
+
Biped BIP 파일 저장
|
457
460
|
|
458
|
-
|
459
|
-
|
461
|
+
Args:
|
462
|
+
inBipRoot: 저장 대상 Biped 루트 노드
|
463
|
+
inFile: 저장할 BIP 파일 경로
|
464
|
+
inBakeAllKeys: 모든 키를 베이크할지 여부 (기본값: True)
|
465
|
+
inCollapseLayers: 레이어를 병합할지 여부 (기본값: True)
|
460
466
|
|
461
|
-
|
462
|
-
|
467
|
+
Returns:
|
468
|
+
bool: 저장 성공 시 True, 실패 시 False
|
469
|
+
"""
|
470
|
+
if not self.is_biped_object(inBipRoot):
|
471
|
+
return False
|
463
472
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
473
|
+
directory = os.path.dirname(inFile)
|
474
|
+
if directory and not os.path.exists(directory):
|
475
|
+
try:
|
476
|
+
os.makedirs(directory, exist_ok=True)
|
477
|
+
except OSError as e:
|
478
|
+
return False
|
479
|
+
|
480
|
+
if inCollapseLayers:
|
481
|
+
self.collapse_layers(inBipRoot)
|
482
|
+
|
483
|
+
if inBakeAllKeys:
|
484
|
+
allTargetBipedObjs = self.get_nodes(inBipRoot)
|
485
|
+
startFrame = rt.execute("(animationRange.start as integer) / TicksPerFrame")
|
486
|
+
endFrame = rt.execute("(animationRange.end as integer) / TicksPerFrame")
|
487
|
+
totalFrame = endFrame - startFrame + 1
|
488
|
+
|
489
|
+
for frame in range(startFrame, endFrame + 1):
|
490
|
+
for item in allTargetBipedObjs:
|
491
|
+
if item == item.controller.rootNode:
|
492
|
+
horizontalController = rt.getPropertyController(item.controller, "horizontal")
|
493
|
+
verticalController = rt.getPropertyController(item.controller, "vertical")
|
494
|
+
turningController = rt.getPropertyController(item.controller, "turning")
|
495
|
+
|
496
|
+
rt.biped.addNewKey(horizontalController, frame)
|
497
|
+
rt.biped.addNewKey(verticalController, frame)
|
498
|
+
rt.biped.addNewKey(turningController, frame)
|
499
|
+
else:
|
500
|
+
rt.biped.addNewKey(item.controller, frame)
|
501
|
+
if progress_callback:
|
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")
|
468
512
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
for i in range(len(baseSkel)):
|
473
|
-
if baseSkel[i] is not None:
|
474
|
-
baseSkel[i].boneEnable = True
|
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)
|
475
516
|
|
476
|
-
|
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)
|
538
|
+
|
539
|
+
return True
|
540
|
+
|
541
|
+
def link_base_skeleton(self, skinBoneBaseName="b"):
|
542
|
+
"""
|
543
|
+
기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
|
544
|
+
"""
|
545
|
+
bipComs = self.get_coms()
|
546
|
+
if len(bipComs) != 1:
|
547
|
+
return False
|
548
|
+
|
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)
|
567
|
+
|
568
|
+
for i, item in enumerate(sortedBipBones):
|
569
|
+
if skinBones[i] is not None:
|
570
|
+
self.bone.link_skin_bone(skinBones[i], item)
|
477
571
|
|
478
572
|
def unlink_base_skeleton(self, skinBoneBaseName="b"):
|
479
573
|
"""
|
480
574
|
기본 스켈레톤 링크 해제
|
481
575
|
"""
|
482
|
-
rt.setWaitCursor()
|
483
|
-
|
484
576
|
bipComs = self.get_coms()
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
self.
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
self.
|
508
|
-
|
509
|
-
for i in range(len(baseSkel)):
|
510
|
-
if baseSkel[i] is not None:
|
511
|
-
baseSkel[i].boneEnable = True
|
512
|
-
|
513
|
-
rt.setArrowCursor()
|
577
|
+
if len(bipComs) != 1:
|
578
|
+
return False
|
579
|
+
|
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)
|
514
600
|
|
515
|
-
def convert_name_for_ue5(self, inBipRoot, inBipNameConfigFile):
|
601
|
+
def convert_name_for_ue5(self, inBipRoot, inBipNameConfigFile, inRenameFingers=True):
|
516
602
|
"""
|
517
603
|
Biped 이름을 UE5에 맞게 변환
|
518
604
|
|
@@ -522,17 +608,11 @@ class Bip:
|
|
522
608
|
Returns:
|
523
609
|
변환된 Biped 객체
|
524
610
|
"""
|
525
|
-
bipComs = self.get_coms()
|
526
|
-
|
527
|
-
if len(bipComs) > 1:
|
528
|
-
rt.messageBox("Please select only one Biped object.")
|
529
|
-
return False
|
530
|
-
|
531
611
|
from pyjallib.max.name import Name
|
532
612
|
|
533
613
|
bipNameTool = Name(configPath=inBipNameConfigFile)
|
534
614
|
|
535
|
-
bipObj =
|
615
|
+
bipObj = inBipRoot
|
536
616
|
bipNodes = self.get_all(bipObj)
|
537
617
|
for bipNode in bipNodes:
|
538
618
|
if bipNode.name == bipObj.controller.rootName:
|
@@ -563,7 +643,7 @@ class Bip:
|
|
563
643
|
if newNameDict["RealName"] == "Forearm":
|
564
644
|
newNameDict["RealName"] = "Lowerarm"
|
565
645
|
|
566
|
-
if newNameDict["RealName"] == "Spine" or newNameDict["RealName"] == "Neck":
|
646
|
+
if newNameDict["RealName"] == "Spine" or newNameDict["RealName"] == "Neck" or newNameDict["RealName"] == "Tail":
|
567
647
|
if newNameDict["Index"] == "":
|
568
648
|
newNameDict["Index"] = str(int(1)).zfill(self.name.get_padding_num())
|
569
649
|
else:
|
@@ -574,13 +654,6 @@ class Bip:
|
|
574
654
|
bipNode.name = newBipName.lower()
|
575
655
|
|
576
656
|
# 손가락 바꾸는 부분
|
577
|
-
# 5개가 아닌 손가락은 지원하지 않음
|
578
|
-
# 손가락 하나의 최대 링크는 3개
|
579
|
-
indices = []
|
580
|
-
if bipObj.controller.knuckles:
|
581
|
-
pass
|
582
|
-
else:
|
583
|
-
indices = list(range(0, 15, 3))
|
584
657
|
|
585
658
|
fingerNum = bipObj.controller.fingers
|
586
659
|
fingerLinkNum = bipObj.controller.fingerLinks
|
@@ -603,7 +676,9 @@ class Bip:
|
|
603
676
|
fingers.append(fingerNode)
|
604
677
|
rFingersList.append(fingers)
|
605
678
|
|
606
|
-
fingerName = ["
|
679
|
+
fingerName = ["finger0", "finger1", "finger2", "finger3", "finger4"]
|
680
|
+
if inRenameFingers:
|
681
|
+
fingerName = ["thumb", "index", "middle", "ring", "pinky"]
|
607
682
|
|
608
683
|
for i, fingers in enumerate(lFingersList):
|
609
684
|
for j, item in enumerate(fingers):
|
@@ -690,8 +765,8 @@ class Bip:
|
|
690
765
|
toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
|
691
766
|
|
692
767
|
if toeLinkNum == 1:
|
693
|
-
rToesList[0][0].name = self.name.replace_name_part("RealName",
|
694
|
-
rToesList[0][0].name = self.name.remove_name_part("Index",
|
768
|
+
rToesList[0][0].name = self.name.replace_name_part("RealName", rToesList[0][0].name, "ball")
|
769
|
+
rToesList[0][0].name = self.name.remove_name_part("Index", rToesList[0][0].name)
|
695
770
|
else:
|
696
771
|
for i, item in enumerate(rToesList[0]):
|
697
772
|
item.name = self.name.replace_name_part("RealName", item.name, "ball")
|
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
|
"""
|