skelform-python 0.2.1__tar.gz → 0.4.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skelform_python
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: SkelForm runtime for Python
5
5
  Author-email: Retropaint <darkglasses1122@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "skelform_python"
7
- version = "0.2.1"
7
+ version = "0.4.0"
8
8
  authors = [
9
9
  { name="Retropaint", email="darkglasses1122@gmail.com" },
10
10
  ]
@@ -17,6 +17,8 @@ class Vec2:
17
17
  return Vec2(self.x + other.x, self.y + other.y)
18
18
 
19
19
  def __mul__(self, other):
20
+ if isinstance(other, float):
21
+ return Vec2(self.x * other, self.y * other)
20
22
  return Vec2(self.x * other.x, self.y * other.y)
21
23
 
22
24
  def __isub__(self, other):
@@ -29,6 +31,26 @@ class Vec2:
29
31
  return self.__mul__(other)
30
32
 
31
33
 
34
+ @dataclass
35
+ class Vertex:
36
+ pos: Vec2
37
+ uv: Vec2
38
+ init_pos: Vec2
39
+
40
+
41
+ @dataclass
42
+ class BoneBindVert:
43
+ id: int
44
+ weight: float
45
+
46
+
47
+ @dataclass
48
+ class BoneBind:
49
+ bone_id: int
50
+ is_path: bool
51
+ verts: list[BoneBindVert]
52
+
53
+
32
54
  @dataclass
33
55
  class Bone:
34
56
  name: str
@@ -39,16 +61,18 @@ class Bone:
39
61
  rot: float
40
62
  scale: Vec2
41
63
  pos: Vec2
64
+ vertices: Optional[list[Vertex]]
65
+ indices: Optional[list[int]]
66
+ binds: Optional[list[BoneBind]]
42
67
  ik_bone_ids: Optional[list[int]]
43
- ik_mode: Optional[int]
44
- ik_constraint_str: Optional[str]
45
- ik_constraint: Optional[int]
68
+ ik_mode: Optional[str]
69
+ ik_constraint: Optional[str]
46
70
  ik_family_id: Optional[int]
47
71
  ik_target_id: Optional[int]
48
72
  init_rot: float
49
73
  init_scale: Vec2
50
74
  init_pos: Vec2
51
- init_ik_constraint: Optional[int]
75
+ init_ik_constraint: Optional[str]
52
76
  zindex: Optional[int] = 0
53
77
 
54
78
 
@@ -56,8 +80,9 @@ class Bone:
56
80
  class Keyframe:
57
81
  frame: int
58
82
  bone_id: int
59
- element: int
83
+ element: str
60
84
  value: float
85
+ value_str: Optional[str]
61
86
 
62
87
 
63
88
  @dataclass
@@ -110,11 +135,11 @@ def animate(
110
135
  bones.append(bone)
111
136
  id = bone.id
112
137
  # yapf: disable
113
- bone.pos.x = ikf(0, bone.pos.x, bone.init_pos.x, kf, frames[a], id, bf)
114
- bone.pos.y = ikf(1, bone.pos.y, bone.init_pos.y, kf, frames[a], id, bf)
115
- bone.rot = ikf(2, bone.rot, bone.init_rot, kf, frames[a], id, bf)
116
- bone.scale.x = ikf(3, bone.scale.x, bone.init_scale.x, kf, frames[a], id, bf)
117
- bone.scale.y = ikf(4, bone.scale.y, bone.init_scale.y, kf, frames[a], id, bf)
138
+ bone.pos.x = ikf("PositionX", bone.pos.x, bone.init_pos.x, kf, frames[a], id, bf)
139
+ bone.pos.y = ikf("PositionY", bone.pos.y, bone.init_pos.y, kf, frames[a], id, bf)
140
+ bone.rot = ikf("Rotation", bone.rot, bone.init_rot, kf, frames[a], id, bf)
141
+ bone.scale.x = ikf("ScaleX", bone.scale.x, bone.init_scale.x, kf, frames[a], id, bf)
142
+ bone.scale.y = ikf("ScaleY", bone.scale.y, bone.init_scale.y, kf, frames[a], id, bf)
118
143
 
119
144
  for bone in bones:
120
145
  bone = reset_bone(bone, animations, bone.id, frames[0], blend_frames[0])
@@ -122,7 +147,7 @@ def animate(
122
147
  return bones
123
148
 
124
149
 
125
- def is_animated(anims: [Animation], bone_id: int, element: int) -> bool:
150
+ def is_animated(anims: [Animation], bone_id: int, element: str) -> bool:
126
151
  for anim in anims:
127
152
  for kf in anim.keyframes:
128
153
  if kf.bone_id == bone_id and kf.element == element:
@@ -134,15 +159,15 @@ def is_animated(anims: [Animation], bone_id: int, element: int) -> bool:
134
159
  def reset_bone(
135
160
  bone: Bone, anims: [Animation], bone_id: int, frame: int, blend_frame: int
136
161
  ):
137
- if not is_animated(anims, bone_id, 0):
162
+ if not is_animated(anims, bone_id, "PositionX"):
138
163
  interpolate(frame, blend_frame, bone.pos.x, bone.init_pos.x)
139
- if not is_animated(anims, bone_id, 1):
164
+ if not is_animated(anims, bone_id, "PositionY"):
140
165
  interpolate(frame, blend_frame, bone.pos.y, bone.init_pos.y)
141
- if not is_animated(anims, bone_id, 2):
166
+ if not is_animated(anims, bone_id, "Rotation"):
142
167
  interpolate(frame, blend_frame, bone.rot, bone.init_rot)
143
- if not is_animated(anims, bone_id, 3):
168
+ if not is_animated(anims, bone_id, "ScaleX"):
144
169
  interpolate(frame, blend_frame, bone.scale.x, bone.init_scale.x)
145
- if not is_animated(anims, bone_id, 4):
170
+ if not is_animated(anims, bone_id, "ScaleY"):
146
171
  interpolate(frame, blend_frame, bone.scale.y, bone.init_scale.y)
147
172
 
148
173
 
@@ -193,9 +218,77 @@ def construct(armature: Armature):
193
218
  final_bones = copy.deepcopy(armature.bones)
194
219
  final_bones = inheritance(final_bones, ik_rots)
195
220
 
221
+ final_bones = construct_verts(final_bones)
222
+
196
223
  return final_bones
197
224
 
198
225
 
226
+ def construct_verts(bones: list[Bone]):
227
+ for b in range(len(bones)):
228
+ if not bones[b].vertices:
229
+ continue
230
+ bone = copy.deepcopy(bones[b])
231
+
232
+ for v in range(len(bone.vertices)):
233
+ bone.vertices[v] = inherit_vert(bone.vertices[v].pos, bone)
234
+
235
+ if not bones[b].binds:
236
+ continue
237
+
238
+ for bi in range(len(bones[b].binds)):
239
+ boneId = bones[b].binds[bi].bone_id
240
+ if boneId == -1:
241
+ continue
242
+ bindBone = {}
243
+ for bone in bones:
244
+ if bone.id == boneId:
245
+ bindBone = bone
246
+ break
247
+ bind = bones[b].binds[bi]
248
+ for v in range(len(bind.verts)):
249
+ id = bind.verts[v].id
250
+
251
+ if not bind.is_path:
252
+ vert: Vertex = bones[b].vertices[id]
253
+ weight: float = bind.verts[v].weight
254
+ endpos: Vec2 = inherit_vert(vert.initPos, bindBone) - vert.pos
255
+ vert.pos += endPos * weight
256
+ continue
257
+
258
+ binds = bones[b].binds
259
+ prev = bi - 1 if bi > 0 else bi
260
+ next = min(bi + 1, len(binds) - 1)
261
+ prevBone = {}
262
+ nextBone = {}
263
+ for bone in bones:
264
+ if bone.id == binds[prev].bone_id:
265
+ prevBone = bone
266
+ elif bone.id == binds[next].bone_id:
267
+ nextBone = bone
268
+
269
+ prevDir: Vec2 = bindBone.pos - prevBone.pos
270
+ nextDir: Vec2 = nextBone.pos - bindBone.pos
271
+ prevNormal: Vec2 = normalize(Vec2(-prevDir.y, prevDir.x))
272
+ nextNormal: Vec2 = normalize(Vec2(-nextDir.y, nextDir.x))
273
+ average: Vec2 = prevNormal + nextNormal
274
+ normalAngle: float = math.atan2(average.y, average.x)
275
+
276
+ vert: Vertex = bones[b].vertices[id]
277
+ vert.pos = vert.init_pos + bindBone.pos
278
+ rotated: Vec2 = rotate(vert.pos - bindBone.pos, normalAngle)
279
+ vert.pos = bindBone.pos + (rotated * bind.verts[v].weight)
280
+ bones[b].vertices[id] = vert
281
+
282
+ return bones
283
+
284
+
285
+ def inherit_vert(pos: Vec2, bone: Bone):
286
+ pos *= bone.scale
287
+ pos = rotate(pos, bone.rot)
288
+ pos += bone.pos
289
+ return pos
290
+
291
+
199
292
  def inverse_kinematics(bones: list[Bone], ik_root_ids: list[int]):
200
293
  ik_rots = {}
201
294
 
@@ -211,8 +304,11 @@ def inverse_kinematics(bones: list[Bone], ik_root_ids: list[int]):
211
304
  root = copy.deepcopy(bones[family.ik_bone_ids[0]].pos)
212
305
  target = copy.deepcopy(bones[family.ik_target_id].pos)
213
306
 
214
- for i in range(10):
215
- fabrik(family, bones, root, target)
307
+ if family.ik_mode == "FABRIK":
308
+ for i in range(10):
309
+ fabrik(family, bones, root, target)
310
+ else:
311
+ arc_ik(family, bones, root, target)
216
312
 
217
313
  # setting bone rotations
218
314
  end_bone = bones[family.ik_bone_ids[-1]].pos
@@ -227,8 +323,8 @@ def inverse_kinematics(bones: list[Bone], ik_root_ids: list[int]):
227
323
  base_dir = normalize(target - root)
228
324
  dir = joint_dir.x * base_dir.y - base_dir.x * joint_dir.y
229
325
  base_angle = math.atan2(base_dir.y, base_dir.x)
230
- cw = family.ik_constraint == 1 and dir > 0
231
- ccw = family.ik_constraint == 2 and dir < 0
326
+ cw = family.ik_constraint == "Clockwise" and dir > 0
327
+ ccw = family.ik_constraint == "CounterClockwise" and dir < 0
232
328
  if ccw or cw:
233
329
  for i in family.ik_bone_ids:
234
330
  bones[i].rot = -bones[i].rot + base_angle * 2
@@ -278,6 +374,35 @@ def fabrik(family, bones, root, target):
278
374
  prev_pos = bones[family.ik_bone_ids[i]].pos
279
375
 
280
376
 
377
+ def arc_ik(family, bones: list[Bone], root: Vec2, target: Vec2):
378
+ dist = [0.0]
379
+
380
+ maxLength: Vec2 = magnitude(
381
+ bones[family.ik_bone_ids[len(family.ik_bone_ids) - 1]].pos - root
382
+ )
383
+ currLength: float = 0.0
384
+ for b in range(1, len(family.ik_bone_ids), 1):
385
+ length: float = magnitude(
386
+ bones[family.ik_bone_ids[b]].pos - bones[family.ik_bone_ids[b - 1]].pos
387
+ )
388
+ currLength += length
389
+ dist.append(currLength / maxLength)
390
+
391
+ base: Vec2 = target - root
392
+ baseAngle: float = math.atan2(base.y, base.x)
393
+ baseMag: float = min(magnitude(base), maxLength)
394
+ peak: float = maxLength / baseMag
395
+ valley: float = baseMag / maxLength
396
+ for b in range(1, len(family.ik_bone_ids), 1):
397
+ bones[family.ik_bone_ids[b]].pos = Vec2(
398
+ bones[family.ik_bone_ids[b]].pos.x * valley,
399
+ root.y + (1.0 - peak) * math.sin(dist[b] * 3.14) * baseMag,
400
+ )
401
+
402
+ rotated: float = rotate(bones[family.ik_bone_ids[b]].pos - root, baseAngle)
403
+ bones[family.ik_bone_ids[b]].pos = rotated + root
404
+
405
+
281
406
  # Flips bone's rotation if either axis of provided scale is negative.
282
407
  # Returns new bone rotations
283
408
  def check_bone_flip(bone_rot: float, scale: Vec2):
File without changes