pyjallib 0.1.10__py3-none-any.whl → 0.1.12__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 +7 -7
- pyjallib/max/__init__.py +24 -25
- pyjallib/max/autoClavicle.py +39 -4
- pyjallib/max/bip.py +69 -2
- pyjallib/max/bone.py +37 -0
- pyjallib/max/boneChain.py +182 -0
- pyjallib/max/groinBone.py +65 -29
- pyjallib/max/hip.py +44 -7
- pyjallib/max/kneeBone.py +61 -26
- pyjallib/max/macro/jal_macro_bone.py +1 -1
- pyjallib/max/twistBone.py +59 -40
- pyjallib/max/volumeBone.py +77 -26
- pyjallib/naming.py +27 -2
- pyjallib/namingConfig.py +3 -2
- pyjallib/perforce.py +489 -9
- {pyjallib-0.1.10.dist-info → pyjallib-0.1.12.dist-info}/METADATA +1 -1
- {pyjallib-0.1.10.dist-info → pyjallib-0.1.12.dist-info}/RECORD +18 -21
- pyjallib/max/autoClavicleChain.py +0 -173
- pyjallib/max/groinBoneChain.py +0 -173
- pyjallib/max/twistBoneChain.py +0 -162
- pyjallib/max/volumeBoneChain.py +0 -363
- {pyjallib-0.1.10.dist-info → pyjallib-0.1.12.dist-info}/WHEEL +0 -0
pyjallib/__init__.py
CHANGED
@@ -6,12 +6,12 @@ pyjallib Package
|
|
6
6
|
Python library for game character development pipeline.
|
7
7
|
"""
|
8
8
|
|
9
|
-
__version__ = '0.1.
|
9
|
+
__version__ = '0.1.12'
|
10
10
|
|
11
11
|
# reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
|
12
|
-
from .namePart import NamePart, NamePartType
|
13
|
-
from .naming import Naming
|
14
|
-
from .namingConfig import NamingConfig
|
15
|
-
from .nameToPath import NameToPath
|
16
|
-
from .perforce import Perforce
|
17
|
-
from .reloadModules import reload_modules
|
12
|
+
from pyjallib.namePart import NamePart, NamePartType
|
13
|
+
from pyjallib.naming import Naming
|
14
|
+
from pyjallib.namingConfig import NamingConfig
|
15
|
+
from pyjallib.nameToPath import NameToPath
|
16
|
+
from pyjallib.perforce import Perforce
|
17
|
+
from pyjallib.reloadModules import reload_modules
|
pyjallib/max/__init__.py
CHANGED
@@ -7,37 +7,35 @@ JalTools 3DS 패키지
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
# 모듈 임포트
|
10
|
-
from .header import Header
|
10
|
+
from pyjallib.max.header import Header
|
11
11
|
|
12
|
-
from .name import Name
|
13
|
-
from .anim import Anim
|
12
|
+
from pyjallib.max.name import Name
|
13
|
+
from pyjallib.max.anim import Anim
|
14
14
|
|
15
|
-
from .helper import Helper
|
16
|
-
from .constraint import Constraint
|
17
|
-
from .bone import Bone
|
15
|
+
from pyjallib.max.helper import Helper
|
16
|
+
from pyjallib.max.constraint import Constraint
|
17
|
+
from pyjallib.max.bone import Bone
|
18
18
|
|
19
|
-
from .mirror import Mirror
|
20
|
-
from .layer import Layer
|
21
|
-
from .align import Align
|
22
|
-
from .select import Select
|
23
|
-
from .link import Link
|
19
|
+
from pyjallib.max.mirror import Mirror
|
20
|
+
from pyjallib.max.layer import Layer
|
21
|
+
from pyjallib.max.align import Align
|
22
|
+
from pyjallib.max.select import Select
|
23
|
+
from pyjallib.max.link import Link
|
24
24
|
|
25
|
-
from .bip import Bip
|
26
|
-
from .skin import Skin
|
27
|
-
from .morph import Morph
|
25
|
+
from pyjallib.max.bip import Bip
|
26
|
+
from pyjallib.max.skin import Skin
|
27
|
+
from pyjallib.max.morph import Morph
|
28
28
|
|
29
|
-
from .
|
30
|
-
from .twistBoneChain import TwistBoneChain
|
31
|
-
from .groinBone import GroinBone
|
32
|
-
from .groinBoneChain import GroinBoneChain
|
33
|
-
from .autoClavicle import AutoClavicle
|
34
|
-
from .autoClavicleChain import AutoClavicleChain
|
35
|
-
from .volumeBone import VolumeBone
|
36
|
-
from .volumeBoneChain import VolumeBoneChain
|
37
|
-
from .kneeBone import KneeBone
|
38
|
-
from .hip import Hip
|
29
|
+
from pyjallib.max.boneChain import BoneChain
|
39
30
|
|
40
|
-
from .
|
31
|
+
from pyjallib.max.twistBone import TwistBone
|
32
|
+
from pyjallib.max.groinBone import GroinBone
|
33
|
+
from pyjallib.max.autoClavicle import AutoClavicle
|
34
|
+
from pyjallib.max.volumeBone import VolumeBone
|
35
|
+
from pyjallib.max.kneeBone import KneeBone
|
36
|
+
from pyjallib.max.hip import Hip
|
37
|
+
|
38
|
+
from pyjallib.max.ui.Container import Container
|
41
39
|
|
42
40
|
# 모듈 내보내기
|
43
41
|
__all__ = [
|
@@ -55,6 +53,7 @@ __all__ = [
|
|
55
53
|
'Bip',
|
56
54
|
'Skin',
|
57
55
|
'Morph',
|
56
|
+
'BoneChain',
|
58
57
|
'TwistBone',
|
59
58
|
'TwistBoneChain',
|
60
59
|
'GroinBone',
|
pyjallib/max/autoClavicle.py
CHANGED
@@ -16,6 +16,8 @@ from .bone import Bone
|
|
16
16
|
from .constraint import Constraint
|
17
17
|
from .bip import Bip
|
18
18
|
|
19
|
+
from .boneChain import BoneChain
|
20
|
+
|
19
21
|
|
20
22
|
class AutoClavicle:
|
21
23
|
"""
|
@@ -166,12 +168,45 @@ class AutoClavicle:
|
|
166
168
|
result = {
|
167
169
|
"Bones": genBones,
|
168
170
|
"Helpers": genHelpers,
|
169
|
-
"
|
170
|
-
"
|
171
|
-
"LiftScale": liftScale
|
171
|
+
"SourceBones": [inClavicle, inUpperArm],
|
172
|
+
"Parameters": [liftScale]
|
172
173
|
}
|
173
174
|
|
174
175
|
# 메소드 호출 후 데이터 초기화
|
175
176
|
self.reset()
|
176
177
|
|
177
|
-
return result
|
178
|
+
return BoneChain.from_result(result)
|
179
|
+
|
180
|
+
def create_bones_from_chain(self, inBoneChain: BoneChain):
|
181
|
+
"""
|
182
|
+
기존 BoneChain 객체에서 자동 쇄골 뼈를 생성합니다.
|
183
|
+
기존 설정을 복원하거나 저장된 데이터에서 쇄골 셋업을 재생성할 때 사용합니다.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
inBoneChain (BoneChain): 자동 쇄골 정보를 포함한 BoneChain 객체
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
BoneChain: 업데이트된 BoneChain 객체 또는 실패 시 None
|
190
|
+
"""
|
191
|
+
if not inBoneChain or inBoneChain.is_empty():
|
192
|
+
return None
|
193
|
+
|
194
|
+
inBoneChain.delete()
|
195
|
+
|
196
|
+
# BoneChain에서 필요한 정보 추출
|
197
|
+
sourceBones = inBoneChain.sourceBones
|
198
|
+
parameters = inBoneChain.parameters
|
199
|
+
|
200
|
+
# 필수 소스 본 확인
|
201
|
+
if len(sourceBones) < 2 or not rt.isValidNode(sourceBones[0]) or not rt.isValidNode(sourceBones[1]):
|
202
|
+
return None
|
203
|
+
|
204
|
+
# 파라미터 가져오기 (또는 기본값 사용)
|
205
|
+
liftScale = parameters[0] if len(parameters) > 0 else 0.8
|
206
|
+
|
207
|
+
# 쇄골 생성
|
208
|
+
inClavicle = sourceBones[0]
|
209
|
+
inUpperArm = sourceBones[1]
|
210
|
+
|
211
|
+
# 새로운 쇄골 생성
|
212
|
+
return self.create_bones(inClavicle, inUpperArm, liftScale)
|
pyjallib/max/bip.py
CHANGED
@@ -439,6 +439,71 @@ class Bip:
|
|
439
439
|
if colNum > 0:
|
440
440
|
rt.biped.deleteAllCopyCollections(inBipRoot.controller)
|
441
441
|
|
442
|
+
def collapse_layers(self, inBipRoot):
|
443
|
+
"""
|
444
|
+
Biped 레이어 병합
|
445
|
+
|
446
|
+
Args:
|
447
|
+
inBipRoot: 대상 Biped 객체
|
448
|
+
"""
|
449
|
+
if not self.is_biped_object(inBipRoot):
|
450
|
+
return False
|
451
|
+
|
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, progress_callback=None):
|
458
|
+
"""
|
459
|
+
Biped BIP 파일 저장
|
460
|
+
|
461
|
+
Args:
|
462
|
+
inBipRoot: 저장 대상 Biped 루트 노드
|
463
|
+
inFile: 저장할 BIP 파일 경로
|
464
|
+
inBakeAllKeys: 모든 키를 베이크할지 여부 (기본값: True)
|
465
|
+
inCollapseLayers: 레이어를 병합할지 여부 (기본값: True)
|
466
|
+
|
467
|
+
Returns:
|
468
|
+
bool: 저장 성공 시 True, 실패 시 False
|
469
|
+
"""
|
470
|
+
if not self.is_biped_object(inBipRoot):
|
471
|
+
return False
|
472
|
+
|
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
|
+
rt.biped.saveBipFile(inBipRoot.controller, inFile)
|
505
|
+
return True
|
506
|
+
|
442
507
|
def link_base_skeleton(self, skinBoneBaseName="b"):
|
443
508
|
"""
|
444
509
|
기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
|
@@ -608,6 +673,7 @@ class Bip:
|
|
608
673
|
for i, fingers in enumerate(lFingersList):
|
609
674
|
for j, item in enumerate(fingers):
|
610
675
|
item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
|
676
|
+
item.name = self.name.replace_name_part("Side", item.name, self.name.get_name_part_value_by_description("Side", "Left"))
|
611
677
|
item.name = self.name.replace_name_part("Index", item.name, str(j+1))
|
612
678
|
|
613
679
|
fingerNub = self.bone.get_every_children(fingers[-1])[0]
|
@@ -618,6 +684,7 @@ class Bip:
|
|
618
684
|
for i, fingers in enumerate(rFingersList):
|
619
685
|
for j, item in enumerate(fingers):
|
620
686
|
item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
|
687
|
+
item.name = self.name.replace_name_part("Side", item.name, self.name.get_name_part_value_by_description("Side", "Right"))
|
621
688
|
item.name = self.name.replace_name_part("Index", item.name, str(j+1))
|
622
689
|
|
623
690
|
fingerNub = self.bone.get_every_children(fingers[-1])[0]
|
@@ -688,8 +755,8 @@ class Bip:
|
|
688
755
|
toeNub.name = self.name.replace_name_part("Nub", toeNub.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
|
689
756
|
|
690
757
|
if toeLinkNum == 1:
|
691
|
-
rToesList[0][0].name = self.name.replace_name_part("RealName",
|
692
|
-
rToesList[0][0].name = self.name.remove_name_part("Index",
|
758
|
+
rToesList[0][0].name = self.name.replace_name_part("RealName", rToesList[0][0].name, "ball")
|
759
|
+
rToesList[0][0].name = self.name.remove_name_part("Index", rToesList[0][0].name)
|
693
760
|
else:
|
694
761
|
for i, item in enumerate(rToesList[0]):
|
695
762
|
item.name = self.name.replace_name_part("RealName", item.name, "ball")
|
pyjallib/max/bone.py
CHANGED
@@ -239,6 +239,7 @@ class Bone:
|
|
239
239
|
nubBone.backfin = False
|
240
240
|
nubBone.sidefins = False
|
241
241
|
nubBone.name = self.name.remove_name_part("Index", inName)
|
242
|
+
nubBone.name = self.name.remove_name_part("Nub", nubBone.name)
|
242
243
|
nubBone.name = self.name.replace_name_part("Nub", nubBone.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
|
243
244
|
|
244
245
|
# 화면 갱신 재개
|
@@ -888,6 +889,7 @@ class Bone:
|
|
888
889
|
returnBones = []
|
889
890
|
spine3 = None
|
890
891
|
neck = None
|
892
|
+
head = None
|
891
893
|
|
892
894
|
handL = None
|
893
895
|
handR = None
|
@@ -905,6 +907,8 @@ class Bone:
|
|
905
907
|
spine3 = item
|
906
908
|
if rt.matchPattern(item.name, pattern="*neck 01"):
|
907
909
|
neck = item
|
910
|
+
if rt.matchPattern(item.name, pattern="*head"):
|
911
|
+
head = item
|
908
912
|
if rt.matchPattern(item.name, pattern="*hand*l"):
|
909
913
|
handL = item
|
910
914
|
if rt.matchPattern(item.name, pattern="*hand*r"):
|
@@ -924,6 +928,8 @@ class Bone:
|
|
924
928
|
|
925
929
|
filteringChar = self.name._get_filtering_char(inBoneArray[-1].name)
|
926
930
|
isLower = inBoneArray[-1].name[0].islower()
|
931
|
+
|
932
|
+
# Spine 4,5 생성
|
927
933
|
spineName = self.name.get_name_part_value_by_description("Base", "Biped") + filteringChar + "Spine"
|
928
934
|
|
929
935
|
spine4 = self.create_nub_bone(spineName, 2)
|
@@ -946,6 +952,23 @@ class Bone:
|
|
946
952
|
returnBones.append(spine4)
|
947
953
|
returnBones.append(spine5)
|
948
954
|
|
955
|
+
# 목 생성
|
956
|
+
neckName = self.name.get_name_part_value_by_description("Base", "Biped") + filteringChar + "Neck"
|
957
|
+
|
958
|
+
nekc2 = self.create_nub_bone(neckName, 2)
|
959
|
+
|
960
|
+
nekc2.name = self.name.replace_name_part("Index", nekc2.name, "2")
|
961
|
+
nekc2.name = self.name.remove_name_part("Nub", nekc2.name)
|
962
|
+
if isLower:
|
963
|
+
nekc2.name = nekc2.name.lower()
|
964
|
+
|
965
|
+
neckDistance = rt.distance(neck, head)/2.0
|
966
|
+
rt.setProperty(nekc2, "transform", neck.transform)
|
967
|
+
self.anim.move_local(nekc2, neckDistance, 0, 0)
|
968
|
+
|
969
|
+
returnBones.append(nekc2)
|
970
|
+
|
971
|
+
# 손가락용 메타카팔 생성
|
949
972
|
for i, finger in enumerate(lFingers):
|
950
973
|
knuckleBoneName = self.name.add_suffix_to_real_name(finger.name, filteringChar+knuckleName)
|
951
974
|
knuckleBoneName = self.name.remove_name_part("Index", knuckleBoneName)
|
@@ -992,6 +1015,7 @@ class Bone:
|
|
992
1015
|
returnBones = []
|
993
1016
|
|
994
1017
|
spine3 = None
|
1018
|
+
neck = None
|
995
1019
|
|
996
1020
|
handL = None
|
997
1021
|
handR = None
|
@@ -1001,6 +1025,8 @@ class Bone:
|
|
1001
1025
|
for item in inBipArray:
|
1002
1026
|
if rt.matchPattern(item.name, pattern="*spine 03"):
|
1003
1027
|
spine3 = item
|
1028
|
+
if rt.matchPattern(item.name, pattern="*neck 01"):
|
1029
|
+
neck = item
|
1004
1030
|
if rt.matchPattern(item.name, pattern="*hand*l"):
|
1005
1031
|
handL = item
|
1006
1032
|
if rt.matchPattern(item.name, pattern="*hand*r"):
|
@@ -1009,6 +1035,8 @@ class Bone:
|
|
1009
1035
|
for item in inMissingBoneArray:
|
1010
1036
|
if rt.matchPattern(item.name, pattern="*spine*"):
|
1011
1037
|
item.parent = spine3
|
1038
|
+
if rt.matchPattern(item.name, pattern="*neck*"):
|
1039
|
+
item.parent = neck
|
1012
1040
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
|
1013
1041
|
item.parent = handL
|
1014
1042
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*r"):
|
@@ -1025,6 +1053,8 @@ class Bone:
|
|
1025
1053
|
spine5 = None
|
1026
1054
|
|
1027
1055
|
neck = None
|
1056
|
+
neck2 = None
|
1057
|
+
head = None
|
1028
1058
|
clavicleL = None
|
1029
1059
|
clavicleR = None
|
1030
1060
|
|
@@ -1042,6 +1072,8 @@ class Bone:
|
|
1042
1072
|
spine3 = item
|
1043
1073
|
if rt.matchPattern(item.name, pattern="*neck*01"):
|
1044
1074
|
neck = item
|
1075
|
+
if rt.matchPattern(item.name, pattern="*head*"):
|
1076
|
+
head = item
|
1045
1077
|
if rt.matchPattern(item.name, pattern="*clavicle*l"):
|
1046
1078
|
clavicleL = item
|
1047
1079
|
if rt.matchPattern(item.name, pattern="*clavicle*r"):
|
@@ -1069,6 +1101,11 @@ class Bone:
|
|
1069
1101
|
neck.parent = spine5
|
1070
1102
|
clavicleL.parent = spine5
|
1071
1103
|
clavicleR.parent = spine5
|
1104
|
+
|
1105
|
+
if rt.matchPattern(item.name, pattern="*neck*02"):
|
1106
|
+
neck2 = item
|
1107
|
+
item.parent = neck
|
1108
|
+
head.parent = neck2
|
1072
1109
|
|
1073
1110
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
|
1074
1111
|
item.parent = handL
|
@@ -0,0 +1,182 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
뼈대 체인(Bone Chain) 기본 클래스 - 뼈대 체인 관리를 위한 공통 기능 제공
|
6
|
+
|
7
|
+
이 모듈은 다양한 뼈대 체인 클래스의 기본 부모 클래스로 사용됩니다.
|
8
|
+
AutoClavicleChain, GroinBoneChain, VolumeBoneChain, TwistBoneChain 등의
|
9
|
+
특수 목적 뼈대 체인들이 상속받아 사용하며, 공통된 관리 기능을 제공합니다.
|
10
|
+
|
11
|
+
기본 클래스는 다음과 같은 공통 기능을 제공합니다:
|
12
|
+
- 체인의 뼈대 및 헬퍼 관리
|
13
|
+
- 체인 비우기/삭제 기능
|
14
|
+
- 체인 상태 확인 기능
|
15
|
+
"""
|
16
|
+
|
17
|
+
from pymxs import runtime as rt
|
18
|
+
|
19
|
+
|
20
|
+
class BoneChain:
|
21
|
+
"""
|
22
|
+
뼈대 체인을 관리하는 기본 클래스
|
23
|
+
|
24
|
+
다양한 뼈대 체인의 공통 기능을 담당하는 부모 클래스입니다.
|
25
|
+
뼈대와 헬퍼를 저장하고 기본적인 조작 기능을 제공합니다.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, inResult=None):
|
29
|
+
"""
|
30
|
+
클래스 초기화
|
31
|
+
|
32
|
+
Args:
|
33
|
+
inResult (dict, optional): 뼈대 생성 결과 데이터를 담은 딕셔너리. 기본값은 None
|
34
|
+
"""
|
35
|
+
# 기본 속성 초기화
|
36
|
+
if inResult is None:
|
37
|
+
# inResult가 None이면 속성들을 빈 리스트로 초기화
|
38
|
+
self.bones = []
|
39
|
+
self.helpers = []
|
40
|
+
self.result = {} # 빈 딕셔너리로 초기화
|
41
|
+
self.sourceBones = []
|
42
|
+
self.parameters = []
|
43
|
+
else:
|
44
|
+
self.bones = inResult.get("Bones", [])
|
45
|
+
self.helpers = inResult.get("Helpers", [])
|
46
|
+
self.result = inResult # 원본 결과 보존
|
47
|
+
self.sourceBones = inResult.get("SourceBones", [])
|
48
|
+
self.parameters = inResult.get("Parameters", [])
|
49
|
+
|
50
|
+
def is_empty(self):
|
51
|
+
"""
|
52
|
+
체인이 비어있는지 확인
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
bool: 체인이 비어있으면 True, 아니면 False
|
56
|
+
"""
|
57
|
+
return len(self.bones) == 0
|
58
|
+
|
59
|
+
def clear(self):
|
60
|
+
"""체인의 모든 뼈대와 헬퍼 참조 제거"""
|
61
|
+
self.bones = []
|
62
|
+
self.helpers = []
|
63
|
+
self.sourceBones = []
|
64
|
+
self.parameters = []
|
65
|
+
|
66
|
+
def delete(self):
|
67
|
+
"""
|
68
|
+
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
bool: 삭제 성공 여부
|
72
|
+
"""
|
73
|
+
if self.is_empty() and not self.helpers:
|
74
|
+
return False
|
75
|
+
|
76
|
+
try:
|
77
|
+
# 뼈대 삭제
|
78
|
+
if self.bones:
|
79
|
+
for bone in self.bones:
|
80
|
+
if rt.isValidNode(bone):
|
81
|
+
rt.delete(bone)
|
82
|
+
|
83
|
+
# 헬퍼 삭제
|
84
|
+
if self.helpers:
|
85
|
+
for helper in self.helpers:
|
86
|
+
if rt.isValidNode(helper):
|
87
|
+
rt.delete(helper)
|
88
|
+
|
89
|
+
self.bones = []
|
90
|
+
self.helpers = []
|
91
|
+
|
92
|
+
return True
|
93
|
+
except:
|
94
|
+
return False
|
95
|
+
|
96
|
+
def delete_all(self):
|
97
|
+
"""
|
98
|
+
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
bool: 삭제 성공 여부
|
102
|
+
"""
|
103
|
+
if self.is_empty() and not self.helpers:
|
104
|
+
return False
|
105
|
+
|
106
|
+
try:
|
107
|
+
# 뼈대 삭제
|
108
|
+
if self.bones:
|
109
|
+
for bone in self.bones:
|
110
|
+
if rt.isValidNode(bone):
|
111
|
+
rt.delete(bone)
|
112
|
+
|
113
|
+
# 헬퍼 삭제
|
114
|
+
if self.helpers:
|
115
|
+
for helper in self.helpers:
|
116
|
+
if rt.isValidNode(helper):
|
117
|
+
rt.delete(helper)
|
118
|
+
|
119
|
+
self.clear()
|
120
|
+
return True
|
121
|
+
except:
|
122
|
+
return False
|
123
|
+
|
124
|
+
def get_bones(self):
|
125
|
+
"""
|
126
|
+
체인의 모든 뼈대 가져오기
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
list: 모든 뼈대 객체의 배열
|
130
|
+
"""
|
131
|
+
if self.is_empty():
|
132
|
+
return []
|
133
|
+
|
134
|
+
return self.bones
|
135
|
+
|
136
|
+
def get_helpers(self):
|
137
|
+
"""
|
138
|
+
체인의 모든 헬퍼 가져오기
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
list: 모든 헬퍼 객체의 배열
|
142
|
+
"""
|
143
|
+
if not self.helpers:
|
144
|
+
return []
|
145
|
+
|
146
|
+
return self.helpers
|
147
|
+
|
148
|
+
@classmethod
|
149
|
+
def from_result(cls, inResult):
|
150
|
+
"""
|
151
|
+
결과 딕셔너리로부터 체인 인스턴스 생성
|
152
|
+
|
153
|
+
Args:
|
154
|
+
inResult (dict): 뼈대 생성 결과를 담은 딕셔너리
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
BoneChain: 생성된 체인 인스턴스
|
158
|
+
"""
|
159
|
+
return cls(inResult)
|
160
|
+
|
161
|
+
def update_from_result(self, inResult):
|
162
|
+
"""
|
163
|
+
기존 체인 인스턴스를 결과 딕셔너리로부터 업데이트
|
164
|
+
|
165
|
+
이미 생성된 체인 객체의 내용을 새로운 결과 데이터로 갱신합니다.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
inResult (dict): 뼈대 생성 결과를 담은 딕셔너리
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
self: 메서드 체이닝을 위한 자기 자신 반환
|
172
|
+
"""
|
173
|
+
if inResult is None:
|
174
|
+
return self
|
175
|
+
|
176
|
+
self.bones = inResult.get("Bones", [])
|
177
|
+
self.helpers = inResult.get("Helpers", [])
|
178
|
+
self.result = inResult # 원본 결과 보존
|
179
|
+
self.sourceBones = inResult.get("SourceBones", [])
|
180
|
+
self.parameters = inResult.get("Parameters", [])
|
181
|
+
|
182
|
+
return self
|