pyjallib 0.1.9__py3-none-any.whl → 0.1.10__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.
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 자동 쇄골 체인(AutoClavicle Chain) 관련 기능을 제공하는 클래스.
6
+ AutoClavicle 클래스가 생성한 자동 쇄골 뼈대들을 관리하고 접근하는 인터페이스를 제공합니다.
7
+
8
+ Examples:
9
+ # 자동 쇄골 체인 생성 예시
10
+ from pyjallib.max import AutoClavicle, AutoClavicleChain
11
+ from pymxs import runtime as rt
12
+
13
+ # 선택된 쇄골과 상완 객체
14
+ clavicle = rt.selection[0]
15
+ upperArm = rt.selection[1]
16
+
17
+ # AutoClavicle 클래스 인스턴스 생성
18
+ auto_clavicle = AutoClavicle()
19
+
20
+ # 자동 쇄골 뼈대 생성
21
+ clavicle_bones = auto_clavicle.create_bones(clavicle, upperArm, liftScale=0.8)
22
+
23
+ # 생성된 뼈대로 AutoClavicleChain 인스턴스 생성
24
+ chain = AutoClavicleChain.from_auto_clavicle_result(
25
+ {
26
+ "Bones": clavicle_bones,
27
+ "Helpers": auto_clavicle.helpers,
28
+ "Clavicle": clavicle,
29
+ "UpperArm": upperArm,
30
+ "LiftScale": 0.8
31
+ }
32
+ )
33
+
34
+ # 체인 관리 기능 사용
35
+ bone = chain.get_bone()
36
+
37
+ # 체인의 모든 뼈대 이름 변경
38
+ chain.rename_bones(prefix="character_", suffix="_autoClavicle")
39
+
40
+ # 체인의 LiftScale 수정
41
+ chain.update_lift_scale(0.9)
42
+
43
+ # 필요 없어지면 체인의 모든 뼈대와 헬퍼 삭제
44
+ # chain.delete_all()
45
+ """
46
+
47
+ from pymxs import runtime as rt
48
+ from pyjallib.max.header import get_pyjallibmaxheader
49
+ jal = get_pyjallibmaxheader()
50
+
51
+
52
+ class AutoClavicleChain:
53
+ def __init__(self, inResult):
54
+ """
55
+ 클래스 초기화.
56
+
57
+ Args:
58
+ inResult: AutoClavicle 클래스의 생성 결과 (딕셔너리)
59
+ """
60
+ self.bones = inResult.get("Bones", [])
61
+ self.helpers = inResult.get("Helpers", [])
62
+ self.clavicle = inResult.get("Clavicle", None)
63
+ self.upperArm = inResult.get("UpperArm", None)
64
+ self.liftScale = inResult.get("LiftScale", 0.8)
65
+
66
+ def get_bones(self):
67
+ """
68
+ 체인의 모든 뼈대 가져오기
69
+
70
+ Returns:
71
+ 모든 뼈대 객체의 배열
72
+ """
73
+ if self.is_empty():
74
+ return []
75
+
76
+ return self.bones
77
+
78
+ def get_helpers(self):
79
+ """
80
+ 체인의 모든 헬퍼 가져오기
81
+
82
+ Returns:
83
+ 모든 헬퍼 객체의 배열
84
+ """
85
+ if self.is_empty():
86
+ return []
87
+
88
+ return self.helpers
89
+
90
+ def is_empty(self):
91
+ """
92
+ 체인이 비어있는지 확인
93
+
94
+ Returns:
95
+ 체인이 비어있으면 True, 아니면 False
96
+ """
97
+ return len(self.bones) == 0
98
+
99
+ def clear(self):
100
+ """체인의 모든 뼈대와 헬퍼 참조 제거"""
101
+ self.bones = []
102
+ self.helpers = []
103
+ self.clavicle = None
104
+ self.upperArm = None
105
+
106
+ def delete_all(self):
107
+ """
108
+ 체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제
109
+
110
+ Returns:
111
+ 삭제 성공 여부 (boolean)
112
+ """
113
+ if self.is_empty():
114
+ return False
115
+
116
+ try:
117
+ for bone in self.bones:
118
+ if rt.isValidNode(bone):
119
+ rt.delete(bone)
120
+
121
+ for helper in self.helpers:
122
+ if rt.isValidNode(helper):
123
+ rt.delete(helper)
124
+
125
+ self.clear()
126
+ return True
127
+ except:
128
+ return False
129
+
130
+ def update_lift_scale(self, newLiftScale=0.8):
131
+ """
132
+ 자동 쇄골의 들어올림 스케일 업데이트
133
+
134
+ Args:
135
+ newLiftScale: 새로운 들어올림 스케일 (기본값: 0.8)
136
+
137
+ Returns:
138
+ 업데이트 성공 여부 (boolean)
139
+ """
140
+ if self.is_empty() or not rt.isValidNode(self.clavicle) or not rt.isValidNode(self.upperArm):
141
+ return False
142
+
143
+ clavicle = self.clavicle
144
+ upperArm = self.upperArm
145
+
146
+ # 기존 본과 헬퍼 삭제
147
+ self.delete_all()
148
+
149
+ # 새로운 LiftScale 값 설정
150
+ self.liftScale = newLiftScale
151
+ self.clavicle = clavicle
152
+ self.upperArm = upperArm
153
+
154
+ # 재생성
155
+ result = jal.autoClavicle.create_bones(self.clavicle, self.upperArm, self.liftScale)
156
+ if result:
157
+ return True
158
+
159
+ return False
160
+
161
+ @classmethod
162
+ def from_auto_clavicle_result(cls, inResult):
163
+ """
164
+ AutoClavicle 클래스의 결과로부터 AutoClavicleChain 인스턴스 생성
165
+
166
+ Args:
167
+ inResult: AutoClavicle 클래스의 메서드가 반환한 결과값 딕셔너리
168
+
169
+ Returns:
170
+ AutoClavicleChain 인스턴스
171
+ """
172
+ chain = cls(inResult)
173
+ return chain
pyjallib/max/bip.py CHANGED
@@ -6,6 +6,9 @@ Biped 모듈 - 3ds Max의 Biped 객체 관련 기능 제공
6
6
  원본 MAXScript의 bip.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
7
7
  """
8
8
 
9
+
10
+ import os
11
+
9
12
  from pymxs import runtime as rt
10
13
 
11
14
  # Import necessary service classes for default initialization
@@ -452,11 +455,11 @@ class Bip:
452
455
  if rt.isValidObj(baseSkelObj):
453
456
  baseSkel[i] = baseSkelObj
454
457
 
455
- self.anim.save_xform(bipSkel)
456
- self.anim.set_xform(bipSkel)
457
-
458
- self.anim.save_xform(baseSkel)
459
- self.anim.set_xform(baseSkel)
458
+ self.anim.save_xform(bipSkel[i])
459
+ self.anim.set_xform(bipSkel[i])
460
+
461
+ self.anim.save_xform(baseSkel[i])
462
+ self.anim.set_xform(baseSkel[i])
460
463
 
461
464
  for i in range(len(baseSkel)):
462
465
  if baseSkel[i] is not None:
@@ -478,21 +481,25 @@ class Bip:
478
481
  """
479
482
  rt.setWaitCursor()
480
483
 
481
- bipSkel = self.get_bips()
484
+ bipComs = self.get_coms()
485
+ allBips = self.get_nodes(bipComs[0])
486
+ bipSkel = [item for item in allBips if item != bipComs[0]]
482
487
  baseSkel = [None] * len(bipSkel)
483
488
 
484
489
  for i in range(len(bipSkel)):
485
- baseSkeletonName = self.name.replace_base(bipSkel[i].name, skinBoneBaseName)
486
- baseSkeletonName = self.name.replace_filteringChar(baseSkeletonName, "_")
490
+ baseSkeletonName = self.name.replace_name_part("Base", bipSkel[i].name, skinBoneBaseName)
491
+ baseSkeletonName = self.name.replace_filtering_char(baseSkeletonName, "_")
492
+ print("baseSkeletonName", baseSkeletonName)
487
493
  baseSkelObj = rt.getNodeByName(baseSkeletonName)
494
+ print("baseSkelObj", baseSkelObj)
488
495
  if rt.isValidObj(baseSkelObj):
489
496
  baseSkel[i] = baseSkelObj
490
497
 
491
- self.anim.save_xform(bipSkel)
492
- self.anim.set_xform(bipSkel)
493
-
494
- self.anim.save_xform(baseSkel)
495
- self.anim.set_xform(baseSkel)
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])
496
503
 
497
504
  for i in range(len(baseSkel)):
498
505
  if baseSkel[i] is not None:
@@ -503,4 +510,194 @@ class Bip:
503
510
  if baseSkel[i] is not None:
504
511
  baseSkel[i].boneEnable = True
505
512
 
506
- rt.setArrowCursor()
513
+ rt.setArrowCursor()
514
+
515
+ def convert_name_for_ue5(self, inBipRoot, inBipNameConfigFile):
516
+ """
517
+ Biped 이름을 UE5에 맞게 변환
518
+
519
+ Args:
520
+ inBipRoot: 변환할 Biped 객체
521
+
522
+ Returns:
523
+ 변환된 Biped 객체
524
+ """
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
+ from pyjallib.max.name import Name
532
+
533
+ bipNameTool = Name(configPath=inBipNameConfigFile)
534
+
535
+ bipObj = bipComs[0]
536
+ bipNodes = self.get_all(bipObj)
537
+ for bipNode in bipNodes:
538
+ if bipNode.name == bipObj.controller.rootName:
539
+ bipNode.name = bipNode.name.lower()
540
+ continue
541
+
542
+ bipNodeNameDict = bipNameTool.convert_to_dictionary(bipNode.name)
543
+
544
+ newNameDict = {}
545
+ for namePartName, value in bipNodeNameDict.items():
546
+ namePart = bipNameTool.get_name_part(namePartName)
547
+ desc = namePart.get_description_by_value(value)
548
+
549
+ if namePartName == "RealName" or namePartName == "Index" or namePartName == "Nub":
550
+ newNameDict[namePartName] = value
551
+ else:
552
+ newNameDict[namePartName] = self.name.get_name_part(namePartName).get_value_by_description(desc)
553
+
554
+ if newNameDict["Index"] == "" and self.name._has_digit(newNameDict["RealName"]):
555
+ if "Finger" not in newNameDict["RealName"]:
556
+ splitedRealName = self.name._split_into_string_and_digit(newNameDict["RealName"])
557
+ newNameDict["RealName"] = splitedRealName[0]
558
+ newNameDict["Index"] = splitedRealName[1]
559
+ if newNameDict["Nub"] == "" and bipNameTool.get_name_part_value_by_description("Nub", "Nub") in (newNameDict["RealName"]):
560
+ newNameDict["RealName"] = newNameDict["RealName"].replace(bipNameTool.get_name_part_value_by_description("Nub", "Nub"), "")
561
+ newNameDict["Nub"] = self.name.get_name_part_value_by_description("Nub", "Nub")
562
+
563
+ if newNameDict["RealName"] == "Forearm":
564
+ newNameDict["RealName"] = "Lowerarm"
565
+
566
+ if newNameDict["RealName"] == "Spine" or newNameDict["RealName"] == "Neck":
567
+ if newNameDict["Index"] == "":
568
+ newNameDict["Index"] = str(int(1)).zfill(self.name.get_padding_num())
569
+ else:
570
+ newNameDict["Index"] = str(int(newNameDict["Index"]) + 1).zfill(self.name.get_padding_num())
571
+
572
+ newBipName = self.name.combine(newNameDict)
573
+
574
+ bipNode.name = newBipName.lower()
575
+
576
+ # 손가락 바꾸는 부분
577
+ # 5개가 아닌 손가락은 지원하지 않음
578
+ # 손가락 하나의 최대 링크는 3개
579
+ indices = []
580
+ if bipObj.controller.knuckles:
581
+ pass
582
+ else:
583
+ indices = list(range(0, 15, 3))
584
+
585
+ fingerNum = bipObj.controller.fingers
586
+ fingerLinkNum = bipObj.controller.fingerLinks
587
+
588
+ lFingersList = []
589
+ rFingersList = []
590
+
591
+ for i in range(1, fingerNum+1):
592
+ fingers = []
593
+ for j in range(1, fingerLinkNum+1):
594
+ linkIndex = (i-1)*fingerLinkNum + j
595
+ fingerNode = rt.biped.getNode(bipObj.controller, rt.name("lFingers"), link=linkIndex)
596
+ fingers.append(fingerNode)
597
+ lFingersList.append(fingers)
598
+ for i in range(1, fingerNum+1):
599
+ fingers = []
600
+ for j in range(1, fingerLinkNum+1):
601
+ linkIndex = (i-1)*fingerLinkNum + j
602
+ fingerNode = rt.biped.getNode(bipObj.controller, rt.name("rFingers"), link=linkIndex)
603
+ fingers.append(fingerNode)
604
+ rFingersList.append(fingers)
605
+
606
+ fingerName = ["thumb", "index", "middle", "ring", "pinky"]
607
+
608
+ for i, fingers in enumerate(lFingersList):
609
+ for j, item in enumerate(fingers):
610
+ item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
611
+ item.name = self.name.replace_name_part("Index", item.name, str(j+1))
612
+
613
+ fingerNub = self.bone.get_every_children(fingers[-1])[0]
614
+ fingerNub.name = self.name.replace_name_part("RealName", fingerNub.name, fingerName[i])
615
+ fingerNub.name = self.name.remove_name_part("Index", fingerNub.name)
616
+ fingerNub.name = self.name.replace_name_part("Nub", fingerNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
617
+
618
+ for i, fingers in enumerate(rFingersList):
619
+ for j, item in enumerate(fingers):
620
+ item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
621
+ item.name = self.name.replace_name_part("Index", item.name, str(j+1))
622
+
623
+ fingerNub = self.bone.get_every_children(fingers[-1])[0]
624
+ fingerNub.name = self.name.replace_name_part("RealName", fingerNub.name, fingerName[i])
625
+ fingerNub.name = self.name.remove_name_part("Index", fingerNub.name)
626
+ fingerNub.name = self.name.replace_name_part("Nub", fingerNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
627
+
628
+ # Toe 이름 바꾸는 부분
629
+ lToesList = []
630
+ rToesList = []
631
+
632
+ toeNum = bipObj.controller.toes
633
+ toeLinkNum = bipObj.controller.toeLinks
634
+
635
+ # Use the same sequential indexing pattern as fingers
636
+ for i in range(1, toeNum+1):
637
+ toes = []
638
+ for j in range(1, toeLinkNum+1):
639
+ linkIndex = (i-1)*toeLinkNum + j
640
+ toeNode = rt.biped.getNode(bipObj.controller, rt.name("lToes"), link=linkIndex)
641
+ if toeNode:
642
+ toes.append(toeNode)
643
+ if toes:
644
+ lToesList.append(toes)
645
+
646
+ for i in range(1, toeNum+1):
647
+ toes = []
648
+ for j in range(1, toeLinkNum+1):
649
+ linkIndex = (i-1)*toeLinkNum + j
650
+ toeNode = rt.biped.getNode(bipObj.controller, rt.name("rToes"), link=linkIndex)
651
+ if toeNode:
652
+ toes.append(toeNode)
653
+ if toes:
654
+ rToesList.append(toes)
655
+
656
+ for i, toes in enumerate(lToesList):
657
+ for j, item in enumerate(toes):
658
+ item.name = self.name.replace_name_part("RealName", item.name, "ball"+str(i+1))
659
+ item.name = self.name.replace_name_part("Index", item.name, str(j+1))
660
+
661
+ toeNub = self.bone.get_every_children(toes[-1])[0]
662
+ toeNub.name = self.name.replace_name_part("RealName", toeNub.name, "ball"+str(i+1))
663
+ toeNub.name = self.name.remove_name_part("Index", toeNub.name)
664
+ toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
665
+
666
+ for i, toes in enumerate(rToesList):
667
+ for j, item in enumerate(toes):
668
+ item.name = self.name.replace_name_part("RealName", item.name, "ball"+str(i+1))
669
+ item.name = self.name.replace_name_part("Index", item.name, str(j+1))
670
+
671
+ toeNub = self.bone.get_every_children(toes[-1])[0]
672
+ toeNub.name = self.name.replace_name_part("RealName", toeNub.name, "ball"+str(i+1))
673
+ toeNub.name = self.name.remove_name_part("Index", toeNub.name)
674
+ toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
675
+
676
+ if toeNum == 1:
677
+ if toeLinkNum == 1:
678
+ lToesList[0][0].name = self.name.replace_name_part("RealName", lToesList[0][0].name, "ball")
679
+ lToesList[0][0].name = self.name.remove_name_part("Index", lToesList[0][0].name)
680
+ else:
681
+ for i, item in enumerate(lToesList[0]):
682
+ item.name = self.name.replace_name_part("RealName", item.name, "ball")
683
+ item.name = self.name.replace_name_part("Index", item.name, str(i+1))
684
+
685
+ toeNub = self.bone.get_every_children(lToesList[0][-1])[0]
686
+ toeNub.name = self.name.replace_name_part("RealName", toeNub.name, "ball")
687
+ toeNub.name = self.name.remove_name_part("Index", toeNub.name)
688
+ toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
689
+
690
+ if toeLinkNum == 1:
691
+ rToesList[0][0].name = self.name.replace_name_part("RealName", lToesList[0][0].name, "ball")
692
+ rToesList[0][0].name = self.name.remove_name_part("Index", lToesList[0][0].name)
693
+ else:
694
+ for i, item in enumerate(rToesList[0]):
695
+ item.name = self.name.replace_name_part("RealName", item.name, "ball")
696
+ item.name = self.name.replace_name_part("Index", item.name, str(i+1))
697
+
698
+ toeNub = self.bone.get_every_children(rToesList[0][-1])[0]
699
+ toeNub.name = self.name.replace_name_part("RealName", toeNub.name, "ball")
700
+ toeNub.name = self.name.remove_name_part("Index", toeNub.name)
701
+ toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
702
+
703
+ return True