pyjallib 0.1.0__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 +17 -0
- pyjallib/max/__init__.py +46 -0
- pyjallib/max/align.py +112 -0
- pyjallib/max/anim.py +594 -0
- pyjallib/max/bip.py +508 -0
- pyjallib/max/bone.py +910 -0
- pyjallib/max/constraint.py +973 -0
- pyjallib/max/header.py +57 -0
- pyjallib/max/helper.py +433 -0
- pyjallib/max/layer.py +262 -0
- pyjallib/max/link.py +78 -0
- pyjallib/max/macro/jal_macro_align.py +155 -0
- pyjallib/max/macro/jal_macro_bone.py +358 -0
- pyjallib/max/macro/jal_macro_constraint.py +140 -0
- pyjallib/max/macro/jal_macro_helper.py +321 -0
- pyjallib/max/macro/jal_macro_link.py +55 -0
- pyjallib/max/macro/jal_macro_select.py +91 -0
- pyjallib/max/mirror.py +388 -0
- pyjallib/max/name.py +521 -0
- pyjallib/max/select.py +278 -0
- pyjallib/max/skin.py +996 -0
- pyjallib/max/twistBone.py +418 -0
- pyjallib/namePart.py +633 -0
- pyjallib/nameToPath.py +113 -0
- pyjallib/naming.py +1066 -0
- pyjallib/namingConfig.py +844 -0
- pyjallib/perforce.py +735 -0
- pyjallib/reloadModules.py +33 -0
- pyjallib-0.1.0.dist-info/METADATA +28 -0
- pyjallib-0.1.0.dist-info/RECORD +32 -0
- pyjallib-0.1.0.dist-info/WHEEL +5 -0
- pyjallib-0.1.0.dist-info/top_level.txt +1 -0
pyjallib/max/mirror.py
ADDED
@@ -0,0 +1,388 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
미러 모듈 - 3ds Max용 객체 미러링 관련 기능 제공
|
6
|
+
원본 MAXScript의 mirror.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
|
7
|
+
"""
|
8
|
+
|
9
|
+
from pymxs import runtime as rt
|
10
|
+
|
11
|
+
# Import necessary service classes for default initialization
|
12
|
+
from .name import Name
|
13
|
+
from .bone import Bone
|
14
|
+
|
15
|
+
|
16
|
+
class Mirror:
|
17
|
+
"""
|
18
|
+
객체 미러링 관련 기능을 제공하는 클래스.
|
19
|
+
MAXScript의 _Mirror 구조체 개념을 Python으로 재구현한 클래스이며,
|
20
|
+
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, nameService=None, boneService=None):
|
24
|
+
"""
|
25
|
+
클래스 초기화
|
26
|
+
|
27
|
+
Args:
|
28
|
+
nameService: Name 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
29
|
+
boneService: Bone 서비스 인스턴스 (제공되지 않으면 새로 생성)
|
30
|
+
"""
|
31
|
+
self.name = nameService if nameService else Name()
|
32
|
+
self.bone = boneService if boneService else Bone(nameService=self.name) # Pass the potentially newly created nameService
|
33
|
+
|
34
|
+
def mirror_matrix(self, mAxis="x", mFlip="x", tm=None, pivotTM=None):
|
35
|
+
"""
|
36
|
+
미러링 행렬 생성
|
37
|
+
|
38
|
+
Args:
|
39
|
+
mAxis: 미러링 축 (기본값: "x")
|
40
|
+
mFlip: 뒤집는 축 (기본값: "x")
|
41
|
+
tm: 변환 행렬 (기본값: 단위 행렬)
|
42
|
+
pivotTM: 피벗 변환 행렬 (기본값: 단위 행렬)
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
미러링된 변환 행렬
|
46
|
+
"""
|
47
|
+
def fetch_reflection(a):
|
48
|
+
"""
|
49
|
+
반사 벡터 값 반환
|
50
|
+
|
51
|
+
Args:
|
52
|
+
a: 축 식별자 ("x", "y", "z")
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
해당 축에 대한 반사 벡터
|
56
|
+
"""
|
57
|
+
if a == "x":
|
58
|
+
return [-1, 1, 1] # YZ 평면에 대한 반사
|
59
|
+
elif a == "y":
|
60
|
+
return [1, -1, 1] # ZX 평면에 대한 반사
|
61
|
+
elif a == "z":
|
62
|
+
return [1, 1, -1] # XY 평면에 대한 반사
|
63
|
+
else:
|
64
|
+
return [1, 1, 1] # 반사 없음
|
65
|
+
|
66
|
+
# 기본값 설정
|
67
|
+
if tm is None:
|
68
|
+
tm = rt.matrix3(1)
|
69
|
+
if pivotTM is None:
|
70
|
+
pivotTM = rt.matrix3(1)
|
71
|
+
|
72
|
+
# 반사 행렬 생성
|
73
|
+
a_reflection = rt.scalematrix(rt.Point3(*fetch_reflection(mAxis)))
|
74
|
+
f_reflection = rt.scalematrix(rt.Point3(*fetch_reflection(mFlip)))
|
75
|
+
|
76
|
+
# 미러링된 변환 행렬 계산: fReflection * tm * aReflection * pivotTm
|
77
|
+
return f_reflection * tm * a_reflection * pivotTM
|
78
|
+
|
79
|
+
def apply_mirror(self, inObj, axis=1, flip=2, pivotObj=None, cloneStatus=2, negative=False):
|
80
|
+
"""
|
81
|
+
객체에 미러링 적용
|
82
|
+
|
83
|
+
Args:
|
84
|
+
inObj: 미러링할 객체
|
85
|
+
axis: 미러링 축 인덱스 (1=x, 2=y, 3=z, 기본값: 1)
|
86
|
+
flip: 뒤집기 축 인덱스 (1=x, 2=y, 3=z, 4=none, 기본값: 2)
|
87
|
+
pivotObj: 피벗 객체 (기본값: None)
|
88
|
+
cloneStatus: 복제 상태 (1=원본 변경, 2=복제본 생성, 3=스냅샷, 기본값: 2)
|
89
|
+
negative: 음수 좌표계 사용 여부 (기본값: False)
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
미러링된 객체 (복제본 또는 원본)
|
93
|
+
"""
|
94
|
+
axisArray = ["x", "y", "z", "none"]
|
95
|
+
copyObj = rt.copy(inObj)
|
96
|
+
objTM = inObj.transform
|
97
|
+
pivotTM = rt.matrix3(1)
|
98
|
+
mirrorIndexAxis = axis
|
99
|
+
flipAxisIndex = flip
|
100
|
+
copyObjName = self.name.gen_mirroring_name(inObj.name)
|
101
|
+
|
102
|
+
# 피벗 객체가 지정된 경우 피벗 변환 행렬 사용
|
103
|
+
if pivotObj is not None:
|
104
|
+
pivotTM = pivotObj.transform
|
105
|
+
|
106
|
+
# negative가 True인 경우 뒤집기 없음으로 설정
|
107
|
+
if negative:
|
108
|
+
flipAxisIndex = 4
|
109
|
+
|
110
|
+
# 복제본 초기 설정
|
111
|
+
copyObj.name = copyObjName
|
112
|
+
copyObj.parent = None
|
113
|
+
copyObj.wirecolor = inObj.wirecolor
|
114
|
+
|
115
|
+
# 복제 상태에 따른 처리
|
116
|
+
if cloneStatus == 1: # 원본 변경
|
117
|
+
rt.delete(copyObj)
|
118
|
+
copyObj = None
|
119
|
+
inObj.transform = self.mirror_matrix(
|
120
|
+
mAxis=axisArray[mirrorIndexAxis-1],
|
121
|
+
mFlip=axisArray[flipAxisIndex-1],
|
122
|
+
tm=objTM,
|
123
|
+
pivotTM=pivotTM
|
124
|
+
)
|
125
|
+
copyObj = inObj
|
126
|
+
elif cloneStatus == 2: # 복제본 생성
|
127
|
+
copyObj.transform = self.mirror_matrix(
|
128
|
+
mAxis=axisArray[mirrorIndexAxis-1],
|
129
|
+
mFlip=axisArray[flipAxisIndex-1],
|
130
|
+
tm=objTM,
|
131
|
+
pivotTM=pivotTM
|
132
|
+
)
|
133
|
+
elif cloneStatus == 3: # 스냅샷 생성
|
134
|
+
rt.delete(copyObj)
|
135
|
+
copyObj = None
|
136
|
+
copyObj = rt.snapShot(inObj)
|
137
|
+
copyObj.transform = self.mirror_matrix(
|
138
|
+
mAxis=axisArray[mirrorIndexAxis-1],
|
139
|
+
mFlip=axisArray[flipAxisIndex-1],
|
140
|
+
tm=objTM,
|
141
|
+
pivotTM=pivotTM
|
142
|
+
)
|
143
|
+
|
144
|
+
return copyObj
|
145
|
+
|
146
|
+
def mirror_object(self, inObjArray, mAxis=1, pivotObj=None, cloneStatus=2):
|
147
|
+
"""
|
148
|
+
객체 배열을 음수 좌표계를 사용하여 미러링
|
149
|
+
|
150
|
+
Args:
|
151
|
+
inObjArray: 미러링할 객체 배열
|
152
|
+
mAxis: 미러링 축 (기본값: 1)
|
153
|
+
pivotObj: 피벗 객체 (기본값: None)
|
154
|
+
cloneStatus: 복제 상태 (기본값: 2)
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
미러링된 객체 배열
|
158
|
+
"""
|
159
|
+
returnArray = []
|
160
|
+
|
161
|
+
for item in inObjArray:
|
162
|
+
mirroredObj = self.apply_mirror(
|
163
|
+
item,
|
164
|
+
axis=mAxis,
|
165
|
+
pivotObj=pivotObj,
|
166
|
+
cloneStatus=cloneStatus,
|
167
|
+
negative=True
|
168
|
+
)
|
169
|
+
returnArray.append(mirroredObj)
|
170
|
+
|
171
|
+
return returnArray
|
172
|
+
|
173
|
+
def mirror_without_negative(self, inMirrorObjArray, mAxis=1, pivotObj=None, cloneStatus=2):
|
174
|
+
"""
|
175
|
+
객체 배열을 양수 좌표계를 사용하여 미러링
|
176
|
+
|
177
|
+
Args:
|
178
|
+
inMirrorObjArray: 미러링할 객체 배열
|
179
|
+
mAxis: 미러링 축 인덱스 (1-6, 기본값: 1)
|
180
|
+
pivotObj: 피벗 객체 (기본값: None)
|
181
|
+
cloneStatus: 복제 상태 (기본값: 2)
|
182
|
+
|
183
|
+
Returns:
|
184
|
+
미러링된 객체 배열
|
185
|
+
"""
|
186
|
+
# 미러링 축과 뒤집기 축 매핑
|
187
|
+
# 1=XY, 2=XZ, 3=YX, 4=YZ, 5=ZX, 6=ZY
|
188
|
+
axisIndex = 1
|
189
|
+
flipIndex = 1
|
190
|
+
|
191
|
+
# 미러링 축 인덱스에 따른 매핑
|
192
|
+
if mAxis == 1:
|
193
|
+
axisIndex = 1 # x
|
194
|
+
flipIndex = 2 # y
|
195
|
+
elif mAxis == 2:
|
196
|
+
axisIndex = 1 # x
|
197
|
+
flipIndex = 3 # z
|
198
|
+
elif mAxis == 3:
|
199
|
+
axisIndex = 2 # y
|
200
|
+
flipIndex = 1 # x
|
201
|
+
elif mAxis == 4:
|
202
|
+
axisIndex = 2 # y
|
203
|
+
flipIndex = 3 # z
|
204
|
+
elif mAxis == 5:
|
205
|
+
axisIndex = 3 # z
|
206
|
+
flipIndex = 1 # x
|
207
|
+
elif mAxis == 6:
|
208
|
+
axisIndex = 3 # z
|
209
|
+
flipIndex = 2 # y
|
210
|
+
else:
|
211
|
+
axisIndex = 1 # x
|
212
|
+
flipIndex = 1 # x
|
213
|
+
|
214
|
+
# 미러링 적용
|
215
|
+
returnArray = []
|
216
|
+
for item in inMirrorObjArray:
|
217
|
+
mirroredObj = self.apply_mirror(
|
218
|
+
item,
|
219
|
+
axis=axisIndex,
|
220
|
+
flip=flipIndex,
|
221
|
+
pivotObj=pivotObj,
|
222
|
+
cloneStatus=cloneStatus,
|
223
|
+
negative=False
|
224
|
+
)
|
225
|
+
returnArray.append(mirroredObj)
|
226
|
+
|
227
|
+
return returnArray
|
228
|
+
|
229
|
+
def mirror_bone(self, inBoneArray, mAxis=1, flipZ=False, offset=0.0):
|
230
|
+
"""
|
231
|
+
뼈대 객체를 미러링
|
232
|
+
|
233
|
+
Args:
|
234
|
+
inBoneArray: 미러링할 뼈대 배열
|
235
|
+
mAxis: 미러링 축 (1=x, 2=y, 3=z, 기본값: 1)
|
236
|
+
flipZ: Z축 뒤집기 여부 (기본값: False)
|
237
|
+
offset: 미러링 오프셋 (기본값: 0.0)
|
238
|
+
|
239
|
+
Returns:
|
240
|
+
미러링된 뼈대 배열
|
241
|
+
"""
|
242
|
+
# 계층 구조에 따라 뼈대 정렬
|
243
|
+
bones = self.bone.sort_bones_as_hierarchy(inBoneArray)
|
244
|
+
|
245
|
+
# 미러링 축 팩터 설정
|
246
|
+
axisFactor = [1, 1, 1]
|
247
|
+
if mAxis == 1:
|
248
|
+
axisFactor = [-1, 1, 1] # x축 미러링
|
249
|
+
elif mAxis == 2:
|
250
|
+
axisFactor = [1, -1, 1] # y축 미러링
|
251
|
+
elif mAxis == 3:
|
252
|
+
axisFactor = [1, 1, -1] # z축 미러링
|
253
|
+
|
254
|
+
# 새 뼈대와 부모 정보 저장 배열 준비
|
255
|
+
parents = []
|
256
|
+
created = []
|
257
|
+
|
258
|
+
# 시작점 위치 (미러링 중심) 설정
|
259
|
+
root = bones[0].transform.translation
|
260
|
+
|
261
|
+
# 정렬된 뼈대 순서대로 처리
|
262
|
+
for i in range(len(bones)):
|
263
|
+
original = bones[i]
|
264
|
+
if rt.classOf(original) != rt.BoneGeometry: # 실제 뼈대가 아닌 경우
|
265
|
+
parents.append(None) # 부모 없음 표시
|
266
|
+
continue
|
267
|
+
|
268
|
+
# 원본 뼈대의 시작점, 끝점, Z축 방향 가져오기
|
269
|
+
boneStart = original.pos
|
270
|
+
boneEnd = self.bone.get_bone_end_position(original)
|
271
|
+
boneZ = original.dir
|
272
|
+
|
273
|
+
# 미러링 적용
|
274
|
+
for k in range(3): # x, y, z 좌표
|
275
|
+
if axisFactor[k] < 0:
|
276
|
+
boneStart[k] = 2.0 * root[k] - boneStart[k] + offset
|
277
|
+
boneEnd[k] = 2.0 * root[k] - boneEnd[k] + offset
|
278
|
+
boneZ[k] = -boneZ[k]
|
279
|
+
|
280
|
+
# Z축 뒤집기 옵션 적용
|
281
|
+
if flipZ:
|
282
|
+
boneZ = -boneZ
|
283
|
+
|
284
|
+
# 새 뼈대 생성
|
285
|
+
reflection = rt.bonesys.createbone(boneStart, boneEnd, boneZ)
|
286
|
+
|
287
|
+
# 원본 뼈대의 속성을 복사
|
288
|
+
reflection.backfin = original.backfin
|
289
|
+
reflection.backfinendtaper = original.backfinendtaper
|
290
|
+
reflection.backfinsize = original.backfinsize
|
291
|
+
reflection.backfinstarttaper = original.backfinstarttaper
|
292
|
+
reflection.frontfin = original.frontfin
|
293
|
+
reflection.frontfinendtaper = original.frontfinendtaper
|
294
|
+
reflection.frontfinsize = original.frontfinsize
|
295
|
+
reflection.frontfinstarttaper = original.frontfinstarttaper
|
296
|
+
reflection.height = original.height
|
297
|
+
|
298
|
+
# 이름 생성 (좌우/앞뒤 방향이 있는 경우 미러링된 이름 생성)
|
299
|
+
if self.name.has_Side(original.name) or self.name.has_FrontBack(original.name):
|
300
|
+
reflection.name = self.name.gen_mirroring_name(original.name, axis=mAxis)
|
301
|
+
else:
|
302
|
+
reflection.name = self.name.add_suffix_to_real_name(original.name, "Mirrored")
|
303
|
+
|
304
|
+
reflection.sidefins = original.sidefins
|
305
|
+
reflection.sidefinsendtaper = original.sidefinsendtaper
|
306
|
+
reflection.sidefinssize = original.sidefinssize
|
307
|
+
reflection.sidefinsstarttaper = original.sidefinsstarttaper
|
308
|
+
reflection.taper = original.taper
|
309
|
+
reflection.width = original.width
|
310
|
+
reflection.wirecolor = original.wirecolor
|
311
|
+
|
312
|
+
created.append(reflection)
|
313
|
+
parents.append(reflection)
|
314
|
+
|
315
|
+
# 계층 구조 연결 (자식부터 상위로)
|
316
|
+
for i in range(len(created)-1, 0, -1):
|
317
|
+
pIndex = bones.index(bones[i].parent) if bones[i].parent in bones else 0
|
318
|
+
if pIndex != 0:
|
319
|
+
created[i].parent = parents[pIndex]
|
320
|
+
|
321
|
+
# 루트 뼈대의 부모 설정
|
322
|
+
created[0].parent = bones[0].parent
|
323
|
+
|
324
|
+
# 부모가 없는 뼈대는 위치 조정
|
325
|
+
for i in range(len(created)):
|
326
|
+
if created[i].parent is None:
|
327
|
+
created[i].position = rt.Point3(
|
328
|
+
bones[i].position.x * axisFactor[0],
|
329
|
+
bones[i].position.y * axisFactor[1],
|
330
|
+
bones[i].position.z * axisFactor[2]
|
331
|
+
)
|
332
|
+
|
333
|
+
return created
|
334
|
+
|
335
|
+
def mirror_geo(self, inMirrorObjArray, mAxis=1, pivotObj=None, cloneStatus=2):
|
336
|
+
"""
|
337
|
+
지오메트리 객체 미러링 (폴리곤 노멀 방향 조정 포함)
|
338
|
+
|
339
|
+
Args:
|
340
|
+
inMirrorObjArray: 미러링할 객체 배열
|
341
|
+
mAxis: 미러링 축 (기본값: 1)
|
342
|
+
pivotObj: 피벗 객체 (기본값: None)
|
343
|
+
cloneStatus: 복제 상태 (기본값: 2)
|
344
|
+
|
345
|
+
Returns:
|
346
|
+
미러링된 객체 배열
|
347
|
+
"""
|
348
|
+
# 객체 미러링
|
349
|
+
mirroredArray = self.mirror_object(
|
350
|
+
inMirrorObjArray,
|
351
|
+
mAxis=mAxis,
|
352
|
+
pivotObj=pivotObj,
|
353
|
+
cloneStatus=cloneStatus
|
354
|
+
)
|
355
|
+
|
356
|
+
# 리셋 대상, 비리셋 대상 분류
|
357
|
+
resetXformArray = []
|
358
|
+
nonResetXformArray = []
|
359
|
+
returnArray = []
|
360
|
+
|
361
|
+
# 객체 타입에 따라 분류
|
362
|
+
for item in mirroredArray:
|
363
|
+
caseIndex = 0
|
364
|
+
if rt.classOf(item) == rt.Editable_Poly:
|
365
|
+
caseIndex += 1
|
366
|
+
if rt.classOf(item) == rt.Editable_mesh:
|
367
|
+
caseIndex += 1
|
368
|
+
if item.modifiers.count > 0:
|
369
|
+
caseIndex += 1
|
370
|
+
|
371
|
+
if caseIndex == 1: # 폴리곤, 메시 또는 모디파이어가 있는 경우
|
372
|
+
resetXformArray.append(item)
|
373
|
+
else:
|
374
|
+
nonResetXformArray.append(item)
|
375
|
+
|
376
|
+
# 리셋 대상 객체에 XForm 리셋 및 노멀 방향 뒤집기 적용
|
377
|
+
for item in resetXformArray:
|
378
|
+
rt.ResetXForm(item)
|
379
|
+
tempNormalMod = rt.normalModifier()
|
380
|
+
tempNormalMod.flip = True
|
381
|
+
rt.addModifier(item, tempNormalMod)
|
382
|
+
rt.collapseStack(item)
|
383
|
+
|
384
|
+
# 처리된 객체들 합치기
|
385
|
+
returnArray.extend(resetXformArray)
|
386
|
+
returnArray.extend(nonResetXformArray)
|
387
|
+
|
388
|
+
return returnArray
|