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 CHANGED
@@ -6,7 +6,7 @@ pyjallib Package
6
6
  Python library for game character development pipeline.
7
7
  """
8
8
 
9
- __version__ = '0.1.11'
9
+ __version__ = '0.1.13'
10
10
 
11
11
  # reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
12
12
  from pyjallib.namePart import NamePart, NamePartType
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
  객체의 현재 변환 행렬(월드, 부모 스페이스)을 저장하여 복원을 가능하게 함.
@@ -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.extend(autoClavicleBone)
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 link_base_skeleton(self, skinBoneBaseName="b"):
443
- """
444
- 기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
442
+ def collapse_layers(self, inBipRoot):
445
443
  """
446
- rt.setWaitCursor()
444
+ Biped 레이어 병합
447
445
 
448
- bipSkel = self.get_bips()
449
- baseSkel = [None] * len(bipSkel)
446
+ Args:
447
+ inBipRoot: 대상 Biped 객체
448
+ """
449
+ if not self.is_biped_object(inBipRoot):
450
+ return False
450
451
 
451
- for i in range(len(bipSkel)):
452
- baseSkeletonName = self.name.replace_base(bipSkel[i].name, skinBoneBaseName)
453
- baseSkeletonName = self.name.replace_filteringChar(baseSkeletonName, "_")
454
- baseSkelObj = rt.getNodeByName(baseSkeletonName)
455
- if rt.isValidObj(baseSkelObj):
456
- baseSkel[i] = baseSkelObj
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
- self.anim.save_xform(bipSkel[i])
459
- self.anim.set_xform(bipSkel[i])
461
+ Args:
462
+ inBipRoot: 저장 대상 Biped 루트 노드
463
+ inFile: 저장할 BIP 파일 경로
464
+ inBakeAllKeys: 모든 키를 베이크할지 여부 (기본값: True)
465
+ inCollapseLayers: 레이어를 병합할지 여부 (기본값: True)
460
466
 
461
- self.anim.save_xform(baseSkel[i])
462
- self.anim.set_xform(baseSkel[i])
467
+ Returns:
468
+ bool: 저장 성공 시 True, 실패 시 False
469
+ """
470
+ if not self.is_biped_object(inBipRoot):
471
+ return False
463
472
 
464
- for i in range(len(baseSkel)):
465
- if baseSkel[i] is not None:
466
- baseSkel[i].scale.controller = rt.scaleXYZ()
467
- baseSkel[i].controller = rt.link_constraint()
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
- self.anim.set_xform([baseSkel[i]], space="World")
470
- baseSkel[i].transform.controller.AddTarget(bipSkel[i], 0)
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
- rt.setArrowCursor()
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
- allBips = self.get_nodes(bipComs[0])
486
- bipSkel = [item for item in allBips if item != bipComs[0]]
487
- baseSkel = [None] * len(bipSkel)
488
-
489
- for i in range(len(bipSkel)):
490
- baseSkeletonName = self.name.replace_name_part("Base", bipSkel[i].name, skinBoneBaseName)
491
- baseSkeletonName = self.name.replace_filtering_char(baseSkeletonName, "_")
492
- print("baseSkeletonName", baseSkeletonName)
493
- baseSkelObj = rt.getNodeByName(baseSkeletonName)
494
- print("baseSkelObj", baseSkelObj)
495
- if rt.isValidObj(baseSkelObj):
496
- baseSkel[i] = baseSkelObj
497
-
498
- self.anim.save_xform(bipSkel[i])
499
- self.anim.set_xform(bipSkel[i])
500
-
501
- self.anim.save_xform(baseSkel[i])
502
- self.anim.set_xform(baseSkel[i])
503
-
504
- for i in range(len(baseSkel)):
505
- if baseSkel[i] is not None:
506
- baseSkel[i].controller = rt.prs()
507
- self.anim.set_xform([baseSkel[i]], space="World")
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 = bipComs[0]
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 = ["thumb", "index", "middle", "ring", "pinky"]
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", lToesList[0][0].name, "ball")
694
- rToesList[0][0].name = self.name.remove_name_part("Index", lToesList[0][0].name)
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="*index*r"):
1241
+ if rt.matchPattern(item.name.lower(), pattern="*pinky*r"):
1186
1242
  self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1187
- if rt.matchPattern(item.name, pattern="*middle*r"):
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="*ring*r"):
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="*pinky*r"):
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
- self.relink_missing_skin_bones_for_ue5manny(skinBones)
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
  """