diff --git a/.github/workflows/pack-checks.yml b/.github/workflows/pack-checks.yml
index 4076a72..e4c3b27 100644
--- a/.github/workflows/pack-checks.yml
+++ b/.github/workflows/pack-checks.yml
@@ -41,9 +41,13 @@ jobs:
run: |
node --test tools/kit-build/test/*.test.mjs tools/lod/test/*.test.mjs \
tools/architecture-kit/test/*.test.mjs tools/scifi-kit/test/*.test.mjs \
- tools/town-kit/test/*.test.mjs tools/anim/test/*.test.mjs interior-kit/_src/test/*.test.mjs
+ tools/town-kit/test/*.test.mjs tools/anim/test/*.test.mjs interior-kit/_src/test/*.test.mjs \
+ tools/licence-lint/test/*.test.mjs
(cd tools/meshy && npm test)
+ - name: licence lint (every file of a pack with licences.json — CC0 only, read from the files themselves)
+ run: node tools/licence-lint/lint.mjs --json licence-lint.json
+
- name: kit-build check (every pack)
run: node tools/kit-build/kit-build.mjs check --summary "$GITHUB_STEP_SUMMARY" --json pack-check.json
@@ -58,4 +62,5 @@ jobs:
path: |
pack-check.json
pack-budget.md
+ licence-lint.json
if-no-files-found: ignore
diff --git a/characters-kit/Astronaut/glTF-Binary/astronaut.vrm b/characters-kit/Astronaut/glTF-Binary/astronaut.vrm
new file mode 100644
index 0000000..c6ad181
Binary files /dev/null and b/characters-kit/Astronaut/glTF-Binary/astronaut.vrm differ
diff --git a/characters-kit/Astronaut/thumb.webp b/characters-kit/Astronaut/thumb.webp
new file mode 100644
index 0000000..39ee1dd
Binary files /dev/null and b/characters-kit/Astronaut/thumb.webp differ
diff --git a/characters-kit/ChillPenguin/glTF-Binary/chillpenguin.vrm b/characters-kit/ChillPenguin/glTF-Binary/chillpenguin.vrm
new file mode 100644
index 0000000..2faf267
Binary files /dev/null and b/characters-kit/ChillPenguin/glTF-Binary/chillpenguin.vrm differ
diff --git a/characters-kit/ChillPenguin/thumb.webp b/characters-kit/ChillPenguin/thumb.webp
new file mode 100644
index 0000000..d7f255c
Binary files /dev/null and b/characters-kit/ChillPenguin/thumb.webp differ
diff --git a/characters-kit/CoolPirate/glTF-Binary/coolpirate.vrm b/characters-kit/CoolPirate/glTF-Binary/coolpirate.vrm
new file mode 100644
index 0000000..10c87f3
Binary files /dev/null and b/characters-kit/CoolPirate/glTF-Binary/coolpirate.vrm differ
diff --git a/characters-kit/CoolPirate/thumb.webp b/characters-kit/CoolPirate/thumb.webp
new file mode 100644
index 0000000..ad02801
Binary files /dev/null and b/characters-kit/CoolPirate/thumb.webp differ
diff --git a/characters-kit/Erika/glTF-Binary/erika.vrm b/characters-kit/Erika/glTF-Binary/erika.vrm
new file mode 100644
index 0000000..de5ea41
Binary files /dev/null and b/characters-kit/Erika/glTF-Binary/erika.vrm differ
diff --git a/characters-kit/Erika/thumb.webp b/characters-kit/Erika/thumb.webp
new file mode 100644
index 0000000..2b36695
Binary files /dev/null and b/characters-kit/Erika/thumb.webp differ
diff --git a/characters-kit/Kate/glTF-Binary/kate.vrm b/characters-kit/Kate/glTF-Binary/kate.vrm
new file mode 100644
index 0000000..a6e86e7
Binary files /dev/null and b/characters-kit/Kate/glTF-Binary/kate.vrm differ
diff --git a/characters-kit/Kate/thumb.webp b/characters-kit/Kate/thumb.webp
new file mode 100644
index 0000000..3f5719f
Binary files /dev/null and b/characters-kit/Kate/thumb.webp differ
diff --git a/characters-kit/MannequinFemale/glTF-Binary/mannequin-female.glb b/characters-kit/MannequinFemale/glTF-Binary/mannequin-female.glb
new file mode 100644
index 0000000..28ce0f6
Binary files /dev/null and b/characters-kit/MannequinFemale/glTF-Binary/mannequin-female.glb differ
diff --git a/characters-kit/MannequinFemale/thumb.webp b/characters-kit/MannequinFemale/thumb.webp
new file mode 100644
index 0000000..2089708
Binary files /dev/null and b/characters-kit/MannequinFemale/thumb.webp differ
diff --git a/characters-kit/MannequinMale/glTF-Binary/mannequin-male.glb b/characters-kit/MannequinMale/glTF-Binary/mannequin-male.glb
new file mode 100644
index 0000000..6f106f3
Binary files /dev/null and b/characters-kit/MannequinMale/glTF-Binary/mannequin-male.glb differ
diff --git a/characters-kit/MannequinMale/thumb.webp b/characters-kit/MannequinMale/thumb.webp
new file mode 100644
index 0000000..d3ff2d6
Binary files /dev/null and b/characters-kit/MannequinMale/thumb.webp differ
diff --git a/characters-kit/Olivia/glTF-Binary/olivia.vrm b/characters-kit/Olivia/glTF-Binary/olivia.vrm
new file mode 100644
index 0000000..7ed2a96
Binary files /dev/null and b/characters-kit/Olivia/glTF-Binary/olivia.vrm differ
diff --git a/characters-kit/Olivia/thumb.webp b/characters-kit/Olivia/thumb.webp
new file mode 100644
index 0000000..c1c04fc
Binary files /dev/null and b/characters-kit/Olivia/thumb.webp differ
diff --git a/characters-kit/Skelly/glTF-Binary/skelly.vrm b/characters-kit/Skelly/glTF-Binary/skelly.vrm
new file mode 100644
index 0000000..594e757
Binary files /dev/null and b/characters-kit/Skelly/glTF-Binary/skelly.vrm differ
diff --git a/characters-kit/Skelly/thumb.webp b/characters-kit/Skelly/thumb.webp
new file mode 100644
index 0000000..5274bf5
Binary files /dev/null and b/characters-kit/Skelly/thumb.webp differ
diff --git a/characters-kit/Witch/glTF-Binary/witch.vrm b/characters-kit/Witch/glTF-Binary/witch.vrm
new file mode 100644
index 0000000..78853c6
Binary files /dev/null and b/characters-kit/Witch/glTF-Binary/witch.vrm differ
diff --git a/characters-kit/Witch/thumb.webp b/characters-kit/Witch/thumb.webp
new file mode 100644
index 0000000..d735f68
Binary files /dev/null and b/characters-kit/Witch/thumb.webp differ
diff --git a/characters-kit/attribution.html b/characters-kit/attribution.html
new file mode 100644
index 0000000..3c2e8ac
--- /dev/null
+++ b/characters-kit/attribution.html
@@ -0,0 +1,26 @@
+
Attribution Statement
+
+The Characters pack holds humanoid characters for theprototype's Characters & motion tools: every one maps onto
+ VRM 1.0's humanoid bones, so any motion-kit clip (or any other humanoid clip) plays on it.
+
+ - VRM avatars (Astronaut, Chill Penguin, Cool Pirate, Erika, Kate, Olivia, Skelly, Witch) from
+ 100Avatars R1 / R2 by Polygonal Mind, listed by the
+ Open Source Avatars registry. Each file is shipped as
+ its authors made it and states its licence in its own VRM meta: CC0, commercial use allowed.
+ - Mannequins (female, male) by Quaternius, from the Universal Animation Library 2 / 1 Standard
+ (quaternius.com, OpenGameArt).
+ The male mannequin's clips were taken out (they ship once, in motion-kit).
+
+All of it is dedicated to the public domain under CC0 1.0:
+ use it for anything, no credit required (crediting Polygonal Mind and Quaternius is appreciated by the authors).
+ licences.json records the licence, author and source of every file; the pack CI's licence lint checks
+ each VRM's embedded licence against it.
+
+Details:
+
+ - Pack: Characters (8 VRM avatars, 2 mannequins)
+ - Authors: Polygonal Mind (100Avatars), Quaternius
+ - License: CC0-1.0
+ - Build: tools/characters-kit (sources pinned by sha256)
+
+
diff --git a/characters-kit/default.json b/characters-kit/default.json
new file mode 100644
index 0000000..3d46998
--- /dev/null
+++ b/characters-kit/default.json
@@ -0,0 +1,122 @@
+[
+ {
+ "name": "Astronaut",
+ "screenshot": "thumb.webp",
+ "label": "Astronaut (VRM avatar, 100Avatars R1 #048)",
+ "variants": {
+ "glTF-Binary": "astronaut.vrm"
+ },
+ "size": [1.296, 1.363, 0.619],
+ "box": [-0.648, 0, -0.322, 0.648, 1.363, 0.297],
+ "tris": 4350,
+ "bytes": 1679188
+ },
+ {
+ "name": "ChillPenguin",
+ "screenshot": "thumb.webp",
+ "label": "ChillPenguin (VRM avatar, 100Avatars R2 #160)",
+ "variants": {
+ "glTF-Binary": "chillpenguin.vrm"
+ },
+ "size": [2.145, 1.598, 0.478],
+ "box": [-1.073, 0, -0.228, 1.072, 1.598, 0.25],
+ "tris": 3958,
+ "bytes": 1670412
+ },
+ {
+ "name": "CoolPirate",
+ "screenshot": "thumb.webp",
+ "label": "CoolPirate (VRM avatar, 100Avatars R2 #145)",
+ "variants": {
+ "glTF-Binary": "coolpirate.vrm"
+ },
+ "size": [1.851, 1.638, 0.312],
+ "box": [-0.925, 0, -0.155, 0.926, 1.638, 0.157],
+ "tris": 4374,
+ "bytes": 1649636
+ },
+ {
+ "name": "Erika",
+ "screenshot": "thumb.webp",
+ "label": "Erika (VRM avatar, 100Avatars R1 #053)",
+ "variants": {
+ "glTF-Binary": "erika.vrm"
+ },
+ "size": [2.014, 1.66, 0.312],
+ "box": [-1.007, 0, -0.166, 1.007, 1.66, 0.146],
+ "tris": 4032,
+ "bytes": 1548672
+ },
+ {
+ "name": "Kate",
+ "screenshot": "thumb.webp",
+ "label": "Kate (VRM avatar, 100Avatars R1 #038)",
+ "variants": {
+ "glTF-Binary": "kate.vrm"
+ },
+ "size": [2.013, 1.728, 0.365],
+ "box": [-1.006, 0, -0.181, 1.007, 1.728, 0.184],
+ "tris": 3960,
+ "bytes": 1402180
+ },
+ {
+ "name": "Olivia",
+ "screenshot": "thumb.webp",
+ "label": "Olivia (VRM avatar, 100Avatars R1 #056)",
+ "variants": {
+ "glTF-Binary": "olivia.vrm"
+ },
+ "size": [1.71, 1.667, 0.377],
+ "box": [-0.855, -0.001, -0.158, 0.855, 1.666, 0.219],
+ "tris": 3888,
+ "bytes": 1699736
+ },
+ {
+ "name": "Skelly",
+ "screenshot": "thumb.webp",
+ "label": "Skelly (VRM avatar, 100Avatars R1 #029)",
+ "variants": {
+ "glTF-Binary": "skelly.vrm"
+ },
+ "size": [1.776, 1.727, 0.33],
+ "box": [-0.888, 0, -0.159, 0.888, 1.727, 0.171],
+ "tris": 2880,
+ "bytes": 1169152
+ },
+ {
+ "name": "Witch",
+ "screenshot": "thumb.webp",
+ "label": "Witch (VRM avatar, 100Avatars R1 #039)",
+ "variants": {
+ "glTF-Binary": "witch.vrm"
+ },
+ "size": [1.98, 2.001, 0.874],
+ "box": [-0.99, -0.001, -0.411, 0.99, 2, 0.463],
+ "tris": 4058,
+ "bytes": 1488388
+ },
+ {
+ "name": "MannequinFemale",
+ "screenshot": "thumb.webp",
+ "label": "Female mannequin (Quaternius, rigged — motion-kit clips play on it)",
+ "variants": {
+ "glTF-Binary": "mannequin-female.glb"
+ },
+ "size": [1.762, 1.806, 0.309],
+ "box": [-0.881, 0.002, -0.128, 0.881, 1.808, 0.181],
+ "tris": 14612,
+ "bytes": 1442824
+ },
+ {
+ "name": "MannequinMale",
+ "screenshot": "thumb.webp",
+ "label": "Male mannequin (Quaternius, rigged — motion-kit clips play on it)",
+ "variants": {
+ "glTF-Binary": "mannequin-male.glb"
+ },
+ "size": [1.944, 1.828, 0.369],
+ "box": [-0.972, 0.001, -0.164, 0.972, 1.829, 0.205],
+ "tris": 13744,
+ "bytes": 474792
+ }
+]
diff --git a/characters-kit/licences.json b/characters-kit/licences.json
new file mode 100644
index 0000000..f63978b
--- /dev/null
+++ b/characters-kit/licences.json
@@ -0,0 +1,162 @@
+{
+ "Astronaut/glTF-Binary/astronaut.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #048 Astronaut",
+ "source": "https://github.com/ToxSam/open-source-avatars (id e69fd8b9-d6ae-44ca-84e0-be4bb075d426)",
+ "url": "https://arweave.net/T0c0z_XEPQHy3vyXz31XB22s_6JTqHdnau8exq_I8tI",
+ "sha256": "1cb8218610deafc0fd4608507fc52cf3303de84b0f641b9a452efb5e8a0e6f23"
+ },
+ "Astronaut/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #048 Astronaut",
+ "source": "https://github.com/ToxSam/open-source-avatars (id e69fd8b9-d6ae-44ca-84e0-be4bb075d426)",
+ "url": "https://arweave.net/T0c0z_XEPQHy3vyXz31XB22s_6JTqHdnau8exq_I8tI",
+ "note": "the registry thumbnail, resized"
+ },
+ "ChillPenguin/glTF-Binary/chillpenguin.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R2 #160 ChillPenguin",
+ "source": "https://github.com/ToxSam/open-source-avatars (id aa2360e6-87ca-4f16-b5d1-8bae48000276)",
+ "url": "https://arweave.net/4c5EhflLKYfBtzpcLlvZY0afAwXLVdxXmpgwGaarAIQ",
+ "sha256": "52b235d5a36c17798f6ce145d1d56965a17b3be81c143d0b301e7ae881660b14"
+ },
+ "ChillPenguin/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R2 #160 ChillPenguin",
+ "source": "https://github.com/ToxSam/open-source-avatars (id aa2360e6-87ca-4f16-b5d1-8bae48000276)",
+ "url": "https://arweave.net/4c5EhflLKYfBtzpcLlvZY0afAwXLVdxXmpgwGaarAIQ",
+ "note": "the registry thumbnail, resized"
+ },
+ "CoolPirate/glTF-Binary/coolpirate.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R2 #145 CoolPirate",
+ "source": "https://github.com/ToxSam/open-source-avatars (id ecd9d092-f34f-4a29-9ebd-0b83f8266cf9)",
+ "url": "https://arweave.net/lQwSPMpcSKzL9lMYyAaQ63vHK1ATP1Xmir43jZg2Ob0",
+ "sha256": "2193b911fe1e1bf3c38b0efb3d09d173aa1c69a1092b73ef4ee1b1e6c29119ea"
+ },
+ "CoolPirate/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R2 #145 CoolPirate",
+ "source": "https://github.com/ToxSam/open-source-avatars (id ecd9d092-f34f-4a29-9ebd-0b83f8266cf9)",
+ "url": "https://arweave.net/lQwSPMpcSKzL9lMYyAaQ63vHK1ATP1Xmir43jZg2Ob0",
+ "note": "the registry thumbnail, resized"
+ },
+ "Erika/glTF-Binary/erika.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #053 Erika",
+ "source": "https://github.com/ToxSam/open-source-avatars (id f6c50bbf-b41a-470e-84b8-bb3a12efc902)",
+ "url": "https://arweave.net/GZkfa0SNnrBWluRL_pXpakg7T3K3d4l87__wR4mD3UM",
+ "sha256": "ac2030d23c791065e5cd596e251257269bc55b8c76b29a6ebf183bcfe8c7c1f7"
+ },
+ "Erika/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #053 Erika",
+ "source": "https://github.com/ToxSam/open-source-avatars (id f6c50bbf-b41a-470e-84b8-bb3a12efc902)",
+ "url": "https://arweave.net/GZkfa0SNnrBWluRL_pXpakg7T3K3d4l87__wR4mD3UM",
+ "note": "the registry thumbnail, resized"
+ },
+ "Kate/glTF-Binary/kate.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #038 Kate",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 3d842fb6-29f5-494a-a696-d9ab10a0257d)",
+ "url": "https://arweave.net/1q4IQwLQXJVS0JGSpeXlRdazmZYdwJbmLbTv7o0s5Y8",
+ "sha256": "d857bca56a26730b4f78872ffa322d11b6f427580e3224d1b1cd43368b50f9de"
+ },
+ "Kate/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #038 Kate",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 3d842fb6-29f5-494a-a696-d9ab10a0257d)",
+ "url": "https://arweave.net/1q4IQwLQXJVS0JGSpeXlRdazmZYdwJbmLbTv7o0s5Y8",
+ "note": "the registry thumbnail, resized"
+ },
+ "Olivia/glTF-Binary/olivia.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #056 Olivia",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 4239c466-c3c1-4f97-ba56-a871f361ed84)",
+ "url": "https://arweave.net/MgsNlTetzAoVEC6E-lswj65vp7StkOZXXd5OjjqzYZI",
+ "sha256": "d2597a8e0f6fbfc6c1e77886b737cea9cd9d2640474dc5cb5eafc847376a2473"
+ },
+ "Olivia/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #056 Olivia",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 4239c466-c3c1-4f97-ba56-a871f361ed84)",
+ "url": "https://arweave.net/MgsNlTetzAoVEC6E-lswj65vp7StkOZXXd5OjjqzYZI",
+ "note": "the registry thumbnail, resized"
+ },
+ "Skelly/glTF-Binary/skelly.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #029 Skelly",
+ "source": "https://github.com/ToxSam/open-source-avatars (id ba0cc801-97f8-4c11-8916-144c560f8064)",
+ "url": "https://arweave.net/3qGxPjmrwuHxuiT5v4LaR_PVoJ9XYLXLM_N7fdxiF_M",
+ "sha256": "3b789306ad0e22845a3668398f7d71c93fd8f99b0a2ee6116311270655dda06c"
+ },
+ "Skelly/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #029 Skelly",
+ "source": "https://github.com/ToxSam/open-source-avatars (id ba0cc801-97f8-4c11-8916-144c560f8064)",
+ "url": "https://arweave.net/3qGxPjmrwuHxuiT5v4LaR_PVoJ9XYLXLM_N7fdxiF_M",
+ "note": "the registry thumbnail, resized"
+ },
+ "Witch/glTF-Binary/witch.vrm": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #039 Witch",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 00df253c-4665-4cbf-b420-761a1cc4f9dd)",
+ "url": "https://arweave.net/0YLwWzDkvVWn9ttv2RAdc8bvWcCBjvYPpu7fpjpHYU0",
+ "sha256": "33fa9a9c4c10d0ba0ab22a051809afddb9b53e7f51cbb69df91e1bf9e1653652"
+ },
+ "Witch/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Polygonal Mind",
+ "title": "100Avatars R1 #039 Witch",
+ "source": "https://github.com/ToxSam/open-source-avatars (id 00df253c-4665-4cbf-b420-761a1cc4f9dd)",
+ "url": "https://arweave.net/0YLwWzDkvVWn9ttv2RAdc8bvWcCBjvYPpu7fpjpHYU0",
+ "note": "the registry thumbnail, resized"
+ },
+ "MannequinFemale/glTF-Binary/mannequin-female.glb": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Universal Animation Library 2 mannequin (female)",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "2ee6cc3fe888d9b144afa8cc4b2ab7bfc5d13a0d5b7548df777f61f64ad65fa6"
+ },
+ "MannequinFemale/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Universal Animation Library 2 mannequin (female)",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "note": "rendered by kit-build thumbs"
+ },
+ "MannequinMale/glTF-Binary/mannequin-male.glb": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Universal Animation Library mannequin (male)",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c36e7d47f98602587570b392fc7741699af122e83374df2cd97e4179a10947a0"
+ },
+ "MannequinMale/thumb.webp": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Universal Animation Library mannequin (male)",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "note": "rendered by kit-build thumbs"
+ }
+}
diff --git a/clips-index.json b/clips-index.json
new file mode 100644
index 0000000..6bd0864
--- /dev/null
+++ b/clips-index.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name": "motion-kit",
+ "title": "Motion (humanoid clips)",
+ "value": "motion-kit/clips.json",
+ "attribution": "motion-kit/attribution.html",
+ "copyright": "(c) Quaternius",
+ "license": "CC0-1.0",
+ "source": "https://github.com/theprototype-app/packs/tree/main/motion-kit"
+ }
+]
diff --git a/index.json b/index.json
index bcf992c..e5892aa 100644
--- a/index.json
+++ b/index.json
@@ -132,5 +132,14 @@
"copyright": "(c) Kay Lousberg (KayKit)",
"license": "CC0-1.0",
"source": "https://github.com/theprototype-app/packs/tree/main/avatars"
+ },
+ {
+ "name": "characters-kit",
+ "title": "Characters (VRM avatars + mannequins)",
+ "value": "characters-kit/default.json",
+ "attribution": "characters-kit/attribution.html",
+ "copyright": "(c) Polygonal Mind (100Avatars), Quaternius",
+ "license": "CC0-1.0",
+ "source": "https://github.com/theprototype-app/packs/tree/main/characters-kit"
}
]
diff --git a/motion-kit/clips.json b/motion-kit/clips.json
new file mode 100644
index 0000000..45c9239
--- /dev/null
+++ b/motion-kit/clips.json
@@ -0,0 +1,1046 @@
+[
+ {
+ "name": "Crouch_Fwd_Loop",
+ "file": "ual1/Crouch_Fwd_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 76244
+ },
+ {
+ "name": "Crouch_Idle_Loop",
+ "file": "ual1/Crouch_Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.9333,
+ "fps": 30,
+ "frames": 89,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 100456
+ },
+ {
+ "name": "Dance_Loop",
+ "file": "ual1/Dance_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1,
+ "fps": 30,
+ "frames": 31,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 50296
+ },
+ {
+ "name": "Death01",
+ "file": "ual1/Death01.tpanim",
+ "lib": "ual1",
+ "duration": 2.4,
+ "fps": 30,
+ "frames": 73,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 86608
+ },
+ {
+ "name": "Driving_Loop",
+ "file": "ual1/Driving_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.6667,
+ "fps": 30,
+ "frames": 51,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67608
+ },
+ {
+ "name": "Fixing_Kneeling",
+ "file": "ual1/Fixing_Kneeling.tpanim",
+ "lib": "ual1",
+ "duration": 5.2,
+ "fps": 30,
+ "frames": 157,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 159284
+ },
+ {
+ "name": "Hit_Chest",
+ "file": "ual1/Hit_Chest.tpanim",
+ "lib": "ual1",
+ "duration": 0.3333,
+ "fps": 30,
+ "frames": 11,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 32992
+ },
+ {
+ "name": "Hit_Head",
+ "file": "ual1/Hit_Head.tpanim",
+ "lib": "ual1",
+ "duration": 0.4333,
+ "fps": 30,
+ "frames": 14,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 35596
+ },
+ {
+ "name": "Idle_Loop",
+ "file": "ual1/Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 89188
+ },
+ {
+ "name": "Idle_Talking_Loop",
+ "file": "ual1/Idle_Talking_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.9333,
+ "fps": 30,
+ "frames": 89,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 100460
+ },
+ {
+ "name": "Idle_Torch_Loop",
+ "file": "ual1/Idle_Torch_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.2667,
+ "fps": 30,
+ "frames": 39,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 57244
+ },
+ {
+ "name": "Interact",
+ "file": "ual1/Interact.tpanim",
+ "lib": "ual1",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 76224
+ },
+ {
+ "name": "Jog_Fwd_Loop",
+ "file": "ual1/Jog_Fwd_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 0.9333,
+ "fps": 30,
+ "frames": 29,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 48592
+ },
+ {
+ "name": "Jump_Land",
+ "file": "ual1/Jump_Land.tpanim",
+ "lib": "ual1",
+ "duration": 1.2667,
+ "fps": 30,
+ "frames": 39,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 57228
+ },
+ {
+ "name": "Jump_Loop",
+ "file": "ual1/Jump_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 89188
+ },
+ {
+ "name": "Jump_Start",
+ "file": "ual1/Jump_Start.tpanim",
+ "lib": "ual1",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 58960
+ },
+ {
+ "name": "PickUp_Table",
+ "file": "ual1/PickUp_Table.tpanim",
+ "lib": "ual1",
+ "duration": 0.8333,
+ "fps": 30,
+ "frames": 26,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 45996
+ },
+ {
+ "name": "Pistol_Aim_Down",
+ "file": "ual1/Pistol_Aim_Down.tpanim",
+ "lib": "ual1",
+ "duration": 0.1667,
+ "fps": 30,
+ "frames": 6,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 28628
+ },
+ {
+ "name": "Pistol_Aim_Neutral",
+ "file": "ual1/Pistol_Aim_Neutral.tpanim",
+ "lib": "ual1",
+ "duration": 0.1667,
+ "fps": 30,
+ "frames": 6,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 28640
+ },
+ {
+ "name": "Pistol_Aim_Up",
+ "file": "ual1/Pistol_Aim_Up.tpanim",
+ "lib": "ual1",
+ "duration": 0.1667,
+ "fps": 30,
+ "frames": 6,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 28624
+ },
+ {
+ "name": "Pistol_Idle_Loop",
+ "file": "ual1/Pistol_Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.6667,
+ "fps": 30,
+ "frames": 51,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67620
+ },
+ {
+ "name": "Pistol_Reload",
+ "file": "ual1/Pistol_Reload.tpanim",
+ "lib": "ual1",
+ "duration": 1.6667,
+ "fps": 30,
+ "frames": 51,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67616
+ },
+ {
+ "name": "Pistol_Shoot",
+ "file": "ual1/Pistol_Shoot.tpanim",
+ "lib": "ual1",
+ "duration": 0.6333,
+ "fps": 30,
+ "frames": 20,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 40804
+ },
+ {
+ "name": "Punch_Cross",
+ "file": "ual1/Punch_Cross.tpanim",
+ "lib": "ual1",
+ "duration": 1,
+ "fps": 30,
+ "frames": 31,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 50300
+ },
+ {
+ "name": "Punch_Enter",
+ "file": "ual1/Punch_Enter.tpanim",
+ "lib": "ual1",
+ "duration": 0.8667,
+ "fps": 30,
+ "frames": 27,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 46860
+ },
+ {
+ "name": "Punch_Jab",
+ "file": "ual1/Punch_Jab.tpanim",
+ "lib": "ual1",
+ "duration": 0.8667,
+ "fps": 30,
+ "frames": 27,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 46852
+ },
+ {
+ "name": "Push_Loop",
+ "file": "ual1/Push_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.6667,
+ "fps": 30,
+ "frames": 81,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 93524
+ },
+ {
+ "name": "Roll",
+ "file": "ual1/Roll.tpanim",
+ "lib": "ual1",
+ "duration": 1.4667,
+ "fps": 30,
+ "frames": 45,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 62400
+ },
+ {
+ "name": "Roll_RM",
+ "file": "ual1/Roll_RM.tpanim",
+ "lib": "ual1",
+ "duration": 1.4667,
+ "fps": 30,
+ "frames": 45,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 62408
+ },
+ {
+ "name": "Sitting_Enter",
+ "file": "ual1/Sitting_Enter.tpanim",
+ "lib": "ual1",
+ "duration": 1.3,
+ "fps": 30,
+ "frames": 40,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 58108
+ },
+ {
+ "name": "Sitting_Exit",
+ "file": "ual1/Sitting_Exit.tpanim",
+ "lib": "ual1",
+ "duration": 1.0333,
+ "fps": 30,
+ "frames": 32,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 51188
+ },
+ {
+ "name": "Sitting_Idle_Loop",
+ "file": "ual1/Sitting_Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.6667,
+ "fps": 30,
+ "frames": 51,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67624
+ },
+ {
+ "name": "Sitting_Talking_Loop",
+ "file": "ual1/Sitting_Talking_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.9333,
+ "fps": 30,
+ "frames": 89,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 100468
+ },
+ {
+ "name": "Spell_Simple_Enter",
+ "file": "ual1/Spell_Simple_Enter.tpanim",
+ "lib": "ual1",
+ "duration": 0.5333,
+ "fps": 30,
+ "frames": 17,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 38224
+ },
+ {
+ "name": "Spell_Simple_Exit",
+ "file": "ual1/Spell_Simple_Exit.tpanim",
+ "lib": "ual1",
+ "duration": 0.4333,
+ "fps": 30,
+ "frames": 14,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 35620
+ },
+ {
+ "name": "Spell_Simple_Idle_Loop",
+ "file": "ual1/Spell_Simple_Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 2.1,
+ "fps": 30,
+ "frames": 64,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 78876
+ },
+ {
+ "name": "Spell_Simple_Shoot",
+ "file": "ual1/Spell_Simple_Shoot.tpanim",
+ "lib": "ual1",
+ "duration": 0.5,
+ "fps": 30,
+ "frames": 16,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 37344
+ },
+ {
+ "name": "Sprint_Loop",
+ "file": "ual1/Sprint_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 0.6667,
+ "fps": 30,
+ "frames": 21,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 41668
+ },
+ {
+ "name": "Swim_Fwd_Loop",
+ "file": "ual1/Swim_Fwd_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 58968
+ },
+ {
+ "name": "Swim_Idle_Loop",
+ "file": "ual1/Swim_Idle_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 3.3333,
+ "fps": 30,
+ "frames": 101,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 110880
+ },
+ {
+ "name": "Sword_Attack",
+ "file": "ual1/Sword_Attack.tpanim",
+ "lib": "ual1",
+ "duration": 1.5333,
+ "fps": 30,
+ "frames": 47,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 64152
+ },
+ {
+ "name": "Sword_Attack_RM",
+ "file": "ual1/Sword_Attack_RM.tpanim",
+ "lib": "ual1",
+ "duration": 1.5333,
+ "fps": 30,
+ "frames": 47,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 64160
+ },
+ {
+ "name": "Sword_Idle",
+ "file": "ual1/Sword_Idle.tpanim",
+ "lib": "ual1",
+ "duration": 1.6667,
+ "fps": 30,
+ "frames": 51,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67604
+ },
+ {
+ "name": "Walk_Formal_Loop",
+ "file": "ual1/Walk_Formal_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 58976
+ },
+ {
+ "name": "Walk_Loop",
+ "file": "ual1/Walk_Loop.tpanim",
+ "lib": "ual1",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 58956
+ },
+ {
+ "name": "Chest_Open",
+ "file": "ual2/Chest_Open.tpanim",
+ "lib": "ual2",
+ "duration": 1.3667,
+ "fps": 30,
+ "frames": 42,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 72212
+ },
+ {
+ "name": "ClimbUp_1m_RM",
+ "file": "ual2/ClimbUp_1m_RM.tpanim",
+ "lib": "ual2",
+ "duration": 0.6667,
+ "fps": 30,
+ "frames": 21,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 50024
+ },
+ {
+ "name": "Consume",
+ "file": "ual2/Consume.tpanim",
+ "lib": "ual2",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 71144
+ },
+ {
+ "name": "Farm_Harvest",
+ "file": "ual2/Farm_Harvest.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108112
+ },
+ {
+ "name": "Farm_PlantSeed",
+ "file": "ual2/Farm_PlantSeed.tpanim",
+ "lib": "ual2",
+ "duration": 2.7667,
+ "fps": 30,
+ "frames": 84,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 116584
+ },
+ {
+ "name": "Farm_Watering",
+ "file": "ual2/Farm_Watering.tpanim",
+ "lib": "ual2",
+ "duration": 3.8,
+ "fps": 30,
+ "frames": 115,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 149400
+ },
+ {
+ "name": "Hit_Knockback",
+ "file": "ual2/Hit_Knockback.tpanim",
+ "lib": "ual2",
+ "duration": 0.8333,
+ "fps": 30,
+ "frames": 26,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 55312
+ },
+ {
+ "name": "Hit_Knockback_RM",
+ "file": "ual2/Hit_Knockback_RM.tpanim",
+ "lib": "ual2",
+ "duration": 0.8333,
+ "fps": 30,
+ "frames": 26,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 55320
+ },
+ {
+ "name": "Idle_FoldArms_Loop",
+ "file": "ual2/Idle_FoldArms_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108128
+ },
+ {
+ "name": "Idle_Lantern_Loop",
+ "file": "ual2/Idle_Lantern_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108128
+ },
+ {
+ "name": "Idle_No_Loop",
+ "file": "ual2/Idle_No_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108112
+ },
+ {
+ "name": "Idle_Rail_Call",
+ "file": "ual2/Idle_Rail_Call.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108120
+ },
+ {
+ "name": "Idle_Rail_Loop",
+ "file": "ual2/Idle_Rail_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108116
+ },
+ {
+ "name": "Idle_Shield_Break",
+ "file": "ual2/Idle_Shield_Break.tpanim",
+ "lib": "ual2",
+ "duration": 1.0667,
+ "fps": 30,
+ "frames": 33,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 62724
+ },
+ {
+ "name": "Idle_Shield_Loop",
+ "file": "ual2/Idle_Shield_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108124
+ },
+ {
+ "name": "Idle_TalkingPhone_Loop",
+ "file": "ual2/Idle_TalkingPhone_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2.9333,
+ "fps": 30,
+ "frames": 89,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 121884
+ },
+ {
+ "name": "LayToIdle",
+ "file": "ual2/LayToIdle.tpanim",
+ "lib": "ual2",
+ "duration": 1.5333,
+ "fps": 30,
+ "frames": 47,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 77488
+ },
+ {
+ "name": "Melee_Hook",
+ "file": "ual2/Melee_Hook.tpanim",
+ "lib": "ual2",
+ "duration": 0.4667,
+ "fps": 30,
+ "frames": 15,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 43668
+ },
+ {
+ "name": "Melee_Hook_Rec",
+ "file": "ual2/Melee_Hook_Rec.tpanim",
+ "lib": "ual2",
+ "duration": 0.6,
+ "fps": 30,
+ "frames": 19,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 47916
+ },
+ {
+ "name": "NinjaJump_Idle_Loop",
+ "file": "ual2/NinjaJump_Idle_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 92288
+ },
+ {
+ "name": "NinjaJump_Land",
+ "file": "ual2/NinjaJump_Land.tpanim",
+ "lib": "ual2",
+ "duration": 1.2667,
+ "fps": 30,
+ "frames": 39,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 69052
+ },
+ {
+ "name": "NinjaJump_Start",
+ "file": "ual2/NinjaJump_Start.tpanim",
+ "lib": "ual2",
+ "duration": 0.9667,
+ "fps": 30,
+ "frames": 30,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 59548
+ },
+ {
+ "name": "OverhandThrow",
+ "file": "ual2/OverhandThrow.tpanim",
+ "lib": "ual2",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 71164
+ },
+ {
+ "name": "Shield_Dash_RM",
+ "file": "ual2/Shield_Dash_RM.tpanim",
+ "lib": "ual2",
+ "duration": 1.1,
+ "fps": 30,
+ "frames": 34,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 63768
+ },
+ {
+ "name": "Shield_OneShot",
+ "file": "ual2/Shield_OneShot.tpanim",
+ "lib": "ual2",
+ "duration": 0.8333,
+ "fps": 30,
+ "frames": 26,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 55316
+ },
+ {
+ "name": "Slide_Exit",
+ "file": "ual2/Slide_Exit.tpanim",
+ "lib": "ual2",
+ "duration": 0.5,
+ "fps": 30,
+ "frames": 16,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 44712
+ },
+ {
+ "name": "Slide_Loop",
+ "file": "ual2/Slide_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 92260
+ },
+ {
+ "name": "Slide_Start",
+ "file": "ual2/Slide_Start.tpanim",
+ "lib": "ual2",
+ "duration": 0.8333,
+ "fps": 30,
+ "frames": 26,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 55308
+ },
+ {
+ "name": "Sword_Block",
+ "file": "ual2/Sword_Block.tpanim",
+ "lib": "ual2",
+ "duration": 1.2333,
+ "fps": 30,
+ "frames": 38,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 67988
+ },
+ {
+ "name": "Sword_Dash_RM",
+ "file": "ual2/Sword_Dash_RM.tpanim",
+ "lib": "ual2",
+ "duration": 1.5667,
+ "fps": 30,
+ "frames": 48,
+ "loop": false,
+ "rootMotion": true,
+ "humanoid": true,
+ "bytes": 78556
+ },
+ {
+ "name": "Sword_Regular_A",
+ "file": "ual2/Sword_Regular_A.tpanim",
+ "lib": "ual2",
+ "duration": 0.4333,
+ "fps": 30,
+ "frames": 14,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 42624
+ },
+ {
+ "name": "Sword_Regular_A_Rec",
+ "file": "ual2/Sword_Regular_A_Rec.tpanim",
+ "lib": "ual2",
+ "duration": 0.9667,
+ "fps": 30,
+ "frames": 30,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 59560
+ },
+ {
+ "name": "Sword_Regular_B",
+ "file": "ual2/Sword_Regular_B.tpanim",
+ "lib": "ual2",
+ "duration": 0.5333,
+ "fps": 30,
+ "frames": 17,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 45800
+ },
+ {
+ "name": "Sword_Regular_B_Rec",
+ "file": "ual2/Sword_Regular_B_Rec.tpanim",
+ "lib": "ual2",
+ "duration": 1.0333,
+ "fps": 30,
+ "frames": 32,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 61672
+ },
+ {
+ "name": "Sword_Regular_C",
+ "file": "ual2/Sword_Regular_C.tpanim",
+ "lib": "ual2",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 92280
+ },
+ {
+ "name": "Sword_Regular_Combo",
+ "file": "ual2/Sword_Regular_Combo.tpanim",
+ "lib": "ual2",
+ "duration": 3,
+ "fps": 30,
+ "frames": 91,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 123976
+ },
+ {
+ "name": "TreeChopping_Loop",
+ "file": "ual2/TreeChopping_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 0.9667,
+ "fps": 30,
+ "frames": 30,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 59552
+ },
+ {
+ "name": "Walk_Carry_Loop",
+ "file": "ual2/Walk_Carry_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 2,
+ "fps": 30,
+ "frames": 61,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 92276
+ },
+ {
+ "name": "Yes",
+ "file": "ual2/Yes.tpanim",
+ "lib": "ual2",
+ "duration": 2.5,
+ "fps": 30,
+ "frames": 76,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 108088
+ },
+ {
+ "name": "Zombie_Idle_Loop",
+ "file": "ual2/Zombie_Idle_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 71172
+ },
+ {
+ "name": "Zombie_Scratch",
+ "file": "ual2/Zombie_Scratch.tpanim",
+ "lib": "ual2",
+ "duration": 1.8,
+ "fps": 30,
+ "frames": 55,
+ "loop": false,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 85956
+ },
+ {
+ "name": "Zombie_Walk_Fwd_Loop",
+ "file": "ual2/Zombie_Walk_Fwd_Loop.tpanim",
+ "lib": "ual2",
+ "duration": 1.3333,
+ "fps": 30,
+ "frames": 41,
+ "loop": true,
+ "rootMotion": false,
+ "humanoid": true,
+ "bytes": 71184
+ }
+]
diff --git a/motion-kit/licences.json b/motion-kit/licences.json
new file mode 100644
index 0000000..f7f4962
--- /dev/null
+++ b/motion-kit/licences.json
@@ -0,0 +1,698 @@
+{
+ "ual1/Crouch_Fwd_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Crouch_Fwd_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "681321c1f7eb95ae27e41e0a75451b15c05c65ad68921372696ab6a2f0f5b928"
+ },
+ "ual1/Crouch_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Crouch_Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "446b45f5c1e1eaa50eb09793efd1caf9183ff39936454626478a3ccedf896bf9"
+ },
+ "ual1/Dance_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Dance_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "58814049b52184f81719818bf2e96e7283ade515d24738258bb1c22ff5a73745"
+ },
+ "ual1/Death01.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Death01 (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "f1dcd5d40770012593138e5ca423935fdf2500688bb86ee0826040ab8153acba"
+ },
+ "ual1/Driving_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Driving_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "f7e9f585baec2b46e5d81b2896610a74852ca7db719a538474780906ee0ea201"
+ },
+ "ual1/Fixing_Kneeling.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Fixing_Kneeling (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "6b6180b95da986bc44c09dc3c7ad588f88c6ce2520ea8794d31200d62af2eb8a"
+ },
+ "ual1/Hit_Chest.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Hit_Chest (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "918f9f5fa085c6915694b0106fb83076727ee97f71f1c46c0a8ea9ef82c7784b"
+ },
+ "ual1/Hit_Head.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Hit_Head (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "ca3dde7010693922824f2dfd70b39af77824788927fe50b7a149fa60f1e9db7f"
+ },
+ "ual1/Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "af9d8a21130ffa983034d64903792d3fdd211c9f340ee306095f3959b1d74d62"
+ },
+ "ual1/Idle_Talking_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Talking_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "db450ee893481dbcaa10f1558ec3e620d9bbe8efa85c038b3558cc2551ce028a"
+ },
+ "ual1/Idle_Torch_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Torch_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "664992a4d37e604635c68d83e9d8818936026d70fe427a23ed622aaa7eb209e4"
+ },
+ "ual1/Interact.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Interact (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c29d5299490b5afc3f777f2bf3e1348414bcc95761736333120e6e60deae4174"
+ },
+ "ual1/Jog_Fwd_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Jog_Fwd_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "2131479fb28535b427b66a5489375cf728f78ef77da6c45c5131b79d5edec3c4"
+ },
+ "ual1/Jump_Land.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Jump_Land (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "a867479636bea5209351eac457da84fa81e0ce8dd1dfce7907c81f4c6e38024d"
+ },
+ "ual1/Jump_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Jump_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "84c5f4ee4dafd626ed10b0a834e1cc90909bcc13fbbb91f8a780e0ac788ddb4d"
+ },
+ "ual1/Jump_Start.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Jump_Start (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "aabcbabdc6c97d689a109eb33a03c0fec2afc3dce54437df1c7b7ec4ac0e263f"
+ },
+ "ual1/PickUp_Table.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "PickUp_Table (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "241013cfdab5258522bdc88dccb5a774fb8fd052ed02a960951e8ecdfd0a206f"
+ },
+ "ual1/Pistol_Aim_Down.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Aim_Down (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c318cae263e258f450455c5553694682cd618ff11b8050f47cf35e182d030637"
+ },
+ "ual1/Pistol_Aim_Neutral.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Aim_Neutral (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "dd3e32f604511c858c8fd52abeaa22385fe4f5d5f6573bd22efaf42bdec02441"
+ },
+ "ual1/Pistol_Aim_Up.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Aim_Up (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "6d53f03d11a72545d972d46bda6a177170eba865325be19b3712cf9758851fe8"
+ },
+ "ual1/Pistol_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "ae300b6a6a0936f7240af0ebd7cc2fe6f3abe908048e74507b591855afb623fe"
+ },
+ "ual1/Pistol_Reload.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Reload (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "d20e6aadcfa96962adf099da5770c30318ff7125a863fb814c6d7e6172ba35ed"
+ },
+ "ual1/Pistol_Shoot.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Pistol_Shoot (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "4be65ecc76dc22c2b33c8c937f1d527e9149ff993642ab7f91e8c5ee2570b4cc"
+ },
+ "ual1/Punch_Cross.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Punch_Cross (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "a8dc2bf78cc4579c6cc827d3c579e423f4e6590e8aa2056ea7f035e5e0a37e1d"
+ },
+ "ual1/Punch_Enter.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Punch_Enter (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "62007efc2b2a0dbfb200e256cb225a038deb2e35b4a304bae0ebcc8c86a4e324"
+ },
+ "ual1/Punch_Jab.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Punch_Jab (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "327493bc0d555c4959987a1b7c34aa4e812c09136a548cd04ad2abf6b2ea8f3a"
+ },
+ "ual1/Push_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Push_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "a2c3c86a73e1e18ba2977a4be1e91f14da13e3c8faea5a9194313974feb6b9ad"
+ },
+ "ual1/Roll.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Roll (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "fa348b32395e3258a2c1eb7e79d46929defbe27a408b691feaf1593f255cf849"
+ },
+ "ual1/Roll_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Roll_RM (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "64c07724d2f56e4f227e33e07d9964e9f3eaa8bf408a6995e7451df4bc29bd86"
+ },
+ "ual1/Sitting_Enter.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sitting_Enter (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1bc7b34828288c9adcdddb6248eab64d6ccdb49bb053586ae8b4e29820b2044b"
+ },
+ "ual1/Sitting_Exit.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sitting_Exit (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "fdf635fa3c28f9805a6f75c09a432d3fe2091cf5235b9dbf5da6768f2d4d39d1"
+ },
+ "ual1/Sitting_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sitting_Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "80fcbfe9fb31ecc32d36e96ab912ae8076de6f7d1b1571fcddd689b0963a843b"
+ },
+ "ual1/Sitting_Talking_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sitting_Talking_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "00266cafe4444afbbc0cc47014471dc72312a8ec84017077c97e69ed7f8f57cc"
+ },
+ "ual1/Spell_Simple_Enter.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Spell_Simple_Enter (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "cabb6f489bd5b3a94b94efb7128bfb542ede90e43c8fdd71b0aa9e9bc2913753"
+ },
+ "ual1/Spell_Simple_Exit.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Spell_Simple_Exit (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "4e9d0124ed5944b966b41f82aca98cf4b1d9beaef2b3c1a839e8d0527de6cd39"
+ },
+ "ual1/Spell_Simple_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Spell_Simple_Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "4f2d559035fe470246bbb6fc214e5c4f2d08b537d1a3da736bd92559e1894061"
+ },
+ "ual1/Spell_Simple_Shoot.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Spell_Simple_Shoot (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "28d6be8338144c31f0cddfcdb0278c33e27e08ed536e7d7bd2964242c0cb7d00"
+ },
+ "ual1/Sprint_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sprint_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "0e70dbf35f7d875cc2318d367fbe6f48c0861b5335afe2c6e97643b966a912f6"
+ },
+ "ual1/Swim_Fwd_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Swim_Fwd_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "aa19041cfd1894eb7f9e214d7fa5ce6a87a60b89a1f3da21abe001ee43d694f5"
+ },
+ "ual1/Swim_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Swim_Idle_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "b41819ee441bed8373817d185422ad48c57e29b5071efdbbf96dc22844cc079c"
+ },
+ "ual1/Sword_Attack.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Attack (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c0c41b04e070d3dc764a1a980faab2d3fecf8371457c62564c672947e69d99bb"
+ },
+ "ual1/Sword_Attack_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Attack_RM (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "b20066fe1d9a51221449d34c72c4b217070a1d06d0289d01c0f5e61ab91379c3"
+ },
+ "ual1/Sword_Idle.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Idle (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "4f97f24b3e2470418d2ddd2ee86c4a6eb1c4b4c174f95a17d353a3848a3a27a2"
+ },
+ "ual1/Walk_Formal_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Walk_Formal_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "06a3c0eaa87dd0470216856aac1237cbfee8022230ed920ef79dc8554732132d"
+ },
+ "ual1/Walk_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Walk_Loop (Universal Animation Library 1 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "6b8574c8dd56c564a99a71e6da9f5fc1a34245331136ec7461d9b093f72fd055"
+ },
+ "ual2/Chest_Open.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Chest_Open (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c1b195b2c462cd8dd7cd02792791fa11dd04ec79394575db336e707e8e979e03"
+ },
+ "ual2/ClimbUp_1m_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "ClimbUp_1m_RM (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "0bea5df3bb99df8a515a9b8ee0bb851737a362d7968ba4e7b82a9a9f43b5285a"
+ },
+ "ual2/Consume.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Consume (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1ab043e01f1c7a8d164a975acb942ab266273cf213dbeb2f350085caecafb10e"
+ },
+ "ual2/Farm_Harvest.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Farm_Harvest (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "de17fc130829aff034200038ebcd29a6e0578b82e7bd8b755212d7cfcb397fc0"
+ },
+ "ual2/Farm_PlantSeed.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Farm_PlantSeed (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "184fff55cb306160516b5a5f8c9a2f08fb152124d7aaf9cf731aacf077582ec6"
+ },
+ "ual2/Farm_Watering.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Farm_Watering (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c0575bf66ecd76a1e2e4cd0297f4dd2cfd9db3f01117385653dbd3f65b5f9b1d"
+ },
+ "ual2/Hit_Knockback.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Hit_Knockback (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "e273ea66687050fef1eafd4db517bb0665b633486d6c7ad508d062007e4ed08d"
+ },
+ "ual2/Hit_Knockback_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Hit_Knockback_RM (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "79b1a80d38c9bbca184d7c6410a473da04e25e92b829047aaa56ad337b029e45"
+ },
+ "ual2/Idle_FoldArms_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_FoldArms_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "415a0fbce4a715a6ef9ede98c2580b178fcdaeb402de6c97b4261a5d15b956de"
+ },
+ "ual2/Idle_Lantern_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Lantern_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1ecee9c7209c5e0af83d378b118fd866eaec4eac0eb27b6aaa507c043aa0b0ad"
+ },
+ "ual2/Idle_No_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_No_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "779e0b4688ace8270404b83de97519a14798e8538e5376463b4c04c24975c05a"
+ },
+ "ual2/Idle_Rail_Call.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Rail_Call (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c5c8d9eaecef86d6db04e853cdf18370919f30cc032a63aaf2b767b3cbe0ecb2"
+ },
+ "ual2/Idle_Rail_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Rail_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1fd74046af449fb9639aa80f9a2f35de17792a14173148d6728fc8906e92a853"
+ },
+ "ual2/Idle_Shield_Break.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Shield_Break (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "e5d03f96de8d4636279aae56cbf65fdfd3e12f894f11f74c342cb99075cd9ddc"
+ },
+ "ual2/Idle_Shield_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_Shield_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "dd350bfadec5be84d1dd5cf162e934606ba03edce280082ac12508e9532d4362"
+ },
+ "ual2/Idle_TalkingPhone_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Idle_TalkingPhone_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "772bb9eea71095d2d4be09f9072b9654a6c6b878ca2d056ceeb97c49071a8cfb"
+ },
+ "ual2/LayToIdle.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "LayToIdle (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "8eca454486cd789dbcefaf238f80dbc5564ffe3066de9c4c848565b8feeecf39"
+ },
+ "ual2/Melee_Hook.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Melee_Hook (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "72011dd1bcd120412c3d3e3eae6369bf041d012f0eab2b8e7c4d3d2f77dcc150"
+ },
+ "ual2/Melee_Hook_Rec.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Melee_Hook_Rec (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "c804c6607a2790c8e8a77f8b95970834915495d2caf63e9cbf621ca8d1ff468b"
+ },
+ "ual2/NinjaJump_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "NinjaJump_Idle_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1d71948de3d38adb607120e8ec38c6a71cf778c04d1c4f846cfd1814ea597c70"
+ },
+ "ual2/NinjaJump_Land.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "NinjaJump_Land (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "34f49a319a3122e311ba0fc859a61e16f158b4e95cc61c4be9198711b1ae06f8"
+ },
+ "ual2/NinjaJump_Start.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "NinjaJump_Start (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "9657931d6f1f508bc2f5d5a4bb146d075ba97b54e54f4fb185ec93e9bd274722"
+ },
+ "ual2/OverhandThrow.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "OverhandThrow (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "061b9138d556b8d07a20e1e6b9788c869ce22c57f8cf2ffddd1e40501f1eb226"
+ },
+ "ual2/Shield_Dash_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Shield_Dash_RM (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "913894001c34d72f0b5580fe8f3b9ee432b183eba5fdda1d808a4b7ee40f44f1"
+ },
+ "ual2/Shield_OneShot.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Shield_OneShot (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "1ea9163beb4b973e8d6ba9476ffb661e6f7d554f1d3a3ab73460a5e93a6f6591"
+ },
+ "ual2/Slide_Exit.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Slide_Exit (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "ffd9714ace97ff0eaaacb8b8e0c394452493f9d3467206e56334b4df6e5345cd"
+ },
+ "ual2/Slide_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Slide_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "344ce2431fb93861cdac801b01a81cb29bca7c017025ac37abc1c0b40d5af31a"
+ },
+ "ual2/Slide_Start.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Slide_Start (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "8f60d8cd0bc4d168dcb7eac82f0f7c62e9051a37318090a211eb6b6a2cf10c9c"
+ },
+ "ual2/Sword_Block.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Block (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "70205b65cc8aa96535993c20e7dcd6d423d981feb365483b2192230b1bcbea56"
+ },
+ "ual2/Sword_Dash_RM.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Dash_RM (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "d9272c2ec324eb6f74e93ff47f9040e72b79cf969258fc4133ee15c2c10fb3b7"
+ },
+ "ual2/Sword_Regular_A.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_A (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "65a440cb3384ec65de0239f4c3afe00970c8e2dc71e5f48415e6515cd8cc0d4a"
+ },
+ "ual2/Sword_Regular_A_Rec.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_A_Rec (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "0639cb5849600266f445f61dd583e67f0fc86f3bf896fbc702b907b3cecdf737"
+ },
+ "ual2/Sword_Regular_B.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_B (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "ca7a4b5ea23c081a89d47e43e34b97e09ddd06b325013024b8bea60fa4e34b30"
+ },
+ "ual2/Sword_Regular_B_Rec.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_B_Rec (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "79fcefc358f6d650204bb708accb20b6448cd6ff469153d7e04841d5815e547a"
+ },
+ "ual2/Sword_Regular_C.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_C (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "79d5e0fe004353fac6233cfaa7f50fc994da27ce8d64f0ddc528fc0579ef27a0"
+ },
+ "ual2/Sword_Regular_Combo.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Sword_Regular_Combo (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "b6ae3ab75af1e5ce2f0261414caa5b072dc9b754f9d992e770434c2f2cf3d31a"
+ },
+ "ual2/TreeChopping_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "TreeChopping_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "de8ac103682415caeb56fbfb8f56af11bfda6d8b87e238a4956d8d486750eadf"
+ },
+ "ual2/Walk_Carry_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Walk_Carry_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "9d95c01655bbda19acb5ac11ecb95ccb75e3cba8dca3b51f00d8cb74e1a266bf"
+ },
+ "ual2/Yes.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Yes (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "41da0e056b91c9ba53fcd965ffaba45c1901a296f5af24d68d4b15d9aa3dd152"
+ },
+ "ual2/Zombie_Idle_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Zombie_Idle_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "63cf018a08e1b3f4d721441faa0d0c7b0c82cbf293ec4aa4e8fa379d9b83f81b"
+ },
+ "ual2/Zombie_Scratch.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Zombie_Scratch (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "a361d3577183a17b601e44a118ac4d77649d1f6118a12125e8366194a99e4a98"
+ },
+ "ual2/Zombie_Walk_Fwd_Loop.tpanim": {
+ "licence": "CC0-1.0",
+ "author": "Quaternius",
+ "title": "Zombie_Walk_Fwd_Loop (Universal Animation Library 2 (Standard))",
+ "source": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "url": "https://quaternius.com",
+ "sha256": "27089c4fff9d5b7618819ddb138a0ffd79955bc086dcc6fffb0c4ea1f23225db"
+ }
+}
diff --git a/motion-kit/ual1/Crouch_Fwd_Loop.tpanim b/motion-kit/ual1/Crouch_Fwd_Loop.tpanim
new file mode 100644
index 0000000..fcb453a
Binary files /dev/null and b/motion-kit/ual1/Crouch_Fwd_Loop.tpanim differ
diff --git a/motion-kit/ual1/Crouch_Idle_Loop.tpanim b/motion-kit/ual1/Crouch_Idle_Loop.tpanim
new file mode 100644
index 0000000..ee400d9
Binary files /dev/null and b/motion-kit/ual1/Crouch_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Dance_Loop.tpanim b/motion-kit/ual1/Dance_Loop.tpanim
new file mode 100644
index 0000000..aa53294
Binary files /dev/null and b/motion-kit/ual1/Dance_Loop.tpanim differ
diff --git a/motion-kit/ual1/Death01.tpanim b/motion-kit/ual1/Death01.tpanim
new file mode 100644
index 0000000..a6a4c40
Binary files /dev/null and b/motion-kit/ual1/Death01.tpanim differ
diff --git a/motion-kit/ual1/Driving_Loop.tpanim b/motion-kit/ual1/Driving_Loop.tpanim
new file mode 100644
index 0000000..6f3888c
Binary files /dev/null and b/motion-kit/ual1/Driving_Loop.tpanim differ
diff --git a/motion-kit/ual1/Fixing_Kneeling.tpanim b/motion-kit/ual1/Fixing_Kneeling.tpanim
new file mode 100644
index 0000000..5a756b9
Binary files /dev/null and b/motion-kit/ual1/Fixing_Kneeling.tpanim differ
diff --git a/motion-kit/ual1/Hit_Chest.tpanim b/motion-kit/ual1/Hit_Chest.tpanim
new file mode 100644
index 0000000..d444b5a
Binary files /dev/null and b/motion-kit/ual1/Hit_Chest.tpanim differ
diff --git a/motion-kit/ual1/Hit_Head.tpanim b/motion-kit/ual1/Hit_Head.tpanim
new file mode 100644
index 0000000..5faa46a
Binary files /dev/null and b/motion-kit/ual1/Hit_Head.tpanim differ
diff --git a/motion-kit/ual1/Idle_Loop.tpanim b/motion-kit/ual1/Idle_Loop.tpanim
new file mode 100644
index 0000000..18d7586
Binary files /dev/null and b/motion-kit/ual1/Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Idle_Talking_Loop.tpanim b/motion-kit/ual1/Idle_Talking_Loop.tpanim
new file mode 100644
index 0000000..ef7ad99
Binary files /dev/null and b/motion-kit/ual1/Idle_Talking_Loop.tpanim differ
diff --git a/motion-kit/ual1/Idle_Torch_Loop.tpanim b/motion-kit/ual1/Idle_Torch_Loop.tpanim
new file mode 100644
index 0000000..8e5409b
Binary files /dev/null and b/motion-kit/ual1/Idle_Torch_Loop.tpanim differ
diff --git a/motion-kit/ual1/Interact.tpanim b/motion-kit/ual1/Interact.tpanim
new file mode 100644
index 0000000..13bbfb9
Binary files /dev/null and b/motion-kit/ual1/Interact.tpanim differ
diff --git a/motion-kit/ual1/Jog_Fwd_Loop.tpanim b/motion-kit/ual1/Jog_Fwd_Loop.tpanim
new file mode 100644
index 0000000..e62fcd3
Binary files /dev/null and b/motion-kit/ual1/Jog_Fwd_Loop.tpanim differ
diff --git a/motion-kit/ual1/Jump_Land.tpanim b/motion-kit/ual1/Jump_Land.tpanim
new file mode 100644
index 0000000..1b8d648
Binary files /dev/null and b/motion-kit/ual1/Jump_Land.tpanim differ
diff --git a/motion-kit/ual1/Jump_Loop.tpanim b/motion-kit/ual1/Jump_Loop.tpanim
new file mode 100644
index 0000000..9e7ee33
Binary files /dev/null and b/motion-kit/ual1/Jump_Loop.tpanim differ
diff --git a/motion-kit/ual1/Jump_Start.tpanim b/motion-kit/ual1/Jump_Start.tpanim
new file mode 100644
index 0000000..8a7f732
Binary files /dev/null and b/motion-kit/ual1/Jump_Start.tpanim differ
diff --git a/motion-kit/ual1/PickUp_Table.tpanim b/motion-kit/ual1/PickUp_Table.tpanim
new file mode 100644
index 0000000..06f18b7
Binary files /dev/null and b/motion-kit/ual1/PickUp_Table.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Aim_Down.tpanim b/motion-kit/ual1/Pistol_Aim_Down.tpanim
new file mode 100644
index 0000000..30ababd
Binary files /dev/null and b/motion-kit/ual1/Pistol_Aim_Down.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Aim_Neutral.tpanim b/motion-kit/ual1/Pistol_Aim_Neutral.tpanim
new file mode 100644
index 0000000..1d3a36e
Binary files /dev/null and b/motion-kit/ual1/Pistol_Aim_Neutral.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Aim_Up.tpanim b/motion-kit/ual1/Pistol_Aim_Up.tpanim
new file mode 100644
index 0000000..0af87bd
Binary files /dev/null and b/motion-kit/ual1/Pistol_Aim_Up.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Idle_Loop.tpanim b/motion-kit/ual1/Pistol_Idle_Loop.tpanim
new file mode 100644
index 0000000..c167437
Binary files /dev/null and b/motion-kit/ual1/Pistol_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Reload.tpanim b/motion-kit/ual1/Pistol_Reload.tpanim
new file mode 100644
index 0000000..9229752
Binary files /dev/null and b/motion-kit/ual1/Pistol_Reload.tpanim differ
diff --git a/motion-kit/ual1/Pistol_Shoot.tpanim b/motion-kit/ual1/Pistol_Shoot.tpanim
new file mode 100644
index 0000000..7d874c7
Binary files /dev/null and b/motion-kit/ual1/Pistol_Shoot.tpanim differ
diff --git a/motion-kit/ual1/Punch_Cross.tpanim b/motion-kit/ual1/Punch_Cross.tpanim
new file mode 100644
index 0000000..336707f
Binary files /dev/null and b/motion-kit/ual1/Punch_Cross.tpanim differ
diff --git a/motion-kit/ual1/Punch_Enter.tpanim b/motion-kit/ual1/Punch_Enter.tpanim
new file mode 100644
index 0000000..86aa387
Binary files /dev/null and b/motion-kit/ual1/Punch_Enter.tpanim differ
diff --git a/motion-kit/ual1/Punch_Jab.tpanim b/motion-kit/ual1/Punch_Jab.tpanim
new file mode 100644
index 0000000..a973953
Binary files /dev/null and b/motion-kit/ual1/Punch_Jab.tpanim differ
diff --git a/motion-kit/ual1/Push_Loop.tpanim b/motion-kit/ual1/Push_Loop.tpanim
new file mode 100644
index 0000000..01ec988
Binary files /dev/null and b/motion-kit/ual1/Push_Loop.tpanim differ
diff --git a/motion-kit/ual1/Roll.tpanim b/motion-kit/ual1/Roll.tpanim
new file mode 100644
index 0000000..89d8d29
Binary files /dev/null and b/motion-kit/ual1/Roll.tpanim differ
diff --git a/motion-kit/ual1/Roll_RM.tpanim b/motion-kit/ual1/Roll_RM.tpanim
new file mode 100644
index 0000000..c78145d
Binary files /dev/null and b/motion-kit/ual1/Roll_RM.tpanim differ
diff --git a/motion-kit/ual1/Sitting_Enter.tpanim b/motion-kit/ual1/Sitting_Enter.tpanim
new file mode 100644
index 0000000..77a92c0
Binary files /dev/null and b/motion-kit/ual1/Sitting_Enter.tpanim differ
diff --git a/motion-kit/ual1/Sitting_Exit.tpanim b/motion-kit/ual1/Sitting_Exit.tpanim
new file mode 100644
index 0000000..5fe00c3
Binary files /dev/null and b/motion-kit/ual1/Sitting_Exit.tpanim differ
diff --git a/motion-kit/ual1/Sitting_Idle_Loop.tpanim b/motion-kit/ual1/Sitting_Idle_Loop.tpanim
new file mode 100644
index 0000000..2e87ca7
Binary files /dev/null and b/motion-kit/ual1/Sitting_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Sitting_Talking_Loop.tpanim b/motion-kit/ual1/Sitting_Talking_Loop.tpanim
new file mode 100644
index 0000000..c56b5b7
Binary files /dev/null and b/motion-kit/ual1/Sitting_Talking_Loop.tpanim differ
diff --git a/motion-kit/ual1/Spell_Simple_Enter.tpanim b/motion-kit/ual1/Spell_Simple_Enter.tpanim
new file mode 100644
index 0000000..a78d9d3
Binary files /dev/null and b/motion-kit/ual1/Spell_Simple_Enter.tpanim differ
diff --git a/motion-kit/ual1/Spell_Simple_Exit.tpanim b/motion-kit/ual1/Spell_Simple_Exit.tpanim
new file mode 100644
index 0000000..356051c
Binary files /dev/null and b/motion-kit/ual1/Spell_Simple_Exit.tpanim differ
diff --git a/motion-kit/ual1/Spell_Simple_Idle_Loop.tpanim b/motion-kit/ual1/Spell_Simple_Idle_Loop.tpanim
new file mode 100644
index 0000000..293816f
Binary files /dev/null and b/motion-kit/ual1/Spell_Simple_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Spell_Simple_Shoot.tpanim b/motion-kit/ual1/Spell_Simple_Shoot.tpanim
new file mode 100644
index 0000000..b1cf261
Binary files /dev/null and b/motion-kit/ual1/Spell_Simple_Shoot.tpanim differ
diff --git a/motion-kit/ual1/Sprint_Loop.tpanim b/motion-kit/ual1/Sprint_Loop.tpanim
new file mode 100644
index 0000000..eb1e102
Binary files /dev/null and b/motion-kit/ual1/Sprint_Loop.tpanim differ
diff --git a/motion-kit/ual1/Swim_Fwd_Loop.tpanim b/motion-kit/ual1/Swim_Fwd_Loop.tpanim
new file mode 100644
index 0000000..122cd05
Binary files /dev/null and b/motion-kit/ual1/Swim_Fwd_Loop.tpanim differ
diff --git a/motion-kit/ual1/Swim_Idle_Loop.tpanim b/motion-kit/ual1/Swim_Idle_Loop.tpanim
new file mode 100644
index 0000000..6a87fb5
Binary files /dev/null and b/motion-kit/ual1/Swim_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual1/Sword_Attack.tpanim b/motion-kit/ual1/Sword_Attack.tpanim
new file mode 100644
index 0000000..f6b4a6d
Binary files /dev/null and b/motion-kit/ual1/Sword_Attack.tpanim differ
diff --git a/motion-kit/ual1/Sword_Attack_RM.tpanim b/motion-kit/ual1/Sword_Attack_RM.tpanim
new file mode 100644
index 0000000..104536a
Binary files /dev/null and b/motion-kit/ual1/Sword_Attack_RM.tpanim differ
diff --git a/motion-kit/ual1/Sword_Idle.tpanim b/motion-kit/ual1/Sword_Idle.tpanim
new file mode 100644
index 0000000..a85e71d
Binary files /dev/null and b/motion-kit/ual1/Sword_Idle.tpanim differ
diff --git a/motion-kit/ual1/Walk_Formal_Loop.tpanim b/motion-kit/ual1/Walk_Formal_Loop.tpanim
new file mode 100644
index 0000000..4e532b5
Binary files /dev/null and b/motion-kit/ual1/Walk_Formal_Loop.tpanim differ
diff --git a/motion-kit/ual1/Walk_Loop.tpanim b/motion-kit/ual1/Walk_Loop.tpanim
new file mode 100644
index 0000000..5227eb7
Binary files /dev/null and b/motion-kit/ual1/Walk_Loop.tpanim differ
diff --git a/motion-kit/ual2/Chest_Open.tpanim b/motion-kit/ual2/Chest_Open.tpanim
new file mode 100644
index 0000000..9d502be
Binary files /dev/null and b/motion-kit/ual2/Chest_Open.tpanim differ
diff --git a/motion-kit/ual2/ClimbUp_1m_RM.tpanim b/motion-kit/ual2/ClimbUp_1m_RM.tpanim
new file mode 100644
index 0000000..fcc6cfc
Binary files /dev/null and b/motion-kit/ual2/ClimbUp_1m_RM.tpanim differ
diff --git a/motion-kit/ual2/Consume.tpanim b/motion-kit/ual2/Consume.tpanim
new file mode 100644
index 0000000..2e99171
Binary files /dev/null and b/motion-kit/ual2/Consume.tpanim differ
diff --git a/motion-kit/ual2/Farm_Harvest.tpanim b/motion-kit/ual2/Farm_Harvest.tpanim
new file mode 100644
index 0000000..bd9a882
Binary files /dev/null and b/motion-kit/ual2/Farm_Harvest.tpanim differ
diff --git a/motion-kit/ual2/Farm_PlantSeed.tpanim b/motion-kit/ual2/Farm_PlantSeed.tpanim
new file mode 100644
index 0000000..6811ac5
Binary files /dev/null and b/motion-kit/ual2/Farm_PlantSeed.tpanim differ
diff --git a/motion-kit/ual2/Farm_Watering.tpanim b/motion-kit/ual2/Farm_Watering.tpanim
new file mode 100644
index 0000000..fe516f2
Binary files /dev/null and b/motion-kit/ual2/Farm_Watering.tpanim differ
diff --git a/motion-kit/ual2/Hit_Knockback.tpanim b/motion-kit/ual2/Hit_Knockback.tpanim
new file mode 100644
index 0000000..fa29461
Binary files /dev/null and b/motion-kit/ual2/Hit_Knockback.tpanim differ
diff --git a/motion-kit/ual2/Hit_Knockback_RM.tpanim b/motion-kit/ual2/Hit_Knockback_RM.tpanim
new file mode 100644
index 0000000..f870cef
Binary files /dev/null and b/motion-kit/ual2/Hit_Knockback_RM.tpanim differ
diff --git a/motion-kit/ual2/Idle_FoldArms_Loop.tpanim b/motion-kit/ual2/Idle_FoldArms_Loop.tpanim
new file mode 100644
index 0000000..9d3b100
Binary files /dev/null and b/motion-kit/ual2/Idle_FoldArms_Loop.tpanim differ
diff --git a/motion-kit/ual2/Idle_Lantern_Loop.tpanim b/motion-kit/ual2/Idle_Lantern_Loop.tpanim
new file mode 100644
index 0000000..f509b74
Binary files /dev/null and b/motion-kit/ual2/Idle_Lantern_Loop.tpanim differ
diff --git a/motion-kit/ual2/Idle_No_Loop.tpanim b/motion-kit/ual2/Idle_No_Loop.tpanim
new file mode 100644
index 0000000..931a451
Binary files /dev/null and b/motion-kit/ual2/Idle_No_Loop.tpanim differ
diff --git a/motion-kit/ual2/Idle_Rail_Call.tpanim b/motion-kit/ual2/Idle_Rail_Call.tpanim
new file mode 100644
index 0000000..9d585c5
Binary files /dev/null and b/motion-kit/ual2/Idle_Rail_Call.tpanim differ
diff --git a/motion-kit/ual2/Idle_Rail_Loop.tpanim b/motion-kit/ual2/Idle_Rail_Loop.tpanim
new file mode 100644
index 0000000..2a26f3a
Binary files /dev/null and b/motion-kit/ual2/Idle_Rail_Loop.tpanim differ
diff --git a/motion-kit/ual2/Idle_Shield_Break.tpanim b/motion-kit/ual2/Idle_Shield_Break.tpanim
new file mode 100644
index 0000000..94ceef4
Binary files /dev/null and b/motion-kit/ual2/Idle_Shield_Break.tpanim differ
diff --git a/motion-kit/ual2/Idle_Shield_Loop.tpanim b/motion-kit/ual2/Idle_Shield_Loop.tpanim
new file mode 100644
index 0000000..90ae57d
Binary files /dev/null and b/motion-kit/ual2/Idle_Shield_Loop.tpanim differ
diff --git a/motion-kit/ual2/Idle_TalkingPhone_Loop.tpanim b/motion-kit/ual2/Idle_TalkingPhone_Loop.tpanim
new file mode 100644
index 0000000..b58087c
Binary files /dev/null and b/motion-kit/ual2/Idle_TalkingPhone_Loop.tpanim differ
diff --git a/motion-kit/ual2/LayToIdle.tpanim b/motion-kit/ual2/LayToIdle.tpanim
new file mode 100644
index 0000000..222a8b7
Binary files /dev/null and b/motion-kit/ual2/LayToIdle.tpanim differ
diff --git a/motion-kit/ual2/Melee_Hook.tpanim b/motion-kit/ual2/Melee_Hook.tpanim
new file mode 100644
index 0000000..63257e4
Binary files /dev/null and b/motion-kit/ual2/Melee_Hook.tpanim differ
diff --git a/motion-kit/ual2/Melee_Hook_Rec.tpanim b/motion-kit/ual2/Melee_Hook_Rec.tpanim
new file mode 100644
index 0000000..dcd3283
Binary files /dev/null and b/motion-kit/ual2/Melee_Hook_Rec.tpanim differ
diff --git a/motion-kit/ual2/NinjaJump_Idle_Loop.tpanim b/motion-kit/ual2/NinjaJump_Idle_Loop.tpanim
new file mode 100644
index 0000000..8ecf7e8
Binary files /dev/null and b/motion-kit/ual2/NinjaJump_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual2/NinjaJump_Land.tpanim b/motion-kit/ual2/NinjaJump_Land.tpanim
new file mode 100644
index 0000000..9cfcac0
Binary files /dev/null and b/motion-kit/ual2/NinjaJump_Land.tpanim differ
diff --git a/motion-kit/ual2/NinjaJump_Start.tpanim b/motion-kit/ual2/NinjaJump_Start.tpanim
new file mode 100644
index 0000000..12aca26
Binary files /dev/null and b/motion-kit/ual2/NinjaJump_Start.tpanim differ
diff --git a/motion-kit/ual2/OverhandThrow.tpanim b/motion-kit/ual2/OverhandThrow.tpanim
new file mode 100644
index 0000000..fa19c40
Binary files /dev/null and b/motion-kit/ual2/OverhandThrow.tpanim differ
diff --git a/motion-kit/ual2/Shield_Dash_RM.tpanim b/motion-kit/ual2/Shield_Dash_RM.tpanim
new file mode 100644
index 0000000..0d7ecbc
Binary files /dev/null and b/motion-kit/ual2/Shield_Dash_RM.tpanim differ
diff --git a/motion-kit/ual2/Shield_OneShot.tpanim b/motion-kit/ual2/Shield_OneShot.tpanim
new file mode 100644
index 0000000..4a400ed
Binary files /dev/null and b/motion-kit/ual2/Shield_OneShot.tpanim differ
diff --git a/motion-kit/ual2/Slide_Exit.tpanim b/motion-kit/ual2/Slide_Exit.tpanim
new file mode 100644
index 0000000..33a44ee
Binary files /dev/null and b/motion-kit/ual2/Slide_Exit.tpanim differ
diff --git a/motion-kit/ual2/Slide_Loop.tpanim b/motion-kit/ual2/Slide_Loop.tpanim
new file mode 100644
index 0000000..e98ed4e
Binary files /dev/null and b/motion-kit/ual2/Slide_Loop.tpanim differ
diff --git a/motion-kit/ual2/Slide_Start.tpanim b/motion-kit/ual2/Slide_Start.tpanim
new file mode 100644
index 0000000..0827eb5
Binary files /dev/null and b/motion-kit/ual2/Slide_Start.tpanim differ
diff --git a/motion-kit/ual2/Sword_Block.tpanim b/motion-kit/ual2/Sword_Block.tpanim
new file mode 100644
index 0000000..cb7e64f
Binary files /dev/null and b/motion-kit/ual2/Sword_Block.tpanim differ
diff --git a/motion-kit/ual2/Sword_Dash_RM.tpanim b/motion-kit/ual2/Sword_Dash_RM.tpanim
new file mode 100644
index 0000000..38a248b
Binary files /dev/null and b/motion-kit/ual2/Sword_Dash_RM.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_A.tpanim b/motion-kit/ual2/Sword_Regular_A.tpanim
new file mode 100644
index 0000000..4af370c
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_A.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_A_Rec.tpanim b/motion-kit/ual2/Sword_Regular_A_Rec.tpanim
new file mode 100644
index 0000000..3abf2d6
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_A_Rec.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_B.tpanim b/motion-kit/ual2/Sword_Regular_B.tpanim
new file mode 100644
index 0000000..31a32a8
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_B.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_B_Rec.tpanim b/motion-kit/ual2/Sword_Regular_B_Rec.tpanim
new file mode 100644
index 0000000..43e32da
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_B_Rec.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_C.tpanim b/motion-kit/ual2/Sword_Regular_C.tpanim
new file mode 100644
index 0000000..5618e1d
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_C.tpanim differ
diff --git a/motion-kit/ual2/Sword_Regular_Combo.tpanim b/motion-kit/ual2/Sword_Regular_Combo.tpanim
new file mode 100644
index 0000000..b7e6a82
Binary files /dev/null and b/motion-kit/ual2/Sword_Regular_Combo.tpanim differ
diff --git a/motion-kit/ual2/TreeChopping_Loop.tpanim b/motion-kit/ual2/TreeChopping_Loop.tpanim
new file mode 100644
index 0000000..df41af6
Binary files /dev/null and b/motion-kit/ual2/TreeChopping_Loop.tpanim differ
diff --git a/motion-kit/ual2/Walk_Carry_Loop.tpanim b/motion-kit/ual2/Walk_Carry_Loop.tpanim
new file mode 100644
index 0000000..102a8d3
Binary files /dev/null and b/motion-kit/ual2/Walk_Carry_Loop.tpanim differ
diff --git a/motion-kit/ual2/Yes.tpanim b/motion-kit/ual2/Yes.tpanim
new file mode 100644
index 0000000..400610f
Binary files /dev/null and b/motion-kit/ual2/Yes.tpanim differ
diff --git a/motion-kit/ual2/Zombie_Idle_Loop.tpanim b/motion-kit/ual2/Zombie_Idle_Loop.tpanim
new file mode 100644
index 0000000..a5154e7
Binary files /dev/null and b/motion-kit/ual2/Zombie_Idle_Loop.tpanim differ
diff --git a/motion-kit/ual2/Zombie_Scratch.tpanim b/motion-kit/ual2/Zombie_Scratch.tpanim
new file mode 100644
index 0000000..60f8a16
Binary files /dev/null and b/motion-kit/ual2/Zombie_Scratch.tpanim differ
diff --git a/motion-kit/ual2/Zombie_Walk_Fwd_Loop.tpanim b/motion-kit/ual2/Zombie_Walk_Fwd_Loop.tpanim
new file mode 100644
index 0000000..78a0372
Binary files /dev/null and b/motion-kit/ual2/Zombie_Walk_Fwd_Loop.tpanim differ
diff --git a/tools/characters-kit/build.mjs b/tools/characters-kit/build.mjs
new file mode 100644
index 0000000..bc40258
--- /dev/null
+++ b/tools/characters-kit/build.mjs
@@ -0,0 +1,117 @@
+// tools/characters-kit/build.mjs — the characters-kit pack (core roadmap 57 M5): CC0 humanoids for the
+// Characters & motion work — every one maps onto VRM 1.0's humanoid, so any motion-kit clip plays on it.
+//
+// node tools/characters-kit/build.mjs [--src ]
+// node tools/kit-build/kit-build.mjs thumbs characters-kit --only MannequinFemale,MannequinMale
+// node tools/kit-build/kit-build.mjs dims characters-kit --write
+//
+// Sources (tools/characters-kit/sources.json, every file pinned by sha256):
+// - 100Avatars R1 / R2 by Polygonal Mind (CC0 1.0), the VRM files as listed by the Open Source
+// Avatars registry (github.com/ToxSam/open-source-avatars, itself CC0). Shipped AS THEY ARE: a VRM's
+// own meta is its licence statement, and the licence lint (tools/licence-lint) reads it per file.
+// R3 is left out on purpose: its files point at an extra licence URL beside "CC0".
+// - Quaternius' Universal Animation Library mannequins (CC0 1.0, OpenGameArt): the female one from
+// UAL 2 Standard as it ships; the male one from UAL 1 Standard with its 46 clips taken OUT (they ship
+// in motion-kit, once).
+// Outputs: characters-kit/- /glTF-Binary/,
- /thumb.webp, default.json, licences.json
+// (one record per shipped file — what the lint checks), attribution.html.
+import fs from 'node:fs';
+import path from 'node:path';
+import os from 'node:os';
+import crypto from 'node:crypto';
+import { execFileSync } from 'node:child_process';
+import { load, makeIO, REPO } from '../kit-build/lib/deps.mjs';
+
+const { prune } = await load('@gltf-transform/functions');
+const sharp = (await load('sharp')).default;
+
+const args = process.argv.slice(2);
+const opt = (/** @type {string} */ name) => {
+ const i = args.indexOf(name);
+ return i >= 0 ? args[i + 1] : undefined;
+};
+const SRC = opt('--src') || path.join(os.homedir(), '.cache/tp-characters-kit');
+const OUT = path.join(REPO, 'characters-kit');
+const sources = JSON.parse(fs.readFileSync(path.join(REPO, 'tools/characters-kit/sources.json'), 'utf8'));
+
+const sha = (/** @type {Buffer} */ b) => crypto.createHash('sha256').update(b).digest('hex');
+
+/** a pinned download (cached) @param {string} url @param {string} want @param {string} name */
+async function fetchPinned(url, want, name) {
+ fs.mkdirSync(SRC, { recursive: true });
+ const file = path.join(SRC, name);
+ if (!fs.existsSync(file) || sha(fs.readFileSync(file)) !== want) {
+ const res = await fetch(url);
+ if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`);
+ fs.writeFileSync(file, Buffer.from(await res.arrayBuffer()));
+ }
+ const got = sha(fs.readFileSync(file));
+ if (got !== want) throw new Error(`${name}: sha256 ${got} is not the pinned ${want}`);
+ return file;
+}
+
+/** a 512² transparent webp from any image @param {Buffer | string} input @param {string} out */
+async function thumb(input, out) {
+ await sharp(input, { animated: false })
+ .resize(512, 512, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
+ .webp({ quality: 82 })
+ .toFile(out);
+}
+
+/** @type {any[]} */
+const rows = [];
+/** @type {Record} */
+const licences = {};
+fs.mkdirSync(OUT, { recursive: true });
+
+for (const v of sources.vrm) {
+ const vrm = await fetchPinned(v.url, v.sha256, `100avatars-${v.series}-${v.number}-${v.name}.vrm`);
+ const png = await fetchPinned(v.thumbnail, v.thumbnailSha256, `100avatars-${v.series}-${v.number}-${v.name}-thumb.png`);
+ const dir = path.join(OUT, v.name, 'glTF-Binary');
+ fs.mkdirSync(dir, { recursive: true });
+ fs.copyFileSync(vrm, path.join(dir, v.file));
+ await thumb(png, path.join(OUT, v.name, 'thumb.webp'));
+ const credit = { licence: 'CC0-1.0', author: 'Polygonal Mind', title: `100Avatars ${v.series} #${v.number} ${v.name}`, source: `https://github.com/ToxSam/open-source-avatars (id ${v.registryId})`, url: v.url };
+ licences[`${v.name}/glTF-Binary/${v.file}`] = { ...credit, sha256: v.sha256 };
+ licences[`${v.name}/thumb.webp`] = { ...credit, note: 'the registry thumbnail, resized' };
+ rows.push({ name: v.name, screenshot: 'thumb.webp', label: `${v.name} (VRM avatar, 100Avatars ${v.series} #${v.number})`, variants: { 'glTF-Binary': v.file } });
+}
+
+const io = makeIO();
+for (const q of sources.quaternius) {
+ const zip = await fetchPinned(q.zip, q.sha256, path.basename(new URL(q.zip).pathname));
+ // unpack once beside the cached zip (unzip treats an entry name with [ ] as a wildcard pattern)
+ const unpacked = zip.replace(/\.zip$/, '');
+ if (!fs.existsSync(path.join(unpacked, q.entry))) execFileSync('unzip', ['-o', '-q', zip, '-d', unpacked]);
+ const bytes = fs.readFileSync(path.join(unpacked, q.entry));
+ const dir = path.join(OUT, q.name, 'glTF-Binary');
+ fs.mkdirSync(dir, { recursive: true });
+ const out = path.join(dir, q.file);
+ if (q.stripAnimations) {
+ const doc = await io.readBinary(new Uint8Array(bytes));
+ // dispose the samplers' accessors too: an orphan animation sampler still references them
+ for (const a of doc.getRoot().listAnimations()) {
+ for (const smp of a.listSamplers()) {
+ smp.getInput()?.dispose();
+ smp.getOutput()?.dispose();
+ smp.dispose();
+ }
+ for (const ch of a.listChannels()) ch.dispose();
+ a.dispose();
+ }
+ await doc.transform(prune());
+ fs.writeFileSync(out, await io.writeBinary(doc));
+ } else fs.writeFileSync(out, bytes);
+ const credit = { licence: 'CC0-1.0', author: 'Quaternius', title: q.stripAnimations ? 'Universal Animation Library mannequin (male)' : 'Universal Animation Library 2 mannequin (female)', source: q.zip, url: 'https://quaternius.com' };
+ licences[`${q.name}/glTF-Binary/${q.file}`] = { ...credit, sha256: sha(fs.readFileSync(out)) };
+ licences[`${q.name}/thumb.webp`] = { ...credit, note: 'rendered by kit-build thumbs' };
+ rows.push({ name: q.name, screenshot: 'thumb.webp', label: `${q.stripAnimations ? 'Male' : 'Female'} mannequin (Quaternius, rigged — motion-kit clips play on it)`, variants: { 'glTF-Binary': q.file } });
+}
+
+// keep measured dims from an earlier `dims --write`
+const listFile = path.join(OUT, 'default.json');
+const old = fs.existsSync(listFile) ? JSON.parse(fs.readFileSync(listFile, 'utf8')) : [];
+for (const r of rows) Object.assign(r, Object.fromEntries(Object.entries(old.find((/** @type {any} */ o) => o.name === r.name) ?? {}).filter(([k]) => ['size', 'box', 'tris', 'bytes', 'animated'].includes(k))));
+fs.writeFileSync(listFile, JSON.stringify(rows, null, '\t') + '\n');
+fs.writeFileSync(path.join(OUT, 'licences.json'), JSON.stringify(licences, null, '\t') + '\n');
+console.log(`characters-kit: ${rows.length} items, ${Object.keys(licences).length} licence records`);
diff --git a/tools/characters-kit/sources.json b/tools/characters-kit/sources.json
new file mode 100644
index 0000000..cc6ead6
--- /dev/null
+++ b/tools/characters-kit/sources.json
@@ -0,0 +1,111 @@
+{
+ "note": "57 M5 characters-kit sources, pinned by sha256. 100Avatars by Polygonal Mind (CC0 1.0, via the Open Source Avatars registry github.com/ToxSam/open-source-avatars, CC0) — each VRM also states CC0 in its own meta (the licence lint reads it). Quaternius mannequins from the Universal Animation Library 1 / 2 Standard (CC0 1.0, OpenGameArt).",
+ "vrm": [
+ {
+ "name": "Astronaut",
+ "file": "astronaut.vrm",
+ "number": "048",
+ "series": "R1",
+ "registryId": "e69fd8b9-d6ae-44ca-84e0-be4bb075d426",
+ "url": "https://arweave.net/T0c0z_XEPQHy3vyXz31XB22s_6JTqHdnau8exq_I8tI",
+ "sha256": "1cb8218610deafc0fd4608507fc52cf3303de84b0f641b9a452efb5e8a0e6f23",
+ "thumbnail": "https://arweave.net/Qo512sj7GqyM2wvlubg4aPyA-Hl_VTxRGYiDZ0A4Wx4",
+ "thumbnailSha256": "e12cc43b73ab8c3c977ba47509928f4f8a7c89393aaba7c5bcbd29f116e2c6f6"
+ },
+ {
+ "name": "ChillPenguin",
+ "file": "chillpenguin.vrm",
+ "number": "160",
+ "series": "R2",
+ "registryId": "aa2360e6-87ca-4f16-b5d1-8bae48000276",
+ "url": "https://arweave.net/4c5EhflLKYfBtzpcLlvZY0afAwXLVdxXmpgwGaarAIQ",
+ "sha256": "52b235d5a36c17798f6ce145d1d56965a17b3be81c143d0b301e7ae881660b14",
+ "thumbnail": "https://arweave.net/IIaNU4y7r4oOHrj3Zx_E-KZX7Ye9RHht9Q2K0i61BtQ",
+ "thumbnailSha256": "49f9cdb896c98912e52064826441f2288b49868590e35dabdc0890c9612392ee"
+ },
+ {
+ "name": "CoolPirate",
+ "file": "coolpirate.vrm",
+ "number": "145",
+ "series": "R2",
+ "registryId": "ecd9d092-f34f-4a29-9ebd-0b83f8266cf9",
+ "url": "https://arweave.net/lQwSPMpcSKzL9lMYyAaQ63vHK1ATP1Xmir43jZg2Ob0",
+ "sha256": "2193b911fe1e1bf3c38b0efb3d09d173aa1c69a1092b73ef4ee1b1e6c29119ea",
+ "thumbnail": "https://arweave.net/xohKEjCNVvTxscKi8PraA-yVanniHik9pMdETdjXNDI",
+ "thumbnailSha256": "f260558ab87bcc6d4214d35445a812daf1b5d7f6173fe4a39cdbf92adfdc27ba"
+ },
+ {
+ "name": "Erika",
+ "file": "erika.vrm",
+ "number": "053",
+ "series": "R1",
+ "registryId": "f6c50bbf-b41a-470e-84b8-bb3a12efc902",
+ "url": "https://arweave.net/GZkfa0SNnrBWluRL_pXpakg7T3K3d4l87__wR4mD3UM",
+ "sha256": "ac2030d23c791065e5cd596e251257269bc55b8c76b29a6ebf183bcfe8c7c1f7",
+ "thumbnail": "https://arweave.net/C3HkA5xa9jXxSySEJCZEhtrFkrCyccD4RR25QfXIpyE",
+ "thumbnailSha256": "cc37f4e9072c07a1a8343ff554679f6865ca3e5784040584642f24d0e31c19b3"
+ },
+ {
+ "name": "Kate",
+ "file": "kate.vrm",
+ "number": "038",
+ "series": "R1",
+ "registryId": "3d842fb6-29f5-494a-a696-d9ab10a0257d",
+ "url": "https://arweave.net/1q4IQwLQXJVS0JGSpeXlRdazmZYdwJbmLbTv7o0s5Y8",
+ "sha256": "d857bca56a26730b4f78872ffa322d11b6f427580e3224d1b1cd43368b50f9de",
+ "thumbnail": "https://arweave.net/ZRZbxFWEXmD7VkMDaGp8RTCrJgZE72kEAnnMqFrwS7I",
+ "thumbnailSha256": "81641a01615ab4873fa3e0128f3754561c3ea0664fbb1f7c4b9b8a434367e4f0"
+ },
+ {
+ "name": "Olivia",
+ "file": "olivia.vrm",
+ "number": "056",
+ "series": "R1",
+ "registryId": "4239c466-c3c1-4f97-ba56-a871f361ed84",
+ "url": "https://arweave.net/MgsNlTetzAoVEC6E-lswj65vp7StkOZXXd5OjjqzYZI",
+ "sha256": "d2597a8e0f6fbfc6c1e77886b737cea9cd9d2640474dc5cb5eafc847376a2473",
+ "thumbnail": "https://arweave.net/5wQlWAKsJjepBJacFSYjF72M0b5lbCOqWwDgoPZ8Hhw",
+ "thumbnailSha256": "4e61f525d7609c280cda16dd341112a034dce97c1bc1e140ade934070d8bfbf0"
+ },
+ {
+ "name": "Skelly",
+ "file": "skelly.vrm",
+ "number": "029",
+ "series": "R1",
+ "registryId": "ba0cc801-97f8-4c11-8916-144c560f8064",
+ "url": "https://arweave.net/3qGxPjmrwuHxuiT5v4LaR_PVoJ9XYLXLM_N7fdxiF_M",
+ "sha256": "3b789306ad0e22845a3668398f7d71c93fd8f99b0a2ee6116311270655dda06c",
+ "thumbnail": "https://arweave.net/L4roz__VkpKXMFpFdMpfqL7ME79-PW7-mysfDp-Ovfo",
+ "thumbnailSha256": "7b7c0a1ba5392b9bd043a26c82a49388ff6e014a6d78081250ddd36caa604171"
+ },
+ {
+ "name": "Witch",
+ "file": "witch.vrm",
+ "number": "039",
+ "series": "R1",
+ "registryId": "00df253c-4665-4cbf-b420-761a1cc4f9dd",
+ "url": "https://arweave.net/0YLwWzDkvVWn9ttv2RAdc8bvWcCBjvYPpu7fpjpHYU0",
+ "sha256": "33fa9a9c4c10d0ba0ab22a051809afddb9b53e7f51cbb69df91e1bf9e1653652",
+ "thumbnail": "https://arweave.net/ZxofC0CRXIB40r26MqyHK7tVAjoYs1a6SYBaxeqruwM",
+ "thumbnailSha256": "b99ce13bec623cf865ffc459a5829be4495607eaf63f72b40ed51dcae46e51a9"
+ }
+ ],
+ "quaternius": [
+ {
+ "name": "MannequinFemale",
+ "zip": "https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip",
+ "sha256": "ec0e40d6d78fe9aaad59e322f40865a8675c22f0745e291622e54520391a9217",
+ "entry": "Universal Animation Library 2 [Standard]/Female Mannequin/Unreal-Godot/Mannequin_F.glb",
+ "file": "mannequin-female.glb",
+ "stripAnimations": false
+ },
+ {
+ "name": "MannequinMale",
+ "zip": "https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip",
+ "sha256": "18ff1a7215f4852b320203e8aaf02a1578b5c8eef9027fbaedfcedc7b85a3ac2",
+ "entry": "Animation Library[Standard]/Godot/AnimationLibrary_Godot_Standard.glb",
+ "file": "mannequin-male.glb",
+ "stripAnimations": true
+ }
+ ]
+}
diff --git a/tools/kit-build/allow.json b/tools/kit-build/allow.json
index bed682b..4861f98 100644
--- a/tools/kit-build/allow.json
+++ b/tools/kit-build/allow.json
@@ -98,6 +98,12 @@
"file": "nature-kit/PondSet/glTF-Binary/PondSet.glb",
"reason": "render probe 404 px (> the 400 px recording limit) — the same 404 px 33-pack-fix-lod measured; none of defight's candidates flickers clearly less (best 403 px), and the roadmap-33 in-app scan passed it under the patch limit. The kitbash's reeds and pond edge, not a fixable coplanar layer; re-judge if the pond is rebuilt"
},
+ {
+ "pack": "characters-kit",
+ "item": "*",
+ "check": "lods",
+ "reason": "skinned characters (57 M5): a decimated level changes the skin weights and the rig, a VRM ships as its author made it, and core's runtime LOD skips skinned meshes"
+ },
{
"pack": "avatars",
"item": "*",
diff --git a/tools/kit-build/lib/rules.mjs b/tools/kit-build/lib/rules.mjs
index 6cc2ed2..2e9b6b2 100644
--- a/tools/kit-build/lib/rules.mjs
+++ b/tools/kit-build/lib/rules.mjs
@@ -46,7 +46,7 @@ export function itemRowProblems(row) {
if (row.screenshot !== undefined && !safeRel(row.screenshot) && !isUrl(row.screenshot)) out.push('screenshot must be a relative path or an https URL');
const glb = row.variants?.['glTF-Binary'];
if (typeof glb !== 'string' || !(safeRel(glb) || isUrl(glb))) out.push('variants["glTF-Binary"] must be a file name or an https URL');
- else if (!isUrl(glb) && !/\.glb$/i.test(glb)) out.push('variants["glTF-Binary"] must be a .glb');
+ else if (!isUrl(glb) && !/\.(glb|vrm)$/i.test(glb)) out.push('variants["glTF-Binary"] must be a .glb (or a .vrm — a GLB with the VRM extension)');
if (row.lods !== undefined) out.push(...lodsProblems(row.lods));
if (row.behavior !== undefined) out.push(...behaviorProblems(row.behavior));
out.push(...dimsShapeProblems(row));
diff --git a/tools/kit-build/packs.json b/tools/kit-build/packs.json
index abc3095..daf6a2b 100644
--- a/tools/kit-build/packs.json
+++ b/tools/kit-build/packs.json
@@ -34,6 +34,11 @@
"maxExtent": 3,
"about": "showcase props, few per scene"
},
+ "character": {
+ "tris": 15000,
+ "maxExtent": 3,
+ "about": "rigged humanoid characters (a VRM avatar, a mannequin): a handful per scene"
+ },
"set": {
"tris": 12000,
"maxExtent": 6,
@@ -386,6 +391,13 @@
"pivot": "any",
"about": "upstream-fetch: every row is an absolute URL into KhronosGroup/glTF-Sample-Assets, no bytes here"
},
+ "characters-kit": {
+ "textureCap": 2048,
+ "emissive": "allowed",
+ "category": "character",
+ "pivot": "bottom-center",
+ "about": "core roadmap 57 M5: CC0 VRM avatars (100Avatars, Polygonal Mind) + Quaternius mannequins (tools/characters-kit); VRM files ship as they are, so their MToon emissive stays"
+ },
"avatars": {
"textureCap": 256,
"emissive": "none",
diff --git a/tools/licence-lint/lint.mjs b/tools/licence-lint/lint.mjs
new file mode 100644
index 0000000..95873ca
--- /dev/null
+++ b/tools/licence-lint/lint.mjs
@@ -0,0 +1,138 @@
+// tools/licence-lint/lint.mjs — the PER-FILE licence lint (core roadmap 57 M5): a pack that ships a
+// `licences.json` must account for EVERY file it ships, and each file must say the same thing itself.
+//
+// node tools/licence-lint/lint.mjs [pack…] [--json out.json]
+//
+// Rules (CC0 only — the 57 packs take nothing else):
+// - every file under the pack (but the pack's own manifests / attribution) has a record
+// {licence, author, source} in licences.json, licence 'CC0-1.0', and its sha256 when recorded;
+// - a .vrm states CC0 in its OWN meta: VRM 0.x licenseName 'CC0' (commercial use allowed, no extra
+// licence URL); VRM 1.0 has no CC0 field, so it must grant everything CC0 does (redistribution,
+// commercial use by a corporation, modification + redistribution, no credit required);
+// - a .tpanim (57 M1 clip file) carries extras.theprototype {kind: 'clip'} and its provenance names
+// the same CC0 licence; a humanoid clip names its hips in VRMC_vrm_animation;
+// - the pack's index row (index.json or clips-index.json) says CC0-1.0.
+import fs from 'node:fs';
+import path from 'node:path';
+import crypto from 'node:crypto';
+import { fileURLToPath } from 'node:url';
+import { readGlb } from '../motion-kit/glbjson.mjs';
+
+const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
+const OWN = new Set(['licences.json', 'default.json', 'clips.json', 'attribution.html', 'kit.md']);
+const ALLOWED = ['CC0-1.0'];
+
+/** @param {string} dir @returns {string[]} */
+function walk(dir) {
+ /** @type {string[]} */
+ const out = [];
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
+ const p = path.join(dir, e.name);
+ if (e.isDirectory()) out.push(...walk(p));
+ else out.push(p);
+ }
+ return out;
+}
+
+/**
+ * A VRM's own licence statement -> problems ([] = CC0).
+ * @param {any} json @returns {string[]}
+ */
+export function vrmLicenceProblems(json) {
+ const v1 = json.extensions?.VRMC_vrm;
+ const v0 = json.extensions?.VRM;
+ if (!v1 && !v0) return ['a .vrm without the VRM extension'];
+ if (v0) {
+ const m = v0.meta ?? {};
+ const out = [];
+ if (m.licenseName !== 'CC0') out.push(`VRM 0.x licenseName is "${m.licenseName ?? ''}", not CC0`);
+ if (m.commercialUssageName && m.commercialUssageName !== 'Allow') out.push('VRM 0.x disallows commercial use');
+ if (m.otherLicenseUrl) out.push(`VRM 0.x names another licence (${m.otherLicenseUrl})`);
+ return out;
+ }
+ const m = v1.meta ?? {};
+ const out = [];
+ if (m.allowRedistribution !== true) out.push('VRM 1.0 does not allow redistribution');
+ if (m.commercialUsage !== 'corporation') out.push(`VRM 1.0 commercialUsage is "${m.commercialUsage}"`);
+ if (m.modification !== 'allowModificationRedistribution') out.push(`VRM 1.0 modification is "${m.modification}"`);
+ if (m.creditNotation && m.creditNotation !== 'unnecessary') out.push('VRM 1.0 requires credit');
+ if (m.otherLicenseUrl) out.push(`VRM 1.0 names another licence (${m.otherLicenseUrl})`);
+ return out;
+}
+
+/** a .tpanim's own statement -> problems @param {any} json @param {string} licence */
+export function clipLicenceProblems(json, licence) {
+ const meta = json.extras?.theprototype;
+ const out = [];
+ if (meta?.kind !== 'clip' || meta?.format !== 1) out.push('not a .tpanim (extras.theprototype {format: 1, kind: "clip"})');
+ const lic = (meta?.provenance ?? []).map((/** @type {any} */ p) => p?.licence?.id).filter(Boolean);
+ if (!lic.length) out.push('its provenance names no licence');
+ else if (lic.some((/** @type {string} */ l) => l !== licence)) out.push(`its provenance says ${lic.join(', ')}, the record ${licence}`);
+ if (meta?.skeleton?.humanoid && !json.extensions?.VRMC_vrm_animation?.humanoid?.humanBones?.hips) out.push('a humanoid clip without VRMC_vrm_animation hips');
+ return out;
+}
+
+/** @param {{packs?: string[], repo?: string}} [o] */
+export function lint(o = {}) {
+ const REPO = o.repo ?? ROOT;
+ /** @type {{level: 'error'|'warn', pack: string, file: string, msg: string}[]} */
+ const findings = [];
+ const add = (/** @type {'error'|'warn'} */ level, /** @type {string} */ pack, /** @type {string} */ file, /** @type {string} */ msg) => findings.push({ level, pack, file, msg });
+ const index = [...JSON.parse(fs.readFileSync(path.join(REPO, 'index.json'), 'utf8')), ...(fs.existsSync(path.join(REPO, 'clips-index.json')) ? JSON.parse(fs.readFileSync(path.join(REPO, 'clips-index.json'), 'utf8')) : [])];
+ const packs = (o.packs?.length ? o.packs : fs.readdirSync(REPO).filter((d) => fs.existsSync(path.join(REPO, d, 'licences.json'))));
+ for (const pack of packs) {
+ const base = path.join(REPO, pack);
+ const recFile = path.join(base, 'licences.json');
+ if (!fs.existsSync(recFile)) {
+ add('error', pack, '-', 'no licences.json');
+ continue;
+ }
+ const records = JSON.parse(fs.readFileSync(recFile, 'utf8'));
+ const row = index.find((r) => r.name === pack);
+ if (!row) add('error', pack, '-', 'not listed in index.json / clips-index.json');
+ else if (!ALLOWED.includes(row.license)) add('error', pack, '-', `the index says ${row.license || '(none)'} — these packs are CC0 only`);
+ const seen = new Set();
+ for (const file of walk(base)) {
+ const rel = path.relative(base, file).split(path.sep).join('/');
+ if (OWN.has(rel)) continue;
+ seen.add(rel);
+ const rec = records[rel];
+ if (!rec) {
+ add('error', pack, rel, 'no licence record in licences.json');
+ continue;
+ }
+ if (!ALLOWED.includes(rec.licence)) add('error', pack, rel, `licence ${rec.licence} is not CC0-1.0`);
+ if (!rec.author || !rec.source) add('error', pack, rel, 'the record names no author / source');
+ const bytes = fs.readFileSync(file);
+ if (rec.sha256 && crypto.createHash('sha256').update(bytes).digest('hex') !== rec.sha256) add('error', pack, rel, 'the file is not the one the record pinned (sha256)');
+ if (/\.(vrm|glb|tpanim)$/i.test(rel)) {
+ let json;
+ try {
+ json = readGlb(bytes).json;
+ } catch (e) {
+ add('error', pack, rel, 'not a GLB: ' + /** @type {Error} */ (e).message);
+ continue;
+ }
+ if (/\.vrm$/i.test(rel)) for (const m of vrmLicenceProblems(json)) add('error', pack, rel, m);
+ if (/\.tpanim$/i.test(rel)) for (const m of clipLicenceProblems(json, rec.licence)) add('error', pack, rel, m);
+ const copy = json.asset?.copyright;
+ if (/\.glb$/i.test(rel) && copy && !/cc0|public domain/i.test(copy)) add('warn', pack, rel, `asset.copyright says "${copy}"`);
+ }
+ }
+ for (const rel of Object.keys(records)) if (!seen.has(rel)) add('warn', pack, rel, 'a licence record for a file the pack does not ship');
+ }
+ return findings;
+}
+
+if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
+ const args = process.argv.slice(2);
+ const j = args.indexOf('--json');
+ const out = j >= 0 ? args[j + 1] : null;
+ const packs = args.filter((a, i) => !a.startsWith('--') && (j < 0 || i !== j + 1));
+ const findings = lint({ packs });
+ for (const f of findings) console.log(`${f.level.toUpperCase().padEnd(5)} ${f.pack}/${f.file}: ${f.msg}`);
+ const errors = findings.filter((f) => f.level === 'error').length;
+ console.log(`licence lint: ${errors} error(s), ${findings.length - errors} warning(s)`);
+ if (out) fs.writeFileSync(out, JSON.stringify(findings, null, 2));
+ process.exit(errors ? 1 : 0);
+}
diff --git a/tools/licence-lint/test/lint.test.mjs b/tools/licence-lint/test/lint.test.mjs
new file mode 100644
index 0000000..15a507e
--- /dev/null
+++ b/tools/licence-lint/test/lint.test.mjs
@@ -0,0 +1,68 @@
+// The licence lint, proven red on each kind of bad input (node --test).
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import crypto from 'node:crypto';
+import { lint, vrmLicenceProblems, clipLicenceProblems } from '../lint.mjs';
+import { writeGlb } from '../../motion-kit/glbjson.mjs';
+
+const vrm0 = (meta) => ({ asset: { version: '2.0' }, extensionsUsed: ['VRM'], extensions: { VRM: { meta } } });
+const vrm1 = (meta) => ({ asset: { version: '2.0' }, extensionsUsed: ['VRMC_vrm'], extensions: { VRMC_vrm: { specVersion: '1.0', meta } } });
+const clip = (licence, humanoid = true, hips = true) => ({
+ asset: { version: '2.0' },
+ extras: { theprototype: { format: 1, kind: 'clip', skeleton: { sig: 'x', humanoid }, provenance: [{ tool: 'import', licence: { id: licence } }] } },
+ ...(hips ? { extensions: { VRMC_vrm_animation: { humanoid: { humanBones: { hips: { node: 0 } } } } } } : {})
+});
+
+test('VRM 0.x: CC0 passes; prohibited / non-commercial / extra licence fail', () => {
+ assert.deepEqual(vrmLicenceProblems(vrm0({ licenseName: 'CC0', commercialUssageName: 'Allow', otherLicenseUrl: '' })), []);
+ assert.match(vrmLicenceProblems(vrm0({ licenseName: 'Redistribution_Prohibited' }))[0], /not CC0/);
+ assert.match(vrmLicenceProblems(vrm0({ licenseName: 'CC0', commercialUssageName: 'Disallow' })).join(), /commercial/);
+ assert.match(vrmLicenceProblems(vrm0({ licenseName: 'CC0', otherLicenseUrl: 'ipfs://x' })).join(), /another licence/);
+ assert.match(vrmLicenceProblems({ asset: {} })[0], /without the VRM extension/);
+});
+
+test('VRM 1.0: only a CC0-equivalent grant passes', () => {
+ const ok = { allowRedistribution: true, commercialUsage: 'corporation', modification: 'allowModificationRedistribution', creditNotation: 'unnecessary' };
+ assert.deepEqual(vrmLicenceProblems(vrm1(ok)), []);
+ assert.equal(vrmLicenceProblems(vrm1({ ...ok, allowRedistribution: false })).length, 1);
+ assert.equal(vrmLicenceProblems(vrm1({ ...ok, creditNotation: 'required' })).length, 1);
+ assert.equal(vrmLicenceProblems(vrm1({ ...ok, commercialUsage: 'personalNonProfit' })).length, 1);
+});
+
+test('.tpanim: CC0 provenance + hips pass; a CC-BY clip or a humanoid without hips fails', () => {
+ assert.deepEqual(clipLicenceProblems(clip('CC0-1.0'), 'CC0-1.0'), []);
+ assert.match(clipLicenceProblems(clip('CC-BY-4.0'), 'CC0-1.0').join(), /provenance says CC-BY/);
+ assert.match(clipLicenceProblems(clip('CC0-1.0', true, false), 'CC0-1.0').join(), /hips/);
+ assert.match(clipLicenceProblems({ asset: {} }, 'CC0-1.0').join(), /not a .tpanim/);
+});
+
+test('a pack: every file needs a CC0 record with its sha256; the index row must be CC0', () => {
+ const repo = fs.mkdtempSync(path.join(os.tmpdir(), 'lint-'));
+ const pack = path.join(repo, 'kit');
+ fs.mkdirSync(path.join(pack, 'A'), { recursive: true });
+ const good = writeGlb(vrm0({ licenseName: 'CC0', commercialUssageName: 'Allow' }), Buffer.alloc(0));
+ fs.writeFileSync(path.join(pack, 'A', 'a.vrm'), good);
+ fs.writeFileSync(path.join(pack, 'A', 'thumb.webp'), 'x');
+ const sha = crypto.createHash('sha256').update(good).digest('hex');
+ const rec = { 'A/a.vrm': { licence: 'CC0-1.0', author: 'Someone', source: 'https://x', sha256: sha }, 'A/thumb.webp': { licence: 'CC0-1.0', author: 'Someone', source: 'https://x' } };
+ fs.writeFileSync(path.join(pack, 'licences.json'), JSON.stringify(rec));
+ fs.writeFileSync(path.join(repo, 'index.json'), JSON.stringify([{ name: 'kit', license: 'CC0-1.0' }]));
+ assert.deepEqual(lint({ repo }).filter((f) => f.level === 'error'), []);
+ // a file with no record
+ fs.writeFileSync(path.join(pack, 'A', 'extra.png'), 'y');
+ assert.match(lint({ repo }).map((f) => f.msg).join(), /no licence record/);
+ fs.rmSync(path.join(pack, 'A', 'extra.png'));
+ // the file changed under its pin
+ fs.writeFileSync(path.join(pack, 'A', 'a.vrm'), writeGlb(vrm0({ licenseName: 'CC_BY' }), Buffer.alloc(0)));
+ const msgs = lint({ repo }).map((f) => f.msg).join();
+ assert.match(msgs, /sha256/);
+ assert.match(msgs, /not CC0/);
+ // the index row
+ fs.writeFileSync(path.join(pack, 'A', 'a.vrm'), good);
+ fs.writeFileSync(path.join(repo, 'index.json'), JSON.stringify([{ name: 'kit', license: 'CC-BY-4.0' }]));
+ assert.match(lint({ repo }).map((f) => f.msg).join(), /CC0 only/);
+ fs.rmSync(repo, { recursive: true, force: true });
+});
diff --git a/tools/motion-kit/build.mjs b/tools/motion-kit/build.mjs
new file mode 100644
index 0000000..c5c1ed3
--- /dev/null
+++ b/tools/motion-kit/build.mjs
@@ -0,0 +1,200 @@
+// tools/motion-kit/build.mjs — the motion-kit clip pack (core roadmap 57 M5): Quaternius' Universal
+// Animation Library 1 + 2, free STANDARD tiers (CC0 1.0), one humanoid `.tpanim` per clip.
+//
+// node tools/motion-kit/build.mjs [--src ] [--core ]
+//
+// A `.tpanim` (core 57 M1) is a glTF holding ONLY a skeleton and ONE animation. A humanoid clip is
+// valid VRM Animation inside (`VRMC_vrm_animation` names the human bones), so the same bytes renamed
+// `.vrma` play in VRM tools, and both carry `extras.theprototype = {format: 1, kind: 'clip', skeleton:
+// {sig, humanoid}, fps, frames, loop, rootMotion, provenance}`. Core retargets a humanoid clip onto any
+// mapped humanoid (VRM avatars, the KayKit avatars, Mixamo / Meshy / UniRig rigs).
+// The humanoid maps of the two rigs come from core's detector (`--core`, written to rigs.json); without
+// `--core` the committed rigs.json is used. `_Loop` clips loop; `_RM` clips keep their root motion.
+// Outputs: motion-kit//.tpanim, motion-kit/clips.json, motion-kit/licences.json.
+import fs from 'node:fs';
+import path from 'node:path';
+import os from 'node:os';
+import crypto from 'node:crypto';
+import { execFileSync } from 'node:child_process';
+import { pathToFileURL } from 'node:url';
+import { load, makeIO, REPO } from '../kit-build/lib/deps.mjs';
+import { readGlb, writeGlb } from './glbjson.mjs';
+
+const { prune } = await load('@gltf-transform/functions');
+
+const args = process.argv.slice(2);
+const opt = (/** @type {string} */ name) => {
+ const i = args.indexOf(name);
+ return i >= 0 ? args[i + 1] : undefined;
+};
+const SRC = opt('--src') || path.join(os.homedir(), '.cache/tp-characters-kit');
+const CORE = opt('--core');
+const OUT = path.join(REPO, 'motion-kit');
+const RIGS = path.join(REPO, 'tools/motion-kit/rigs.json');
+
+const LIBS = [
+ {
+ id: 'ual1',
+ title: 'Universal Animation Library 1 (Standard)',
+ zip: 'https://opengameart.org/sites/default/files/universal_animation_librarystandard.zip',
+ sha256: '18ff1a7215f4852b320203e8aaf02a1578b5c8eef9027fbaedfcedc7b85a3ac2',
+ entry: 'Animation Library[Standard]/Godot/AnimationLibrary_Godot_Standard.glb'
+ },
+ {
+ id: 'ual2',
+ title: 'Universal Animation Library 2 (Standard)',
+ zip: 'https://opengameart.org/sites/default/files/universal_animation_library_2standard.zip',
+ sha256: 'ec0e40d6d78fe9aaad59e322f40865a8675c22f0745e291622e54520391a9217',
+ entry: 'Universal Animation Library 2 [Standard]/Unreal-Godot/UAL2_Standard.glb'
+ }
+];
+/** a reference pose, not a motion */
+const SKIP = /^A_TPose$/;
+
+const sha = (/** @type {Buffer | Uint8Array} */ b) => crypto.createHash('sha256').update(b).digest('hex');
+
+async function fetchPinned(/** @type {string} */ url, /** @type {string} */ want) {
+ fs.mkdirSync(SRC, { recursive: true });
+ const file = path.join(SRC, path.basename(new URL(url).pathname));
+ if (!fs.existsSync(file) || sha(fs.readFileSync(file)) !== want) {
+ const res = await fetch(url);
+ if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`);
+ fs.writeFileSync(file, Buffer.from(await res.arrayBuffer()));
+ }
+ if (sha(fs.readFileSync(file)) !== want) throw new Error(`${file}: not the pinned sha256`);
+ const unpacked = file.replace(/\.zip$/, '');
+ if (!fs.existsSync(unpacked)) execFileSync('unzip', ['-o', '-q', file, '-d', unpacked]);
+ return unpacked;
+}
+
+/** the skin joints + their ancestors, by name, with parent links (core's detector input) @param {any} json */
+function boneList(json) {
+ const joints = new Set((json.skins ?? []).flatMap((/** @type {any} */ s) => s.joints));
+ const parent = new Map();
+ json.nodes.forEach((/** @type {any} */ n, /** @type {number} */ i) => (n.children ?? []).forEach((/** @type {number} */ c) => parent.set(c, i)));
+ const idx = [...joints];
+ const at = new Map(idx.map((n, k) => [n, k]));
+ return idx.map((n) => {
+ let p = parent.get(n);
+ while (p !== undefined && !at.has(p)) p = parent.get(p);
+ return { name: json.nodes[n].name, parent: p === undefined ? -1 : at.get(p) };
+ });
+}
+
+/** core's skeletonSignature over node rows in depth-first order @param {any} json */
+function skeletonSig(json) {
+ const parent = new Map();
+ json.nodes.forEach((/** @type {any} */ n, /** @type {number} */ i) => (n.children ?? []).forEach((/** @type {number} */ c) => parent.set(c, i)));
+ /** @type {number[]} */
+ const order = [];
+ const walk = (/** @type {number} */ i) => {
+ order.push(i);
+ for (const c of json.nodes[i].children ?? []) walk(c);
+ };
+ for (const r of json.scenes?.[json.scene ?? 0]?.nodes ?? []) walk(r);
+ const text = order.map((i) => json.nodes[i].name + '>' + (parent.has(i) ? json.nodes[parent.get(i)].name : '')).join('|');
+ let h = 0x811c9dc5;
+ for (let i = 0; i < text.length; i++) {
+ h ^= text.charCodeAt(i);
+ h = Math.imul(h, 0x01000193) >>> 0;
+ }
+ return h.toString(16).padStart(8, '0');
+}
+
+/** @type {Record>} */
+let rigs = fs.existsSync(RIGS) ? JSON.parse(fs.readFileSync(RIGS, 'utf8')).rigs ?? {} : {};
+const detect = CORE ? (await import(pathToFileURL(path.resolve(CORE, 'src/lib/humanoid/humanoidMap.js')).href)).detectHumanoidMap : null;
+
+const io = makeIO();
+/** @type {any[]} */
+const clips = [];
+/** @type {Record} */
+const licences = {};
+const date = '2026-10-23T00:00:00Z';
+fs.rmSync(OUT, { recursive: true, force: true });
+fs.mkdirSync(OUT, { recursive: true });
+
+for (const lib of LIBS) {
+ const dir = await fetchPinned(lib.zip, lib.sha256);
+ const src = fs.readFileSync(path.join(dir, lib.entry));
+ const srcJson = readGlb(src).json;
+ if (detect) {
+ const m = detect(boneList(srcJson));
+ if (!m.ok) throw new Error(`${lib.id}: not a full humanoid (missing ${m.missing.join(', ')})`);
+ rigs[lib.id] = m.map;
+ }
+ const map = rigs[lib.id];
+ if (!map) throw new Error(`${lib.id}: no humanoid map — run once with --core `);
+ const names = (srcJson.animations ?? []).map((/** @type {any} */ a) => a.name);
+ fs.mkdirSync(path.join(OUT, lib.id), { recursive: true });
+ for (const name of names) {
+ if (SKIP.test(name)) continue;
+ const doc = await io.readBinary(new Uint8Array(src));
+ const root = doc.getRoot();
+ for (const a of root.listAnimations()) {
+ if (a.getName() === name) continue;
+ // samplers + channels go; their accessors are left to prune (clips SHARE time accessors, so
+ // disposing one here would cut the kept clip's input)
+ for (const s of a.listSamplers()) s.dispose();
+ for (const c of a.listChannels()) c.dispose();
+ a.dispose();
+ }
+ // only the skeleton stays: meshes, skins, materials, textures go
+ for (const n of root.listNodes()) {
+ if (n.getMesh()) n.setMesh(null);
+ if (n.getSkin()) n.setSkin(null);
+ }
+ for (const s of root.listSkins()) s.dispose();
+ for (const m of root.listMeshes()) m.dispose();
+ // valid VRM Animation: rotation on every bone, translation on the hips only, no scale (three-vrm refuses a scale track)
+ for (const c of root.listAnimations()[0].listChannels()) {
+ const p = c.getTargetPath();
+ if (p === 'scale' || (p === 'translation' && c.getTargetNode()?.getName() !== map.hips)) {
+ c.getSampler()?.dispose();
+ c.dispose();
+ }
+ }
+ await doc.transform(prune({ keepLeaves: true }));
+ const anim = root.listAnimations()[0];
+ const input = anim.listSamplers()[0]?.getInput();
+ const times = input ? input.getArray() : new Float32Array([0]);
+ const duration = Math.max(...anim.listSamplers().map((s) => { const a = s.getInput()?.getArray(); return a ? a[a.length - 1] : 0; }));
+ const dt = times.length > 1 ? times[1] - times[0] : 1 / 30;
+ const fps = Math.max(1, Math.round(1 / dt));
+ const glb = readGlb(await io.writeBinary(doc));
+ const json = glb.json;
+ // drop mesh-only nodes the prune kept as leaves (the source's mesh holder)
+ const at = new Map(json.nodes.map((/** @type {any} */ n, /** @type {number} */ i) => [n.name, i]));
+ /** @type {Record} */
+ const humanBones = {};
+ for (const [bone, node] of Object.entries(map)) if (at.has(node)) humanBones[bone] = { node: at.get(node) };
+ const meta = {
+ format: 1,
+ kind: 'clip',
+ skeleton: { sig: skeletonSig(json), humanoid: true },
+ fps,
+ frames: Math.max(1, Math.round(duration * fps) + 1),
+ loop: /_Loop$/.test(name),
+ rootMotion: /_RM$/.test(name),
+ provenance: [
+ { tool: 'import', model: 'Quaternius ' + lib.title, licence: { id: 'CC0-1.0', author: 'Quaternius', url: 'https://quaternius.com' }, date, source: lib.zip },
+ { tool: 'convert', model: 'tools/motion-kit', date, source: name }
+ ]
+ };
+ json.asset = { ...(json.asset ?? {}), version: '2.0', generator: 'theprototype motion-kit' };
+ json.extras = { ...(json.extras ?? {}), theprototype: meta };
+ json.extensionsUsed = [...new Set([...(json.extensionsUsed ?? []), 'VRMC_vrm_animation'])];
+ json.extensions = { ...(json.extensions ?? {}), VRMC_vrm_animation: { specVersion: '1.0', humanoid: { humanBones } } };
+ if (json.animations?.[0]) json.animations[0].extras = { theprototype: meta };
+ const out = writeGlb(json, glb.bin);
+ const rel = `${lib.id}/${name}.tpanim`;
+ fs.writeFileSync(path.join(OUT, rel), out);
+ clips.push({ name, file: rel, lib: lib.id, duration: +duration.toFixed(4), fps, frames: meta.frames, loop: meta.loop, rootMotion: meta.rootMotion, humanoid: true, bytes: out.byteLength });
+ licences[rel] = { licence: 'CC0-1.0', author: 'Quaternius', title: `${name} (${lib.title})`, source: lib.zip, url: 'https://quaternius.com', sha256: sha(out) };
+ }
+}
+fs.writeFileSync(path.join(OUT, 'clips.json'), JSON.stringify(clips, null, '\t') + '\n');
+fs.writeFileSync(path.join(OUT, 'licences.json'), JSON.stringify(licences, null, '\t') + '\n');
+fs.writeFileSync(RIGS, JSON.stringify({ note: "humanoid maps (VRM 1.0 bone -> node name) of the source rigs, from core's detector (humanoid/humanoidMap.js)", rigs }, null, '\t') + '\n');
+const total = clips.reduce((n, c) => n + c.bytes, 0);
+console.log(`motion-kit: ${clips.length} clips, ${(total / 1048576).toFixed(1)} MiB`);
diff --git a/tools/motion-kit/glbjson.mjs b/tools/motion-kit/glbjson.mjs
new file mode 100644
index 0000000..34c9508
--- /dev/null
+++ b/tools/motion-kit/glbjson.mjs
@@ -0,0 +1,45 @@
+// The JSON + BIN chunks of a GLB, read and written as they are (no glTF library): the motion-kit build
+// adds what gltf-transform does not know (the VRMC_vrm_animation extension, root extras), and the
+// licence lint reads each file's own statements.
+
+/** @param {Uint8Array | Buffer} bytes @returns {{json: any, bin: Buffer}} */
+export function readGlb(bytes) {
+ const b = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ if (b.readUInt32LE(0) !== 0x46546c67) throw new Error('not a GLB');
+ let at = 12;
+ let json = null;
+ let bin = Buffer.alloc(0);
+ while (at + 8 <= b.length) {
+ const len = b.readUInt32LE(at);
+ const type = b.readUInt32LE(at + 4);
+ const body = b.subarray(at + 8, at + 8 + len);
+ if (type === 0x4e4f534a) json = JSON.parse(body.toString('utf8'));
+ else if (type === 0x004e4942) bin = Buffer.from(body);
+ at += 8 + len;
+ }
+ if (!json) throw new Error('GLB has no JSON chunk');
+ return { json, bin };
+}
+
+/** @param {any} json @param {Buffer} bin @returns {Buffer} */
+export function writeGlb(json, bin) {
+ let j = Buffer.from(JSON.stringify(json), 'utf8');
+ j = Buffer.concat([j, Buffer.alloc((4 - (j.length % 4)) % 4, 0x20)]);
+ const B = Buffer.concat([bin, Buffer.alloc((4 - (bin.length % 4)) % 4, 0)]);
+ const total = 12 + 8 + j.length + (B.length ? 8 + B.length : 0);
+ const head = Buffer.alloc(12);
+ head.writeUInt32LE(0x46546c67, 0);
+ head.writeUInt32LE(2, 4);
+ head.writeUInt32LE(total, 8);
+ const jh = Buffer.alloc(8);
+ jh.writeUInt32LE(j.length, 0);
+ jh.writeUInt32LE(0x4e4f534a, 4);
+ const parts = [head, jh, j];
+ if (B.length) {
+ const bh = Buffer.alloc(8);
+ bh.writeUInt32LE(B.length, 0);
+ bh.writeUInt32LE(0x004e4942, 4);
+ parts.push(bh, B);
+ }
+ return Buffer.concat(parts);
+}
diff --git a/tools/motion-kit/rigs.json b/tools/motion-kit/rigs.json
new file mode 100644
index 0000000..0734497
--- /dev/null
+++ b/tools/motion-kit/rigs.json
@@ -0,0 +1,113 @@
+{
+ "note": "humanoid maps (VRM 1.0 bone -> node name) of the source rigs, from core's detector (humanoid/humanoidMap.js)",
+ "rigs": {
+ "ual1": {
+ "hips": "DEF-hips",
+ "head": "DEF-head",
+ "neck": "DEF-neck",
+ "spine": "DEF-spine.001",
+ "chest": "DEF-spine.002",
+ "upperChest": "DEF-spine.003",
+ "leftUpperLeg": "DEF-thigh.L",
+ "leftLowerLeg": "DEF-shin.L",
+ "leftFoot": "DEF-foot.L",
+ "leftToes": "DEF-toe.L",
+ "leftUpperArm": "DEF-upper_arm.L",
+ "leftShoulder": "DEF-shoulder.L",
+ "leftLowerArm": "DEF-forearm.L",
+ "leftHand": "DEF-hand.L",
+ "leftThumbMetacarpal": "DEF-thumb.01.L",
+ "leftThumbProximal": "DEF-thumb.02.L",
+ "leftThumbDistal": "DEF-thumb.03.L",
+ "leftIndexProximal": "DEF-f_index.01.L",
+ "leftIndexIntermediate": "DEF-f_index.02.L",
+ "leftIndexDistal": "DEF-f_index.03.L",
+ "leftMiddleProximal": "DEF-f_middle.01.L",
+ "leftMiddleIntermediate": "DEF-f_middle.02.L",
+ "leftMiddleDistal": "DEF-f_middle.03.L",
+ "leftRingProximal": "DEF-f_ring.01.L",
+ "leftRingIntermediate": "DEF-f_ring.02.L",
+ "leftRingDistal": "DEF-f_ring.03.L",
+ "leftLittleProximal": "DEF-f_pinky.01.L",
+ "leftLittleIntermediate": "DEF-f_pinky.02.L",
+ "leftLittleDistal": "DEF-f_pinky.03.L",
+ "rightUpperLeg": "DEF-thigh.R",
+ "rightLowerLeg": "DEF-shin.R",
+ "rightFoot": "DEF-foot.R",
+ "rightToes": "DEF-toe.R",
+ "rightUpperArm": "DEF-upper_arm.R",
+ "rightShoulder": "DEF-shoulder.R",
+ "rightLowerArm": "DEF-forearm.R",
+ "rightHand": "DEF-hand.R",
+ "rightThumbMetacarpal": "DEF-thumb.01.R",
+ "rightThumbProximal": "DEF-thumb.02.R",
+ "rightThumbDistal": "DEF-thumb.03.R",
+ "rightIndexProximal": "DEF-f_index.01.R",
+ "rightIndexIntermediate": "DEF-f_index.02.R",
+ "rightIndexDistal": "DEF-f_index.03.R",
+ "rightMiddleProximal": "DEF-f_middle.01.R",
+ "rightMiddleIntermediate": "DEF-f_middle.02.R",
+ "rightMiddleDistal": "DEF-f_middle.03.R",
+ "rightRingProximal": "DEF-f_ring.01.R",
+ "rightRingIntermediate": "DEF-f_ring.02.R",
+ "rightRingDistal": "DEF-f_ring.03.R",
+ "rightLittleProximal": "DEF-f_pinky.01.R",
+ "rightLittleIntermediate": "DEF-f_pinky.02.R",
+ "rightLittleDistal": "DEF-f_pinky.03.R"
+ },
+ "ual2": {
+ "hips": "pelvis",
+ "head": "Head",
+ "neck": "neck_01",
+ "spine": "spine_01",
+ "chest": "spine_02",
+ "upperChest": "spine_03",
+ "leftUpperLeg": "thigh_l",
+ "leftLowerLeg": "calf_l",
+ "leftFoot": "foot_l",
+ "leftToes": "ball_l",
+ "leftUpperArm": "upperarm_l",
+ "leftShoulder": "clavicle_l",
+ "leftLowerArm": "lowerarm_l",
+ "leftHand": "hand_l",
+ "leftThumbMetacarpal": "thumb_01_l",
+ "leftThumbProximal": "thumb_02_l",
+ "leftThumbDistal": "thumb_03_l",
+ "leftIndexProximal": "index_01_l",
+ "leftIndexIntermediate": "index_02_l",
+ "leftIndexDistal": "index_03_l",
+ "leftMiddleProximal": "middle_01_l",
+ "leftMiddleIntermediate": "middle_02_l",
+ "leftMiddleDistal": "middle_03_l",
+ "leftRingProximal": "ring_01_l",
+ "leftRingIntermediate": "ring_02_l",
+ "leftRingDistal": "ring_03_l",
+ "leftLittleProximal": "pinky_01_l",
+ "leftLittleIntermediate": "pinky_02_l",
+ "leftLittleDistal": "pinky_03_l",
+ "rightUpperLeg": "thigh_r",
+ "rightLowerLeg": "calf_r",
+ "rightFoot": "foot_r",
+ "rightToes": "ball_r",
+ "rightUpperArm": "upperarm_r",
+ "rightShoulder": "clavicle_r",
+ "rightLowerArm": "lowerarm_r",
+ "rightHand": "hand_r",
+ "rightThumbMetacarpal": "thumb_01_r",
+ "rightThumbProximal": "thumb_02_r",
+ "rightThumbDistal": "thumb_03_r",
+ "rightIndexProximal": "index_01_r",
+ "rightIndexIntermediate": "index_02_r",
+ "rightIndexDistal": "index_03_r",
+ "rightMiddleProximal": "middle_01_r",
+ "rightMiddleIntermediate": "middle_02_r",
+ "rightMiddleDistal": "middle_03_r",
+ "rightRingProximal": "ring_01_r",
+ "rightRingIntermediate": "ring_02_r",
+ "rightRingDistal": "ring_03_r",
+ "rightLittleProximal": "pinky_01_r",
+ "rightLittleIntermediate": "pinky_02_r",
+ "rightLittleDistal": "pinky_03_r"
+ }
+ }
+}