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.

+ +

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:

+ +
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" + } + } +}