skelform-python 0.2.1__tar.gz → 0.4.1__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.
- {skelform_python-0.2.1 → skelform_python-0.4.1}/PKG-INFO +1 -1
- {skelform_python-0.2.1 → skelform_python-0.4.1}/pyproject.toml +1 -1
- {skelform_python-0.2.1 → skelform_python-0.4.1}/skelform_python/__init__.py +153 -20
- {skelform_python-0.2.1 → skelform_python-0.4.1}/.gitignore +0 -0
- {skelform_python-0.2.1 → skelform_python-0.4.1}/README.md +0 -0
- {skelform_python-0.2.1 → skelform_python-0.4.1}/readme.md +0 -0
- {skelform_python-0.2.1 → skelform_python-0.4.1}/skelform_python/tests.py +0 -0
- {skelform_python-0.2.1 → skelform_python-0.4.1}/uv.lock +0 -0
|
@@ -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[
|
|
44
|
-
|
|
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[
|
|
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:
|
|
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(
|
|
114
|
-
bone.pos.y = ikf(
|
|
115
|
-
bone.rot = ikf(
|
|
116
|
-
bone.scale.x = ikf(
|
|
117
|
-
bone.scale.y = ikf(
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
215
|
-
|
|
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 ==
|
|
231
|
-
ccw = family.ik_constraint ==
|
|
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):
|
|
@@ -288,6 +413,14 @@ def check_bone_flip(bone_rot: float, scale: Vec2):
|
|
|
288
413
|
return bone_rot
|
|
289
414
|
|
|
290
415
|
|
|
416
|
+
def get_bone_texture(bone_tex: str, styles: [Style]):
|
|
417
|
+
for style in styles:
|
|
418
|
+
for tex in style.textures:
|
|
419
|
+
if tex.name == bone_tex:
|
|
420
|
+
return tex
|
|
421
|
+
return False
|
|
422
|
+
|
|
423
|
+
|
|
291
424
|
# Returns a (bone.id, Texture) map of textures to draw bones with.
|
|
292
425
|
def setup_bone_textures(bones: [Bone], styles: [Style]):
|
|
293
426
|
final_textures = {}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|