pyjallib 0.1.9__py3-none-any.whl → 0.1.11__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/max/twistBone.py CHANGED
@@ -3,7 +3,11 @@
3
3
 
4
4
  """
5
5
  트위스트 뼈대(Twist Bone) 모듈 - 3ds Max용 트위스트 뼈대 생성 관련 기능 제공
6
- 원본 MAXScript의 twistBone.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
6
+
7
+ 이 모듈은 3D 캐릭터 리깅에서 사용되는 트위스트 뼈대를 생성하고 제어하는 기능을 제공합니다.
8
+ 트위스트 뼈대는 팔이나 다리의 회전 움직임을 더욱 자연스럽게 표현하기 위해 사용됩니다.
9
+ 원본 MAXScript의 twistBone.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현되어
10
+ 3ds Max 내에서 스크립트 형태로 실행할 수 있습니다.
7
11
  """
8
12
 
9
13
  from pymxs import runtime as rt
@@ -15,530 +19,328 @@ from .constraint import Constraint
15
19
  from .bip import Bip
16
20
  from .bone import Bone
17
21
 
22
+ from .boneChain import BoneChain
23
+
18
24
 
19
25
  class TwistBone:
20
26
  """
21
27
  트위스트 뼈대(Twist Bone) 관련 기능을 제공하는 클래스.
28
+
29
+ 이 클래스는 3ds Max에서 트위스트 뼈대를 생성하고 제어하는 다양한 기능을 제공합니다.
22
30
  MAXScript의 _TwistBone 구조체 개념을 Python으로 재구현한 클래스이며,
23
31
  3ds Max의 기능들을 pymxs API를 통해 제어합니다.
32
+
33
+ 트위스트 뼈대는 상체(Upper)와 하체(Lower) 두 가지 타입으로 생성이 가능하며,
34
+ 각각 다른 회전 표현식을 사용하여 자연스러운 회전 움직임을 구현합니다.
24
35
  """
25
36
 
26
- def __init__(self, nameService=None, animService=None, constService=None, bipService=None, boneService=None):
37
+ def __init__(self, nameService=None, animService=None, constraintService=None, bipService=None, boneService=None):
27
38
  """
28
- 클래스 초기화.
39
+ TwistBone 클래스 초기화.
40
+
41
+ 의존성 주입 방식으로 필요한 서비스들을 외부에서 제공받거나 내부에서 생성합니다.
42
+ 서비스들이 제공되지 않을 경우 각 서비스의 기본 인스턴스를 생성하여 사용합니다.
29
43
 
30
44
  Args:
31
- nameService: 이름 처리 서비스 (제공되지 않으면 새로 생성)
32
- animService: 애니메이션 서비스 (제공되지 않으면 새로 생성)
33
- constService: 제약 서비스 (제공되지 않으면 새로 생성)
34
- bipService: 바이페드 서비스 (제공되지 않으면 새로 생성)
45
+ nameService (Name, optional): 이름 처리 서비스. 기본값은 None이며, 제공되지 않으면 새로 생성됩니다.
46
+ animService (Anim, optional): 애니메이션 서비스. 기본값은 None이며, 제공되지 않으면 새로 생성됩니다.
47
+ constraintService (Constraint, optional): 제약 서비스. 기본값은 None이며, 제공되지 않으면 새로 생성됩니다.
48
+ bipService (Bip, optional): 바이페드 서비스. 기본값은 None이며, 제공되지 않으면 새로 생성됩니다.
49
+ boneService (Bone, optional): 뼈대 서비스. 기본값은 None이며, 제공되지 않으면 새로 생성됩니다.
35
50
  """
36
51
  self.name = nameService if nameService else Name()
37
52
  self.anim = animService if animService else Anim()
38
53
  # Ensure dependent services use the potentially newly created instances
39
- self.const = constService if constService else Constraint(nameService=self.name)
54
+ self.const = constraintService if constraintService else Constraint(nameService=self.name)
40
55
  self.bip = bipService if bipService else Bip(animService=self.anim, nameService=self.name)
41
56
  self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim)
42
57
 
43
- # 표현식 초기화
44
- self._init_expressions()
45
-
46
- def _init_expressions(self):
47
- """표현식 초기화"""
48
- # 허벅지(Thigh) 표현식
49
- self.thighExpression = (
50
- "try(\n"
51
- "TM=Limb.transform*inverse Limb.parent.transform\n"
52
- "vector=normalize (cross -TM.row1 [1,0,0])\n"
53
- "angle=acos -(normalize TM.row1).x\n"
54
- "(quat 0 1 0 0)*(quat angle vector)*inverse TM.rotation)\n"
55
- "catch((quat 0 0 0 1))"
56
- )
57
-
58
- # 허벅지 추가 표현식
59
- self.thighExtraExpression = (
60
- "try(\n"
61
- "(Limb.transform*inverse LimbParent.transform).rotation\n"
62
- ")catch((quat 0 0 0 1))"
63
- )
64
-
65
- # 종아리(Calf) 표현식
66
- self.calfExpression = (
67
- "try(\n"
68
- "TM=Limb.transform*inverse Limb.parent.transform\n"
69
- "vector=normalize (cross TM.row1 [1,0,0])\n"
70
- "angle=acos (normalize TM.row1).x\n"
71
- "TM.rotation*(quat -angle vector))\n"
72
- "catch((quat 0 0 0 1))"
73
- )
74
-
75
- # 종아리 추가 표현식
76
- self.calfExtraExpression = (
77
- "try(dependson TB\n"
78
- "TB.rotation.controller[1].value\n"
79
- ")catch((quat 0 0 0 1))"
80
- )
81
-
82
- # 상완(Upper Arm) 표현식
83
- self.upperArmExpression = (
84
- "try(\n"
85
- "TM=Limb.transform*inverse Limb.parent.transform\n"
86
- "vector=normalize (cross TM.row1 [1,0,0])\n"
87
- "angle=acos (normalize TM.row1).x\n"
88
- "(quat angle vector)*inverse TM.rotation)\n"
89
- "catch((quat 0 0 0 1))"
90
- )
91
-
92
- # 상완 추가 표현식
93
- self.upperArmExtraExpression = (
94
- "try(\n"
95
- "(Limb.transform*inverse LimbParent.transform).rotation\n"
96
- ")catch((quat 0 0 0 1))"
97
- )
98
-
99
- # 오른쪽 전완(Forearm) 표현식
100
- self.rForeArmExpression = (
101
- "try(\n"
102
- "TM=(matrix3 [1,0,0] [0,0,-1] [0,1,0] [0,0,0])*Limb.transform*inverse Limb.parent.transform\n"
103
- "vector=normalize (cross TM.row1 [1,0,0])\n"
104
- "angle=acos (normalize TM.row1).x\n"
105
- "TM.rotation*(quat -angle vector))\n"
106
- "catch((quat 0 0 0 1))"
107
- )
108
-
109
- # 왼쪽 전완(Forearm) 표현식
110
- self.lForeArmExpression = (
111
- "try(\n"
112
- "TM=(matrix3 [1,0,0] [0,0,1] [0,-1,0] [0,0,0])*Limb.transform*inverse Limb.parent.transform\n"
113
- "vector=normalize (cross TM.row1 [1,0,0])\n"
114
- "angle=acos (normalize TM.row1).x\n"
115
- "TM.rotation*(quat -angle vector))\n"
116
- "catch((quat 0 0 0 1))"
58
+ # 객체 속성 초기화
59
+ self.limb = None
60
+ self.child = None
61
+ self.twistNum = 0
62
+ self.bones = []
63
+ self.twistType = ""
64
+
65
+ self.upperTwistBoneExpression = (
66
+ "localTm = limb.transform * (inverse limbParent.transform)\n"
67
+ "tm = localTm * inverse(localRefTm)\n"
68
+ "\n"
69
+ "q = tm.rotation\n"
70
+ "\n"
71
+ "axis = [1,0,0]\n"
72
+ "proj = (dot q.axis axis) * axis\n"
73
+ "twist = quat q.angle proj\n"
74
+ "twist = normalize twist\n"
75
+ "--swing = tm.rotation * (inverse twist)\n"
76
+ "\n"
77
+ "inverse twist\n"
117
78
  )
118
79
 
119
- # 전완 추가 표현식
120
- self.foreArmExtraExpression = (
121
- "try(dependson TB\n"
122
- "TB.rotation.controller[1].value\n"
123
- ")catch((quat 0 0 0 1))"
80
+ self.lowerTwistBoneExpression = (
81
+ "localTm = limb.transform * (inverse limbParent.transform)\n"
82
+ "tm = localTm * inverse(localRefTm)\n"
83
+ "\n"
84
+ "q = tm.rotation\n"
85
+ "\n"
86
+ "axis = [1,0,0]\n"
87
+ "proj = (dot q.axis axis) * axis\n"
88
+ "twist = quat q.angle proj\n"
89
+ "twist = normalize twist\n"
90
+ "--swing = tm.rotation * (inverse twist)\n"
91
+ "\n"
92
+ "twist\n"
124
93
  )
125
-
126
- def create_bones(self, inObj, inChild, inTwistNum, inExpression, inExtraExpression, inControllerLimb, inWeightVar):
127
- """
128
- 트위스트 뼈대 체인 생성
129
-
130
- Args:
131
- inObj: 시작 객체
132
- inChild: 끝 객체
133
- inTwistNum: 트위스트 뼈대 개수
134
- inExpression: 기본 회전 표현식
135
- inExtraExpression: 추가 회전 표현식
136
- inControllerLimb: 컨트롤러 대상 팔다리
137
- inWeightVar: 가중치
138
94
 
139
- Returns:
140
- 생성된 트위스트 뼈대 체인 배열
95
+ def reset(self):
141
96
  """
142
- Limb = inObj
143
- distanceVar = rt.distance(Limb, inChild)
144
-
145
- TBExpression = inExpression
146
- ControllerLimb = inControllerLimb
147
- weightVar = inWeightVar
148
-
149
- boneChainArray = []
150
-
151
- # 첫 번째 트위스트 뼈대 생성
152
- TwistBone = rt.BoneSys.createBone(
153
- Limb.transform.position,
154
- inChild.transform.position,
155
- rt.Point3(0, 0, 1)
156
- )
157
- boneName = self.name.get_string(inObj.name) + "Twist"
158
- TwistBone.name = self.name.replace_Index(boneName, "0")
159
- TwistBone.transform = Limb.transform
160
- TwistBone.parent = Limb
161
- TwistBone.length = distanceVar / inTwistNum
162
- TwistBone.width = distanceVar / 8
163
- TwistBone.height = TwistBone.width
164
- TwistBone.taper = 0
165
- TwistBone.sidefins = False
166
- TwistBone.frontfin = False
167
- TwistBone.backfin = False
168
-
169
- # 회전 컨트롤러 설정
170
- TBRotListController = self.const.assign_rot_list(TwistBone)
171
- TBController = rt.Rotation_Script()
172
- TBController.addNode("Limb", ControllerLimb)
173
- TBController.setExpression(TBExpression)
174
-
175
- rt.setPropertyController(TBRotListController, "Available", TBController)
176
- TBRotListController.delete(1)
177
- TBRotListController.setActive(TBRotListController.count)
178
- TBRotListController.weight[0] = weightVar
179
-
180
- boneChainArray.append(TwistBone)
181
-
182
- # 추가 회전 컨트롤러 설정
183
- TBExtraController = rt.Rotation_Script()
184
- if rt.matchPattern(inExtraExpression, pattern="*\nTB.*"):
185
- TBExtraController.addNode("TB", TwistBone)
186
- else:
187
- TBExtraController.addNode("Limb", Limb)
188
- TBExtraController.addNode("LimbParent", TwistBone)
189
- TBExtraController.setExpression(inExtraExpression)
190
-
191
- PrevTBE = TwistBone
192
-
193
- # 추가 트위스트 뼈대 생성 (2개 이상인 경우)
194
- if inTwistNum > 1:
195
- for j in range(2, inTwistNum):
196
- TwistBoneExtra = rt.BoneSys.createBone(
197
- rt.Point3(0, 0, 0),
198
- rt.Point3(1, 0, 0),
199
- rt.Point3(0, 0, 1)
200
- )
201
- matAux = rt.matrix3(1)
202
- matAux.position = rt.Point3(distanceVar/inTwistNum, 0, 0)
203
- TwistBoneExtra.transform = matAux * PrevTBE.transform
204
- TwistBoneExtra.name = self.name.replace_Index(boneName, str(j-1))
205
- TwistBoneExtra.parent = PrevTBE
206
- TwistBoneExtra.length = distanceVar / inTwistNum
207
- TwistBoneExtra.width = PrevTBE.width
208
- TwistBoneExtra.height = PrevTBE.height
209
- TwistBoneExtra.taper = 0
210
- TwistBoneExtra.sidefins = False
211
- TwistBoneExtra.frontfin = False
212
- TwistBoneExtra.backfin = False
213
-
214
- # 회전 컨트롤러 설정
215
- TBExtraRotListController = self.const.assign_rot_list(TwistBoneExtra)
216
- rt.setPropertyController(TBExtraRotListController, "Available", TBExtraController)
217
- TBExtraRotListController.delete(1)
218
- TBExtraRotListController.setActive(TBExtraRotListController.count)
219
- TBExtraRotListController.weight[0] = 100 / (inTwistNum - 1)
220
-
221
- PrevTBE = TwistBoneExtra
222
- boneChainArray.append(TwistBoneExtra)
223
-
224
- # 마지막 트위스트 뼈대 생성
225
- TwistBoneEnd = rt.BoneSys.createBone(
226
- rt.Point3(0, 0, 0),
227
- rt.Point3(1, 0, 0),
228
- rt.Point3(0, 0, 1)
229
- )
230
- matAux = rt.matrix3(1)
231
- matAux.position = rt.Point3(distanceVar/inTwistNum, 0, 0)
232
- TwistBoneEnd.transform = matAux * PrevTBE.transform
233
- TwistBoneEnd.name = self.name.replace_Index(boneName, str(inTwistNum-1))
234
- TwistBoneEnd.parent = inObj
235
- TwistBoneEnd.length = distanceVar / inTwistNum
236
- TwistBoneEnd.width = PrevTBE.width
237
- TwistBoneEnd.height = PrevTBE.height
238
- TwistBoneEnd.taper = 0
239
- TwistBoneEnd.sidefins = False
240
- TwistBoneEnd.frontfin = False
241
- TwistBoneEnd.backfin = False
242
-
243
- boneChainArray.append(TwistBoneEnd)
97
+ 클래스의 주요 컴포넌트들을 초기화합니다.
98
+ 서비스가 아닌 클래스 자체의 작업 데이터를 초기화하는 함수입니다.
244
99
 
245
- return boneChainArray
246
-
247
- def reorder_bones(self, inBoneChainArray):
100
+ Returns:
101
+ self: 메소드 체이닝을 위한 자기 자신 반환
248
102
  """
249
- 뼈대 체인의 순서 재배치
103
+ self.limb = None
104
+ self.child = None
105
+ self.twistNum = 0
106
+ self.bones = []
107
+ self.twistType = ""
250
108
 
251
- Args:
252
- inBoneChainArray: 재배치할 뼈대 체인 배열
109
+ return self
253
110
 
254
- Returns:
255
- 재배치된 뼈대 체인 배열
111
+ def create_upper_limb_bones(self, inObj, inChild, twistNum=4):
256
112
  """
257
- boneChainArray = rt.deepcopy(inBoneChainArray)
258
- returnBoneArray = []
113
+ 상체(팔, 어깨 ) 부분의 트위스트 뼈대를 생성하는 메소드.
259
114
 
260
- # 번째와 마지막 뼈대 가져오기
261
- firstBone = boneChainArray[0]
262
- lastBone = boneChainArray[-1]
263
- returnBoneArray.append(lastBone)
264
-
265
- # 뼈대가 2개 이상인 경우 위치 조정
266
- if len(boneChainArray) > 1:
267
- self.anim.move_local(firstBone, firstBone.length, 0, 0)
268
- self.anim.move_local(lastBone, -(firstBone.length * (len(boneChainArray)-1)), 0, 0)
269
-
270
- # 중간 뼈대들을 새 배열에 추가
271
- for i in range(len(boneChainArray)-1):
272
- returnBoneArray.append(boneChainArray[i])
273
-
274
- # 새로운 순서대로 이름 재설정
275
- for i in range(len(returnBoneArray)):
276
- returnBoneArray[i].name = self.name.replace_Index(boneChainArray[i].name, str(i))
277
-
278
- return returnBoneArray
279
-
280
- def create_upperArm_type(self, inObj, inTwistNum):
281
- """
282
- 상완(Upper Arm) 타입의 트위스트 뼈대 생성
115
+ 상체용 트위스트 뼈대는 부모 객체(inObj)의 위치에서 시작하여
116
+ 자식 객체(inChild) 방향으로 여러 개의 뼈대를 생성합니다.
117
+ 생성된 뼈대들은 스크립트 컨트롤러를 통해 자동으로 회전되어
118
+ 자연스러운 트위스트 움직임을 표현합니다.
283
119
 
284
120
  Args:
285
- inObj: 상완 뼈대 객체
286
- inTwistNum: 트위스트 뼈대 개수
287
-
288
- Returns:
289
- 생성된 트위스트 뼈대 체인 또는 False(실패 시)
290
- """
291
- if inObj.parent is None or inObj.children.count == 0:
292
- return False
293
-
294
- weightVal = 100.0
295
-
296
- return self.create_bones(
297
- inObj,
298
- inObj.children[0],
299
- inTwistNum,
300
- self.upperArmExpression,
301
- self.upperArmExtraExpression,
302
- inObj,
303
- weightVal
304
- )
305
-
306
- def create_foreArm_type(self, inObj, inTwistNum, reorder=True):
307
- """
308
- 전완(Forearm) 타입의 트위스트 뼈대 생성
121
+ inObj: 트위스트 뼈대의 부모 객체(뼈). 일반적으로 상완 또는 대퇴부에 해당합니다.
122
+ inChild: 자식 객체(뼈). 일반적으로 전완 또는 하퇴부에 해당합니다.
123
+ twistNum (int, optional): 생성할 트위스트 뼈대의 개수. 기본값은 4입니다.
309
124
 
310
- Args:
311
- inObj: 전완 뼈대 객체
312
- inTwistNum: 트위스트 뼈대 개수
313
- side: 좌/우측 ("left" 또는 "right", 기본값: "left")
314
-
315
125
  Returns:
316
- 생성된 트위스트 뼈대 체인 또는 False(실패 시)
126
+ BoneChain: 생성된 트위스트 뼈대 BoneChain 객체
317
127
  """
318
- if inObj.parent is None or inObj.children.count == 0:
319
- return False
320
-
321
- controllerLimb = None
322
- weightVal = 100.0
128
+ limb = inObj
129
+ distance = rt.distance(limb, inChild)
130
+ facingDirVec = inChild.transform.position - inObj.transform.position
131
+ inObjXAxisVec = inObj.objectTransform.row1
132
+ distanceDir = 1.0 if rt.dot(inObjXAxisVec, facingDirVec) > 0 else -1.0
133
+ offssetAmount = (distance / twistNum) * distanceDir
323
134
 
324
- # 좌/우측에 따른 표현식 선택
325
- TBExpression = self.lForeArmExpression if self.bip.is_left_node(inObj) else self.rForeArmExpression
135
+ boneChainArray = []
326
136
 
327
- # Biped 컨트롤러 노드 설정
328
- if self.bip.is_left_node(inObj):
329
- controllerLimb = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lArm"), link=4)
330
- else:
331
- controllerLimb = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rArm"), link=4)
137
+ # 번째 트위스트 뼈대 생성
138
+ boneName = self.name.add_suffix_to_real_name(inObj.name, self.name._get_filtering_char(inObj.name) + "Twist")
139
+ if inObj.name[0].islower():
140
+ boneName = boneName.lower()
141
+ twistBone = self.bone.create_nub_bone(boneName, 2)
142
+ twistBone.name = self.name.replace_name_part("Index", boneName, "1")
143
+ twistBone.name = self.name.remove_name_part("Nub", twistBone.name)
144
+ twistBone.transform = limb.transform
145
+ twistBone.parent = limb
146
+ twistBoneLocalRefTM = limb.transform * rt.inverse(limb.parent.transform)
147
+
148
+ twistBoneRotListController = self.const.assign_rot_list(twistBone)
149
+ twistBoneController = rt.Rotation_Script()
150
+ twistBoneController.addConstant("localRefTm", twistBoneLocalRefTM)
151
+ twistBoneController.addNode("limb", limb)
152
+ twistBoneController.addNode("limbParent", limb.parent)
153
+ twistBoneController.setExpression(self.upperTwistBoneExpression)
154
+ twistBoneController.update()
155
+
156
+ rt.setPropertyController(twistBoneRotListController, "Available", twistBoneController)
157
+ twistBoneRotListController.delete(1)
158
+ twistBoneRotListController.setActive(twistBoneRotListController.count)
159
+ twistBoneRotListController.weight[0] = 100.0
160
+
161
+ boneChainArray.append(twistBone)
162
+
163
+ if twistNum > 1:
164
+ lastBone = self.bone.create_nub_bone(boneName, 2)
165
+ lastBone.name = self.name.replace_name_part("Index", boneName, str(twistNum))
166
+ lastBone.name = self.name.remove_name_part("Nub", lastBone.name)
167
+ lastBone.transform = limb.transform
168
+ lastBone.parent = limb
169
+ self.anim.move_local(lastBone, offssetAmount*(twistNum-1), 0, 0)
332
170
 
333
- if inTwistNum > 1:
334
- weightVal = 100 / (inTwistNum - 1)
171
+ weightVal = 100.0 / (twistNum-1)
335
172
 
336
- createdBones = self.create_bones(
337
- inObj,
338
- controllerLimb,
339
- inTwistNum,
340
- TBExpression,
341
- self.foreArmExtraExpression,
342
- controllerLimb,
343
- weightVal
344
- )
345
-
346
- return self.reorder_bones(createdBones) if reorder else createdBones
347
-
348
- def create_thigh_type(self, inObj, inTwistNum):
349
- """
350
- 허벅지(Thigh) 타입의 트위스트 뼈대 생성
351
-
352
- Args:
353
- inObj: 허벅지 뼈대 객체
354
- inTwistNum: 트위스트 뼈대 개수
173
+ if twistNum > 2:
174
+ for i in range(1, twistNum-1):
175
+ twistExtraBone = self.bone.create_nub_bone(boneName, 2)
176
+ twistExtraBone.name = self.name.replace_name_part("Index", boneName, str(i+1))
177
+ twistExtraBone.name = self.name.remove_name_part("Nub", twistExtraBone.name)
178
+ twistExtraBone.transform = limb.transform
179
+ twistExtraBone.parent = limb
180
+ self.anim.move_local(twistExtraBone, offssetAmount*i, 0, 0)
181
+
182
+ twistExtraBoneRotListController = self.const.assign_rot_list(twistExtraBone)
183
+ twistExtraBoneController = rt.Rotation_Script()
184
+ twistExtraBoneController.addConstant("localRefTm", twistBoneLocalRefTM)
185
+ twistExtraBoneController.addNode("limb", limb)
186
+ twistExtraBoneController.addNode("limbParent", limb.parent)
187
+ twistExtraBoneController.setExpression(self.upperTwistBoneExpression)
188
+
189
+ rt.setPropertyController(twistExtraBoneRotListController, "Available", twistExtraBoneController)
190
+ twistExtraBoneRotListController.delete(1)
191
+ twistExtraBoneRotListController.setActive(twistExtraBoneRotListController.count)
192
+ twistExtraBoneRotListController.weight[0] = weightVal * (twistNum-1-i)
193
+
194
+ boneChainArray.append(twistExtraBone)
355
195
 
356
- Returns:
357
- 생성된 트위스트 뼈대 체인 또는 False(실패 시)
358
- """
359
- if inObj.parent is None or inObj.children.count == 0:
360
- return False
361
-
362
- controllerLimb = None
363
- weightVal = 100
364
-
365
- return self.create_bones(
366
- inObj,
367
- inObj.children[0],
368
- inTwistNum,
369
- self.thighExpression,
370
- self.thighExtraExpression,
371
- inObj,
372
- weightVal
373
- )
374
-
375
- def create_calf_type(self, inObj, inTwistNum, reorder=True):
376
- """
377
- 종아리(Calf) 타입의 트위스트 뼈대 생성
196
+ boneChainArray.append(lastBone)
378
197
 
379
- Args:
380
- inObj: 종아리 뼈대 객체
381
- inTwistNum: 트위스트 뼈대 개수
382
- side: 좌/우측 ("left" 또는 "right", 기본값: "left")
383
-
384
- Returns:
385
- 생성된 트위스트 뼈대 체인 또는 False(실패 시)
386
- """
387
- if inObj.parent is None or inObj.children.count == 0:
388
- return False
389
-
390
- controllerLimb = None
391
- weightVal = 100
198
+ # 결과를 BoneChain 형태로 준비
199
+ result = {
200
+ "Bones": boneChainArray,
201
+ "Helpers": [],
202
+ "SourceBones": [inObj, inChild],
203
+ "Parameters": [twistNum, "Upper"]
204
+ }
392
205
 
393
- # Biped 컨트롤러 노드 설정
394
- if self.bip.is_left_node(inObj):
395
- controllerLimb = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lLeg"), link=3)
396
- else:
397
- controllerLimb = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rLeg"), link=3)
398
-
399
- # 복수 뼈대인 경우 가중치 조정
400
- if inTwistNum > 1:
401
- weightVal = 100 / (inTwistNum - 1)
402
-
403
- createdBones = self.create_bones(
404
- inObj,
405
- controllerLimb,
406
- inTwistNum,
407
- self.calfExpression,
408
- self.calfExtraExpression,
409
- controllerLimb,
410
- weightVal
411
- )
206
+ # 메소드 호출 데이터 초기화
207
+ self.reset()
412
208
 
413
- return self.reorder_bones(createdBones) if reorder else createdBones
414
-
415
- def create_bend_type(self):
416
- """
417
- 굽힘(Bend) 타입의 트위스트 뼈대 생성
418
- (아직 구현되지 않음)
419
- """
420
- pass
421
-
422
- def get_upperArm_type(self, inObj):
209
+ return BoneChain.from_result(result)
210
+
211
+ def create_lower_limb_bones(self, inObj, inChild, twistNum=4):
423
212
  """
424
- 상완(Upper Arm) 타입의 트위스트 뼈대 가져오기
213
+ 하체(팔뚝, 다리 등) 부분의 트위스트 뼈대를 생성하는 메소드.
214
+
215
+ 하체용 트위스트 뼈대는 부모 객체(inObj)의 위치에서 시작하여
216
+ 자식 객체(inChild) 쪽으로 여러 개의 뼈대를 생성합니다.
217
+ 상체와는 다른 회전 표현식을 사용하여 하체에 적합한 트위스트 움직임을 구현합니다.
425
218
 
426
219
  Args:
427
- inObj: 상완 뼈대 객체
428
-
220
+ inObj: 트위스트 뼈대의 부모 객체(뼈). 일반적으로 전완 또는 하퇴부에 해당합니다.
221
+ inChild: 자식 객체(뼈). 일반적으로 손목 또는 발목에 해당합니다.
222
+ twistNum (int, optional): 생성할 트위스트 뼈대의 개수. 기본값은 4입니다.
223
+
429
224
  Returns:
430
- 상완 타입의 트위스트 뼈대 또는 False(실패 시)
225
+ BoneChain: 생성된 트위스트 뼈대 BoneChain 객체
431
226
  """
432
- returnVal = []
433
-
434
- parentBipObj = None
227
+ limb = inChild
228
+ distance = rt.distance(inObj, inChild)
229
+ facingDirVec = inChild.transform.position - inObj.transform.position
230
+ inObjXAxisVec = inObj.objectTransform.row1
231
+ distanceDir = 1.0 if rt.dot(inObjXAxisVec, facingDirVec) > 0 else -1.0
232
+ offssetAmount = (distance / twistNum) * distanceDir
435
233
 
436
- if not self.bip.is_biped_object(inObj):
437
- return returnVal
234
+ boneChainArray = []
438
235
 
439
- if self.bip.is_left_node(inObj):
440
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lArm"), link=2)
441
- else:
442
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rArm"), link=2)
236
+ # 첫 번째 트위스트 뼈대 생성
237
+ boneName = self.name.add_suffix_to_real_name(inObj.name, self.name._get_filtering_char(inObj.name) + "Twist")
238
+ if inObj.name[0].islower():
239
+ boneName = boneName.lower()
240
+ twistBone = self.bone.create_nub_bone(boneName, 2)
241
+ twistBone.name = self.name.replace_name_part("Index", boneName, "1")
242
+ twistBone.name = self.name.remove_name_part("Nub", twistBone.name)
243
+ twistBone.transform = inObj.transform
244
+ twistBone.parent = inObj
245
+ self.anim.move_local(twistBone, offssetAmount*(twistNum-1), 0, 0)
246
+ twistBoneLocalRefTM = limb.transform * rt.inverse(limb.parent.transform)
247
+
248
+ twistBoneRotListController = self.const.assign_rot_list(twistBone)
249
+ twistBoneController = rt.Rotation_Script()
250
+ twistBoneController.addConstant("localRefTm", twistBoneLocalRefTM)
251
+ twistBoneController.addNode("limb", limb)
252
+ twistBoneController.addNode("limbParent", limb.parent)
253
+ twistBoneController.setExpression(self.lowerTwistBoneExpression)
254
+ twistBoneController.update()
255
+
256
+ rt.setPropertyController(twistBoneRotListController, "Available", twistBoneController)
257
+ twistBoneRotListController.delete(1)
258
+ twistBoneRotListController.setActive(twistBoneRotListController.count)
259
+ twistBoneRotListController.weight[0] = 100.0
260
+
261
+ if twistNum > 1:
262
+ lastBone = self.bone.create_nub_bone(boneName, 2)
263
+ lastBone.name = self.name.replace_name_part("Index", boneName, str(twistNum))
264
+ lastBone.name = self.name.remove_name_part("Nub", lastBone.name)
265
+ lastBone.transform = inObj.transform
266
+ lastBone.parent = inObj
267
+ self.anim.move_local(lastBone, 0, 0, 0)
443
268
 
444
- children = self.bone.get_every_children(parentBipObj)
445
- for child in children:
446
- if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
447
- returnVal.append(child)
448
-
449
- returnVal = self.name.sort_by_name(returnVal)
450
-
451
- return returnVal
452
-
453
- def get_foreArm_type(self, inObj):
454
- """
455
- 전완(Forearm) 타입의 트위스트 뼈대 가져오기
456
-
457
- Args:
458
- inObj: 전완 뼈대 객체
269
+ weightVal = 100.0 / (twistNum-1)
459
270
 
460
- Returns:
461
- 전완 타입의 트위스트 뼈대 또는 False(실패 )
462
- """
463
- returnVal = []
464
-
465
- parentBipObj = None
466
-
467
- if not self.bip.is_biped_object(inObj):
468
- return returnVal
469
-
470
- if self.bip.is_left_node(inObj):
471
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lArm"), link=3)
472
- else:
473
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rArm"), link=3)
271
+ if twistNum > 2:
272
+ for i in range(1, twistNum-1):
273
+ twistExtraBone = self.bone.create_nub_bone(boneName, 2)
274
+ twistExtraBone.name = self.name.replace_name_part("Index", boneName, str(i+1))
275
+ twistExtraBone.name = self.name.remove_name_part("Nub", twistExtraBone.name)
276
+ twistExtraBone.transform = inObj.transform
277
+ twistExtraBone.parent = inObj
278
+ self.anim.move_local(twistExtraBone, offssetAmount*(twistNum-1-i), 0, 0)
279
+
280
+ twistExtraBoneRotListController = self.const.assign_rot_list(twistExtraBone)
281
+ twistExtraBoneController = rt.Rotation_Script()
282
+ twistExtraBoneController.addConstant("localRefTm", twistBoneLocalRefTM)
283
+ twistExtraBoneController.addNode("limb", limb)
284
+ twistExtraBoneController.addNode("limbParent", limb.parent)
285
+ twistExtraBoneController.setExpression(self.lowerTwistBoneExpression)
286
+
287
+ rt.setPropertyController(twistExtraBoneRotListController, "Available", twistExtraBoneController)
288
+ twistExtraBoneRotListController.delete(1)
289
+ twistExtraBoneRotListController.setActive(twistExtraBoneRotListController.count)
290
+ twistExtraBoneRotListController.weight[0] = weightVal * (twistNum-1-i)
291
+
292
+ boneChainArray.append(twistExtraBone)
474
293
 
475
- children = self.bone.get_every_children(parentBipObj)
476
- for child in children:
477
- if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
478
- returnVal.append(child)
294
+ boneChainArray.append(lastBone)
479
295
 
480
- returnVal = self.name.sort_by_name(returnVal)
296
+ # 결과를 BoneChain 형태로 준비
297
+ result = {
298
+ "Bones": boneChainArray,
299
+ "Helpers": [],
300
+ "SourceBones": [inObj, inChild],
301
+ "Parameters": [twistNum, "Lower"]
302
+ }
481
303
 
482
- return returnVal
304
+ # 메소드 호출 후 데이터 초기화
305
+ self.reset()
306
+
307
+ return BoneChain.from_result(result)
483
308
 
484
- def get_thigh_type(self, inObj):
309
+ def create_bones_from_chain(self, inBoneChain: BoneChain):
485
310
  """
486
- 허벅지(Thigh) 타입의 트위스트 뼈대 가져오기
311
+ 기존 BoneChain 객체에서 트위스트 본을 생성합니다.
312
+ 기존 설정을 복원하거나 저장된 데이터에서 트위스트 본 셋업을 재생성할 때 사용합니다.
487
313
 
488
314
  Args:
489
- inObj: 허벅지 뼈대 객체
490
-
315
+ inBoneChain (BoneChain): 트위스트 정보를 포함한 BoneChain 객체
316
+
491
317
  Returns:
492
- 허벅지 타입의 트위스트 뼈대 또는 False(실패 시)
318
+ BoneChain: 업데이트된 BoneChain 객체 또는 실패 시 None
493
319
  """
494
- returnVal = []
495
-
496
- parentBipObj = None
497
-
498
- if not self.bip.is_biped_object(inObj):
499
- return returnVal
500
-
501
- if self.bip.is_left_node(inObj):
502
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lLeg"), link=1)
503
- else:
504
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rLeg"), link=1)
320
+ if not inBoneChain or inBoneChain.is_empty():
321
+ return None
505
322
 
506
- children = self.bone.get_every_children(parentBipObj)
507
- for child in children:
508
- if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
509
- returnVal.append(child)
510
-
511
- returnVal = self.name.sort_by_name(returnVal)
512
-
513
- return returnVal
514
-
515
- def get_calf_type(self, inObj):
516
- """
517
- 종아리(Calf) 타입의 트위스트 뼈대 가져오기
518
-
519
- Args:
520
- inObj: 종아리 뼈대 객체
323
+ # 기존 객체 삭제
324
+ inBoneChain.delete()
521
325
 
522
- Returns:
523
- 종아리 타입의 트위스트 뼈대 또는 False(실패 시)
524
- """
525
- returnVal = []
526
-
527
- parentBipObj = None
326
+ # BoneChain에서 필요한 정보 추출
327
+ sourceBones = inBoneChain.sourceBones
328
+ parameters = inBoneChain.parameters
528
329
 
529
- if not self.bip.is_biped_object(inObj):
530
- return returnVal
531
-
532
- if self.bip.is_left_node(inObj):
533
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lLeg"), link=2)
534
- else:
535
- parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rLeg"), link=2)
330
+ # 필수 소스 본 확인
331
+ if len(sourceBones) < 2 or not rt.isValidNode(sourceBones[0]) or not rt.isValidNode(sourceBones[1]):
332
+ return None
536
333
 
537
- children = self.bone.get_every_children(parentBipObj)
538
- for child in children:
539
- if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
540
- returnVal.append(child)
334
+ # 파라미터 가져오기 (또는 기본값 사용)
335
+ twistNum = parameters[0] if len(parameters) > 0 else 4
336
+ twistType = parameters[1] if len(parameters) > 1 else "Upper"
541
337
 
542
- returnVal = self.name.sort_by_name(returnVal)
338
+ # 생성
339
+ inObj = sourceBones[0]
340
+ inChild = sourceBones[1]
543
341
 
544
- return returnVal
342
+ # 타입에 따라 적절한 방식으로 트위스트 본 생성
343
+ if twistType == "Upper":
344
+ return self.create_upper_limb_bones(inObj, inChild, twistNum)
345
+ else:
346
+ return self.create_lower_limb_bones(inObj, inChild, twistNum)