pyjallib 0.1.12__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 +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 +34 -0
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.13.dist-info}/METADATA +1 -1
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.13.dist-info}/RECORD +22 -18
- {pyjallib-0.1.12.dist-info → pyjallib-0.1.13.dist-info}/WHEEL +0 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
FBXHandler 모듈
|
6
|
+
3ds Max에서 FBX 파일을 익스포트/임포트하는 기능을 제공
|
7
|
+
이 모듈은 pymxs를 사용하여 3ds Max와 통신하며, FBX 익스포트 및 임포트 옵션을 설정하고 파일을 처리합니다.
|
8
|
+
"""
|
9
|
+
|
10
|
+
from pymxs import runtime as rt
|
11
|
+
import os
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import List, Optional, Dict, Any
|
14
|
+
|
15
|
+
class FBXHandler:
|
16
|
+
"""
|
17
|
+
3ds Max FBX 파일 익스포트/임포트를 위한 클래스
|
18
|
+
pymxs를 사용하여 3ds Max와 통신
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
"""FBX 핸들러 초기화"""
|
23
|
+
self._setup_fbx_plugin()
|
24
|
+
|
25
|
+
def _setup_fbx_plugin(self):
|
26
|
+
"""FBX 플러그인 로드 및 초기화"""
|
27
|
+
rt.pluginManager.loadClass(rt.FbxExporter)
|
28
|
+
rt.pluginManager.loadClass(rt.FbxImporter)
|
29
|
+
|
30
|
+
def _get_export_fbx_class_index(self) -> int:
|
31
|
+
"""FBX 익스포터 클래스 인덱스 가져오기"""
|
32
|
+
exporterPlugin = rt.exporterPlugin
|
33
|
+
for i, cls in enumerate(exporterPlugin.classes):
|
34
|
+
if "FBX" in str(cls):
|
35
|
+
return i + 1 # 1-based index
|
36
|
+
return 0
|
37
|
+
|
38
|
+
def _get_import_fbx_class_index(self) -> int:
|
39
|
+
"""FBX 임포터 클래스 인덱스 가져오기"""
|
40
|
+
importerPlugin = rt.importerPlugin
|
41
|
+
for i, cls in enumerate(importerPlugin.classes):
|
42
|
+
if "FBX" in str(cls):
|
43
|
+
return i + 1 # 1-based index
|
44
|
+
return 0
|
45
|
+
|
46
|
+
def _set_export_options(self):
|
47
|
+
"""FBX 익스포트 옵션 설정"""
|
48
|
+
# FBX 익스포트 프리셋 리셋
|
49
|
+
rt.FBXExporterSetParam("ResetExport")
|
50
|
+
|
51
|
+
# 지오메트리 옵션
|
52
|
+
rt.FBXExporterSetParam("SmoothingGroups", True)
|
53
|
+
rt.FBXExporterSetParam("NormalsPerPoly", False)
|
54
|
+
rt.FBXExporterSetParam("TangentSpaceExport", True)
|
55
|
+
rt.FBXExporterSetParam("SmoothMeshExport", False)
|
56
|
+
rt.FBXExporterSetParam("Preserveinstances", False)
|
57
|
+
rt.FBXExporterSetParam("SelectionSetExport", False)
|
58
|
+
rt.FBXExporterSetParam("GeomAsBone", False)
|
59
|
+
rt.FBXExporterSetParam("Triangulate", False)
|
60
|
+
rt.FBXExporterSetParam("PreserveEdgeOrientation", True)
|
61
|
+
|
62
|
+
# 애니메이션 옵션
|
63
|
+
rt.FBXExporterSetParam("Animation", True)
|
64
|
+
rt.FBXExporterSetParam("UseSceneName", True)
|
65
|
+
rt.FBXExporterSetParam("Removesinglekeys", False)
|
66
|
+
rt.FBXExporterSetParam("BakeAnimation", True)
|
67
|
+
rt.FBXExporterSetParam("Skin", True)
|
68
|
+
rt.FBXExporterSetParam("Shape", True)
|
69
|
+
|
70
|
+
# 포인트 캐시
|
71
|
+
rt.FBXExporterSetParam("PointCache", False)
|
72
|
+
|
73
|
+
# 카메라 및 라이트
|
74
|
+
rt.FBXExporterSetParam("Cameras", False)
|
75
|
+
rt.FBXExporterSetParam("Lights", False)
|
76
|
+
|
77
|
+
# 텍스처
|
78
|
+
rt.FBXExporterSetParam("EmbedTextures", False)
|
79
|
+
|
80
|
+
# 기타 옵션
|
81
|
+
rt.FBXExporterSetParam("UpAxis", "Z")
|
82
|
+
rt.FBXExporterSetParam("GenerateLog", False)
|
83
|
+
rt.FBXExporterSetParam("ShowWarnings", False)
|
84
|
+
rt.FBXExporterSetParam("ASCII", False)
|
85
|
+
rt.FBXExporterSetParam("FileVersion", "FBX202031")
|
86
|
+
|
87
|
+
def _set_import_options(self, inImportMode: str = "add_and_update_animation", inUpAxis: str = "Z"):
|
88
|
+
"""FBX 임포트 옵션 설정
|
89
|
+
|
90
|
+
Args:
|
91
|
+
inImportMode: 임포트 모드 ('add_and_update_animation' 또는 'update_animation')
|
92
|
+
"""
|
93
|
+
# FBX 임포트 프리셋 리셋
|
94
|
+
rt.FBXImporterSetParam("ResetImport")
|
95
|
+
|
96
|
+
# 임포트 모드 설정
|
97
|
+
if inImportMode == "update_animation":
|
98
|
+
rt.FBXImporterSetParam("Mode", rt.Name("exmerge")) # Update Animation 모드
|
99
|
+
else: # "add_and_update_animation" (기본값)
|
100
|
+
rt.FBXImporterSetParam("Mode", rt.Name("merge")) # Add and Update Animation 모드
|
101
|
+
|
102
|
+
rt.FBXImporterSetParam("SmoothingGroups", True)
|
103
|
+
rt.FBXImporterSetParam("Animation", True)
|
104
|
+
rt.FBXImporterSetParam("BakeAnimationLayers", True)
|
105
|
+
rt.FBXImporterSetParam("FillTimeline", True)
|
106
|
+
rt.FBXImporterSetParam("Skin", True)
|
107
|
+
rt.FBXImporterSetParam("Shape", True)
|
108
|
+
rt.FBXImporterSetParam("Cameras", False)
|
109
|
+
rt.FBXImporterSetParam("Lights", False)
|
110
|
+
rt.FBXImporterSetParam("GenerateLog", False)
|
111
|
+
rt.FBXImporterSetParam("GenerateLog", False)
|
112
|
+
rt.FBXImporterSetParam("ImportBoneAsDummy", True)
|
113
|
+
rt.FBXImporterSetParam("UpAxis", inUpAxis)
|
114
|
+
|
115
|
+
def set_fbx_exporting_anim_range(self, inStartFrame: Optional[int] = None, inEndFrame: Optional[int] = None):
|
116
|
+
"""애니메이션 범위 설정
|
117
|
+
|
118
|
+
Args:
|
119
|
+
inStartFrame: 시작 프레임 (None이면 현재 애니메이션 범위 사용)
|
120
|
+
inEndFrame: 끝 프레임 (None이면 현재 애니메이션 범위 사용)
|
121
|
+
"""
|
122
|
+
if inStartFrame is None or inEndFrame is None:
|
123
|
+
# 매개변수가 없으면 현재 Max 파일의 애니메이션 범위 사용
|
124
|
+
animRange = rt.animationrange
|
125
|
+
startFrame = inStartFrame if inStartFrame is not None else animRange.start
|
126
|
+
endFrame = inEndFrame if inEndFrame is not None else animRange.end
|
127
|
+
else:
|
128
|
+
# 매개변수가 있으면 해당 값 사용
|
129
|
+
startFrame = inStartFrame
|
130
|
+
endFrame = inEndFrame
|
131
|
+
|
132
|
+
rt.FBXExporterSetParam("BakeFrameStart", startFrame)
|
133
|
+
rt.FBXExporterSetParam("BakeFrameEnd", endFrame)
|
134
|
+
|
135
|
+
def export_selection(self, inExportFile: str, inMatchAnimRange: bool = True, inStartFrame: Optional[int] = None, inEndFrame: Optional[int] = None) -> bool:
|
136
|
+
"""
|
137
|
+
선택된 오브젝트를 FBX로 익스포트
|
138
|
+
|
139
|
+
Args:
|
140
|
+
inExportFile: 익스포트할 파일 경로
|
141
|
+
inMatchAnimRange: 현재 애니메이션 범위에 맞출지 여부
|
142
|
+
inStartFrame: 시작 프레임 (None이면 현재 애니메이션 범위 사용)
|
143
|
+
inEndFrame: 끝 프레임 (None이면 현재 애니메이션 범위 사용)
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
bool: 익스포트 성공 여부
|
147
|
+
"""
|
148
|
+
# 파일 경로 검증 및 디렉토리 생성
|
149
|
+
filePath = Path(inExportFile)
|
150
|
+
filePath.parent.mkdir(parents=True, exist_ok=True)
|
151
|
+
|
152
|
+
# 선택된 오브젝트가 있는지 확인
|
153
|
+
if len(rt.selection) == 0:
|
154
|
+
return False
|
155
|
+
|
156
|
+
# FBX 익스포터 클래스 인덱스 가져오기
|
157
|
+
exportClassIndex = self._get_export_fbx_class_index()
|
158
|
+
if exportClassIndex == 0:
|
159
|
+
return False
|
160
|
+
|
161
|
+
# FBX 익스포트 옵션 설정
|
162
|
+
self._set_export_options()
|
163
|
+
|
164
|
+
# 애니메이션 범위 설정
|
165
|
+
if inMatchAnimRange:
|
166
|
+
self.set_fbx_exporting_anim_range(inStartFrame, inEndFrame)
|
167
|
+
|
168
|
+
# 익스포트 실행
|
169
|
+
exporterPlugin = rt.exporterPlugin
|
170
|
+
result = rt.exportFile(
|
171
|
+
str(filePath),
|
172
|
+
rt.Name("noPrompt"),
|
173
|
+
using=exporterPlugin.classes[exportClassIndex - 1], # 0-based index로 변환
|
174
|
+
selectedOnly=True
|
175
|
+
)
|
176
|
+
|
177
|
+
return result
|
178
|
+
|
179
|
+
def import_fbx(self, inImportFile: str, inImportMode: str = "add_and_update_animation", inUpAxis: str = "Z") -> bool:
|
180
|
+
"""
|
181
|
+
FBX 파일을 임포트
|
182
|
+
|
183
|
+
Args:
|
184
|
+
inImportFile: 임포트할 파일 경로
|
185
|
+
inImportMode: 임포트 모드 ('add_and_update_animation' 또는 'update_animation')
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
bool: 임포트 성공 여부
|
189
|
+
"""
|
190
|
+
# 파일 존재 여부 확인
|
191
|
+
filePath = Path(inImportFile)
|
192
|
+
if not filePath.exists():
|
193
|
+
return False
|
194
|
+
|
195
|
+
# FBX 임포터 클래스 인덱스 가져오기
|
196
|
+
importClassIndex = self._get_import_fbx_class_index()
|
197
|
+
if importClassIndex == 0:
|
198
|
+
return False
|
199
|
+
|
200
|
+
# FBX 임포트 옵션 설정
|
201
|
+
self._set_import_options(inImportMode, inUpAxis)
|
202
|
+
|
203
|
+
# 임포트 실행
|
204
|
+
importerPlugin = rt.importerPlugin
|
205
|
+
result = rt.importFile(
|
206
|
+
str(filePath),
|
207
|
+
rt.Name("noPrompt"),
|
208
|
+
using=importerPlugin.classes[importClassIndex - 1] # 0-based index로 변환
|
209
|
+
)
|
210
|
+
|
211
|
+
return result
|
212
|
+
|
213
|
+
def reset_import_options(self):
|
214
|
+
"""FBX 임포트 옵션을 기본값으로 리셋"""
|
215
|
+
rt.FBXImporterSetParam("ResetImport")
|
pyjallib/max/groinBone.py
CHANGED
@@ -110,7 +110,7 @@ class GroinBone:
|
|
110
110
|
lThighTwistHelperName = self.name.replace_name_part("Index", lThighTwistHelperName, "0")
|
111
111
|
lThighTwistHelper = self.helper.create_point(lThighTwistHelperName)
|
112
112
|
lThighTwistHelper.transform = pelvisHelper.transform
|
113
|
-
lThighTwistHelper.position = inLThighTwist.position
|
113
|
+
lThighTwistHelper.position = inLThighTwist.transform.position
|
114
114
|
lThighTwistHelper.parent = inLThighTwist
|
115
115
|
self.helper.set_shape_to_box(lThighTwistHelper)
|
116
116
|
|
@@ -119,7 +119,7 @@ class GroinBone:
|
|
119
119
|
rThighTwistHelperName = self.name.replace_name_part("Index", rThighTwistHelperName, "0")
|
120
120
|
rThighTwistHelper = self.helper.create_point(rThighTwistHelperName)
|
121
121
|
rThighTwistHelper.transform = pelvisHelper.transform
|
122
|
-
rThighTwistHelper.position = inRThighTwist.position
|
122
|
+
rThighTwistHelper.position = inRThighTwist.transform.position
|
123
123
|
rThighTwistHelper.parent = inRThighTwist
|
124
124
|
self.helper.set_shape_to_box(rThighTwistHelper)
|
125
125
|
|
@@ -193,4 +193,4 @@ class GroinBone:
|
|
193
193
|
inLThighTwist = sourceBones[1]
|
194
194
|
inRThighTwist = sourceBones[2]
|
195
195
|
|
196
|
-
return self.create_bone(inPelvis, inLThighTwist, inRThighTwist, pelvisWeight, thighWeight)
|
196
|
+
return self.create_bone(inPelvis, inLThighTwist, inRThighTwist, inPelvisWeight=pelvisWeight, inThighWeight=thighWeight)
|
pyjallib/max/header.py
CHANGED
@@ -33,6 +33,11 @@ from .hip import Hip
|
|
33
33
|
|
34
34
|
from .morph import Morph
|
35
35
|
|
36
|
+
from .rootMotion import RootMotion
|
37
|
+
|
38
|
+
from .fbxHandler import FBXHandler
|
39
|
+
from .toolManager import ToolManager
|
40
|
+
|
36
41
|
class Header:
|
37
42
|
"""
|
38
43
|
JalLib.max 패키지의 헤더 모듈
|
@@ -79,7 +84,11 @@ class Header:
|
|
79
84
|
|
80
85
|
self.morph = Morph()
|
81
86
|
|
82
|
-
self.
|
87
|
+
self.rootMotion = RootMotion(nameService=self.name, animService=self.anim, constraintService=self.constraint, helperService=self.helper, bipService=self.bip)
|
88
|
+
|
89
|
+
self.fbx = FBXHandler()
|
90
|
+
|
91
|
+
self.toolManager = ToolManager()
|
83
92
|
|
84
93
|
def update_nameConifg(self, configPath):
|
85
94
|
"""
|
@@ -89,18 +98,6 @@ class Header:
|
|
89
98
|
configPath: ConfigPath 인스턴스
|
90
99
|
"""
|
91
100
|
self.name.load_from_config_file(configPath)
|
92
|
-
|
93
|
-
def add_tool(self, tool):
|
94
|
-
"""
|
95
|
-
도구를 추가합니다.
|
96
|
-
|
97
|
-
Args:
|
98
|
-
tool: 추가할 도구
|
99
|
-
"""
|
100
|
-
if tool in self.tools:
|
101
|
-
self.tools.remove(tool)
|
102
|
-
|
103
|
-
self.tools.append(tool)
|
104
101
|
|
105
102
|
# 모듈 레벨에서 전역 인스턴스 생성
|
106
103
|
_pyjallibmaxheader = Header.get_instance()
|
pyjallib/max/hip.py
CHANGED
@@ -177,7 +177,7 @@ class Hip:
|
|
177
177
|
self.helpers.append(thighPosHelper)
|
178
178
|
self.helpers.append(thighRotRootHelper)
|
179
179
|
|
180
|
-
def assing_constraint(self, inCalf, inPelvisWeight=0
|
180
|
+
def assing_constraint(self, inCalf, inPelvisWeight=60.0, inThighWeight=40.0, inPushAmount=5.0):
|
181
181
|
self.calf = inCalf
|
182
182
|
self.pelvisWeight = inPelvisWeight
|
183
183
|
self.thighWeight = inThighWeight
|
@@ -188,8 +188,8 @@ class Hip:
|
|
188
188
|
distanceDir = -1.0 if rt.dot(inObjXAxisVec, facingDirVec) > 0 else 1.0
|
189
189
|
|
190
190
|
rotConst = self.const.assign_rot_const_multi(self.thighRotHelper, [self.pelvisHelper, self.thighTwistHelper])
|
191
|
-
rotConst.setWeight(1, self.pelvisWeight
|
192
|
-
rotConst.setWeight(2, self.thighWeight
|
191
|
+
rotConst.setWeight(1, self.pelvisWeight)
|
192
|
+
rotConst.setWeight(2, self.thighWeight)
|
193
193
|
|
194
194
|
localRotRefTm = self.thighRotHelper.transform * rt.inverse(self.thighRotRootHelper.transform)
|
195
195
|
posConst = self.const.assign_pos_script_controller(self.thighPosHelper)
|
@@ -200,7 +200,7 @@ class Hip:
|
|
200
200
|
posConst.setExpression(self.posScriptExpression)
|
201
201
|
posConst.update()
|
202
202
|
|
203
|
-
def create_bone(self, inPelvis, inThigh, inThighTwist, inCalf, pushAmount=5.0, inPelvisWeight=0
|
203
|
+
def create_bone(self, inPelvis, inThigh, inThighTwist, inCalf, pushAmount=5.0, inPelvisWeight=60.0, inThighWeight=40.0):
|
204
204
|
if not rt.isValidNode(inPelvis) or not rt.isValidNode(inThigh) or not rt.isValidNode(inThighTwist):
|
205
205
|
return False
|
206
206
|
|
pyjallib/max/kneeBone.py
CHANGED
@@ -66,6 +66,7 @@ class KneeBone:
|
|
66
66
|
self.calfTwistHelpers = []
|
67
67
|
|
68
68
|
self.middleBones = []
|
69
|
+
self.middleHelper = None
|
69
70
|
|
70
71
|
self.liftScale = 0.05
|
71
72
|
|
@@ -177,7 +178,7 @@ class KneeBone:
|
|
177
178
|
|
178
179
|
calfRotRootHelper = self.helper.create_point(calfRotRootHelperName, crossToggle=False, boxToggle=True)
|
179
180
|
calfRotRootHelper.transform = inCalf.transform
|
180
|
-
calfRotRootHelper.position = inFoot.position
|
181
|
+
calfRotRootHelper.position = inFoot.transform.position
|
181
182
|
calfRotRootHelper.parent = inCalf
|
182
183
|
|
183
184
|
self.thighRotRootHelper = thighRotRootHelper
|
@@ -275,7 +276,7 @@ class KneeBone:
|
|
275
276
|
|
276
277
|
self.const.set_rot_controllers_weight_in_list(self.calfRotHelper, 1, self.liftScale * 100.0)
|
277
278
|
|
278
|
-
def create_middle_bone(self, inThigh, inCalf, inKneePopScale=0.1, inKneeBackScale=1.5):
|
279
|
+
def create_middle_bone(self, inThigh, inCalf, inKneeVolumeSize=5.0, inKneePopScale=0.1, inKneeBackScale=1.5):
|
279
280
|
"""
|
280
281
|
무릎 중간 본을 생성합니다.
|
281
282
|
|
@@ -292,7 +293,7 @@ class KneeBone:
|
|
292
293
|
bool: 중간 본 생성 성공 여부
|
293
294
|
"""
|
294
295
|
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf):
|
295
|
-
return
|
296
|
+
return None
|
296
297
|
|
297
298
|
facingDirVec = inCalf.transform.position - inThigh.transform.position
|
298
299
|
inObjXAxisVec = inCalf.objectTransform.row1
|
@@ -309,7 +310,7 @@ class KneeBone:
|
|
309
310
|
transScales.append(inKneeBackScale)
|
310
311
|
transScales.append(inKneePopScale)
|
311
312
|
|
312
|
-
result = self.volumeBone.create_bones(self.calf, self.thigh, inVolumeSize=
|
313
|
+
result = self.volumeBone.create_bones(self.calf, self.thigh, inVolumeSize=inKneeVolumeSize, inRotAxises=["Z", "Z"], inTransAxises=["PosY", "NegY"], inTransScales=transScales)
|
313
314
|
|
314
315
|
filteringChar = self.name._get_filtering_char(inCalf.name)
|
315
316
|
calfName = self.name.get_RealName(inCalf.name)
|
@@ -320,15 +321,16 @@ class KneeBone:
|
|
320
321
|
replaceName = replaceName.lower()
|
321
322
|
calfName = calfName.lower()
|
322
323
|
|
323
|
-
for item in result
|
324
|
+
for item in result.bones:
|
324
325
|
item.name = item.name.replace(calfName, replaceName)
|
325
326
|
|
326
|
-
result[
|
327
|
-
result[
|
327
|
+
result.bones[0].name = result.bones[0].name.replace(calfName, replaceName)
|
328
|
+
result.helpers[0].name = result.helpers[0].name.replace(calfName, replaceName)
|
328
329
|
|
329
|
-
# 결과 저장
|
330
|
-
if result
|
331
|
-
self.middleBones.
|
330
|
+
# 결과 저장 - 기존 확장 방식에서 직접 할당으로 변경
|
331
|
+
if result.bones:
|
332
|
+
self.middleBones = result.bones
|
333
|
+
self.middleHelper = result.helpers[0]
|
332
334
|
|
333
335
|
return result
|
334
336
|
|
@@ -424,7 +426,7 @@ class KneeBone:
|
|
424
426
|
self.calfTwistBones.append(liftTwistBone)
|
425
427
|
self.calfTwistHelpers.append(liftTwistHelper)
|
426
428
|
|
427
|
-
def create_bone(self, inThigh, inCalf, inFoot, inLiftScale=0.05, inKneePopScale=0.1, inKneeBackScale=1.5):
|
429
|
+
def create_bone(self, inThigh, inCalf, inFoot, inLiftScale=0.05, inKneeVolumeSize=5.0, inKneePopScale=0.1, inKneeBackScale=1.5):
|
428
430
|
"""
|
429
431
|
자동 무릎 본 시스템의 모든 요소를 생성하는 주요 메서드입니다.
|
430
432
|
|
@@ -448,27 +450,48 @@ class KneeBone:
|
|
448
450
|
BoneChain: 생성된 자동 무릎 본 체인 객체
|
449
451
|
"""
|
450
452
|
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf) or not rt.isValidNode(inFoot):
|
451
|
-
return
|
453
|
+
return None
|
452
454
|
|
453
455
|
self.create_lookat_helper(inThigh, inFoot)
|
454
456
|
self.create_rot_root_heleprs(inThigh, inCalf, inFoot)
|
455
457
|
self.create_rot_helper(inThigh, inCalf, inFoot)
|
456
458
|
self.assign_thigh_rot_constraint(inLiftScale=inLiftScale)
|
457
459
|
self.assign_calf_rot_constraint(inLiftScale=inLiftScale)
|
458
|
-
self.create_middle_bone(inThigh, inCalf, inKneePopScale=inKneePopScale, inKneeBackScale=inKneeBackScale)
|
460
|
+
self.create_middle_bone(inThigh, inCalf, inKneeVolumeSize=inKneeVolumeSize, inKneePopScale=inKneePopScale, inKneeBackScale=inKneeBackScale)
|
459
461
|
self.create_twist_bones(inThigh, inCalf)
|
460
462
|
|
461
|
-
# 모든 생성된
|
462
|
-
all_bones =
|
463
|
+
# 모든 생성된 본들을 개별적으로 수집
|
464
|
+
all_bones = []
|
465
|
+
|
466
|
+
# 대퇴부 트위스트 본 추가
|
467
|
+
for bone in self.thighTwistBones:
|
468
|
+
all_bones.append(bone)
|
469
|
+
|
470
|
+
# 종아리 트위스트 본 추가
|
471
|
+
for bone in self.calfTwistBones:
|
472
|
+
all_bones.append(bone)
|
473
|
+
|
474
|
+
# 중간 본 추가
|
475
|
+
for bone in self.middleBones:
|
476
|
+
all_bones.append(bone)
|
477
|
+
|
478
|
+
# 모든 헬퍼 수집
|
463
479
|
all_helpers = [self.lookAtHleper, self.thighRotHelper, self.calfRotHelper,
|
464
|
-
self.thighRotRootHelper, self.calfRotRootHelper
|
480
|
+
self.thighRotRootHelper, self.calfRotRootHelper, self.middleHelper]
|
481
|
+
|
482
|
+
# 트위스트 헬퍼 추가
|
483
|
+
for helper in self.thighTwistHelpers:
|
484
|
+
all_helpers.append(helper)
|
485
|
+
|
486
|
+
for helper in self.calfTwistHelpers:
|
487
|
+
all_helpers.append(helper)
|
465
488
|
|
466
489
|
# 결과를 BoneChain 형태로 준비
|
467
490
|
result = {
|
468
491
|
"Bones": all_bones,
|
469
492
|
"Helpers": all_helpers,
|
470
493
|
"SourceBones": [inThigh, inCalf, inFoot],
|
471
|
-
"Parameters": [inLiftScale, inKneePopScale, inKneeBackScale]
|
494
|
+
"Parameters": [inLiftScale, inKneeVolumeSize, inKneePopScale, inKneeBackScale]
|
472
495
|
}
|
473
496
|
|
474
497
|
# 메소드 호출 후 데이터 초기화
|
@@ -503,8 +526,9 @@ class KneeBone:
|
|
503
526
|
|
504
527
|
# 파라미터 가져오기 (또는 기본값 사용)
|
505
528
|
liftScale = parameters[0] if len(parameters) > 0 else 0.05
|
506
|
-
|
507
|
-
|
529
|
+
kneeVolumeSize = parameters[1] if len(parameters) > 1 else 5.0
|
530
|
+
kneePopScale = parameters[2] if len(parameters) > 2 else 0.1
|
531
|
+
kneeBackScale = parameters[3] if len(parameters) > 3 else 1.5
|
508
532
|
|
509
533
|
# 무릎 본 생성
|
510
534
|
inThigh = sourceBones[0]
|
@@ -512,7 +536,7 @@ class KneeBone:
|
|
512
536
|
inFoot = sourceBones[2]
|
513
537
|
|
514
538
|
# 새로운 자동 무릎 본 생성
|
515
|
-
return self.create_bone(inThigh, inCalf, inFoot, liftScale, kneePopScale, kneeBackScale)
|
539
|
+
return self.create_bone(inThigh, inCalf, inFoot, liftScale, kneeVolumeSize, kneePopScale, kneeBackScale)
|
516
540
|
|
517
541
|
def reset(self):
|
518
542
|
"""
|
pyjallib/max/layer.py
CHANGED
@@ -66,12 +66,18 @@ class Layer:
|
|
66
66
|
레이어에 포함된 노드 배열 또는 빈 배열
|
67
67
|
"""
|
68
68
|
returnVal = []
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
|
70
|
+
code = f"""
|
71
|
+
layer = layermanager.getLayer {inLayerNum}
|
72
|
+
layer.nodes &theNodes
|
73
|
+
theNodes
|
74
|
+
"""
|
75
|
+
|
76
|
+
nodes = rt.execute(code)
|
77
|
+
|
78
|
+
for item in nodes:
|
79
|
+
if rt.isValidNode(item):
|
80
|
+
returnVal.append(item)
|
75
81
|
|
76
82
|
return returnVal
|
77
83
|
|