@genex-ai/cli-demo 0.11.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.js +203 -4
- package/package.json +7 -2
- package/templates/controllers/NOTICE.md +65 -0
- package/templates/controllers/assets/animation-library.glb +0 -0
- package/templates/controllers/assets/character.glb +0 -0
- package/templates/controllers/assets/default-avatar.vrm +0 -0
- package/templates/controllers/character/character-animations.ts +682 -0
- package/templates/controllers/character/character-controller.ts +1636 -0
- package/templates/controllers/character/follow-camera.ts +644 -0
- package/templates/controllers/character/keyboard-input.ts +277 -0
- package/templates/controllers/character/presets.ts +176 -0
- package/templates/controllers/character/touch-joystick.ts +387 -0
- package/templates/controllers/character/vrm/capsule-fit.ts +52 -0
- package/templates/controllers/character/vrm/foot-ik.ts +341 -0
- package/templates/controllers/character/vrm/vrm-loader.ts +44 -0
- package/templates/controllers/character/vrm/vrm-retarget.ts +195 -0
- package/templates/controllers/drone/drone-controller.ts +1073 -0
- package/templates/controllers/drone/presets.ts +225 -0
- package/templates/controllers/interact/enter-exit.ts +502 -0
- package/templates/controllers/shared/colliders.ts +456 -0
- package/templates/controllers/shared/math.ts +230 -0
- package/templates/controllers/shared/physics-world.ts +622 -0
- package/templates/controllers/vehicle/presets.ts +297 -0
- package/templates/controllers/vehicle/vehicle-controller.ts +615 -0
- package/templates/controllers/vehicle/wheel.ts +1200 -0
- package/templates/skills/genex-getting-started/SKILL.md +5 -0
- package/templates/skills/genex-threejs-character-controller/SKILL.md +205 -0
- package/templates/skills/genex-threejs-character-controller/references/animations.md +235 -0
- package/templates/skills/genex-threejs-character-controller/references/tuning-and-presets.md +102 -0
- package/templates/skills/genex-threejs-character-controller/references/wiring.md +198 -0
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +126 -54
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +17 -11
- package/templates/skills/genex-threejs-physics-rapier/SKILL.md +128 -0
- package/templates/skills/genex-threejs-physics-rapier/references/colliders-from-assets.md +202 -0
- package/templates/skills/genex-threejs-physics-rapier/references/physics-setup.md +207 -0
- package/templates/skills/genex-threejs-skill-router/SKILL.md +3 -0
- package/templates/skills/genex-threejs-skill-router/references/routing-map.md +15 -7
- package/templates/skills/genex-threejs-vehicle-controllers/SKILL.md +110 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/car.md +162 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/drone.md +150 -0
- package/templates/skills/genex-threejs-vehicle-controllers/references/enter-exit.md +199 -0
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ genex model "<prompt>" # generate a 3D model → assets/models/
|
|
|
11
11
|
genex skybox "<prompt>" # generate a 360° sky → assets/skybox/
|
|
12
12
|
genex sfx "<prompt>" # generate a sound fx → assets/sfx/
|
|
13
13
|
genex texture "<prompt>" # generate a texture → assets/textures/
|
|
14
|
+
genex controller <type> # install a tuned character|car|drone controller → src/controllers/
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
> **Invoking it.** First-time setup runs via `npx @genex-ai/cli-demo@latest init`.
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
|
-
import
|
|
5
|
+
import path13 from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/commands/init.ts
|
|
@@ -91,6 +91,7 @@ async function walk(rootSrc, src, dest, opts, result) {
|
|
|
91
91
|
const srcPath = path2.join(src, entry.name);
|
|
92
92
|
const destPath = path2.join(dest, entry.name);
|
|
93
93
|
const rel = path2.relative(rootSrc, srcPath);
|
|
94
|
+
if (opts.exclude?.includes(rel)) continue;
|
|
94
95
|
if (entry.isDirectory()) {
|
|
95
96
|
await fs2.mkdir(destPath, { recursive: true });
|
|
96
97
|
await walk(rootSrc, srcPath, destPath, opts, result);
|
|
@@ -740,7 +741,10 @@ async function runInit(opts) {
|
|
|
740
741
|
for (const t of targets) {
|
|
741
742
|
const src = t.full ? templatesDir : path7.join(templatesDir, "skills");
|
|
742
743
|
const dest = t.full ? t.baseDir : path7.join(t.baseDir, "skills");
|
|
743
|
-
const { copied, updated } = await copyTemplates(src, dest, {
|
|
744
|
+
const { copied, updated } = await copyTemplates(src, dest, {
|
|
745
|
+
force: opts.force,
|
|
746
|
+
exclude: ["controllers"]
|
|
747
|
+
});
|
|
744
748
|
const added = copied.length - updated.length;
|
|
745
749
|
totalNew += added;
|
|
746
750
|
totalUpdated += updated.length;
|
|
@@ -1450,13 +1454,202 @@ function printHint(kind, written, log) {
|
|
|
1450
1454
|
log.dim(` ${hint[kind]}`);
|
|
1451
1455
|
}
|
|
1452
1456
|
|
|
1457
|
+
// src/commands/controller.ts
|
|
1458
|
+
import fs10 from "fs/promises";
|
|
1459
|
+
import path12 from "path";
|
|
1460
|
+
var CONTROLLER_KINDS = ["character", "car", "drone"];
|
|
1461
|
+
var SHARED = [
|
|
1462
|
+
"shared/math.ts",
|
|
1463
|
+
"shared/physics-world.ts",
|
|
1464
|
+
"shared/colliders.ts"
|
|
1465
|
+
];
|
|
1466
|
+
var INPUT_AND_CAMERA = [
|
|
1467
|
+
"character/follow-camera.ts",
|
|
1468
|
+
"character/keyboard-input.ts",
|
|
1469
|
+
"character/touch-joystick.ts"
|
|
1470
|
+
];
|
|
1471
|
+
var NOTICE = "NOTICE.md";
|
|
1472
|
+
var CONTROLLER_FILE_SETS = {
|
|
1473
|
+
character: {
|
|
1474
|
+
code: [
|
|
1475
|
+
...SHARED,
|
|
1476
|
+
"character/character-controller.ts",
|
|
1477
|
+
"character/character-animations.ts",
|
|
1478
|
+
"character/presets.ts",
|
|
1479
|
+
// VRM avatar support (three-vrm): load + retarget the UAL clips + auto-fit
|
|
1480
|
+
// the capsule + optional foot IK. Owner's avatar replaces the old mannequin.
|
|
1481
|
+
"character/vrm/vrm-loader.ts",
|
|
1482
|
+
"character/vrm/vrm-retarget.ts",
|
|
1483
|
+
"character/vrm/capsule-fit.ts",
|
|
1484
|
+
"character/vrm/foot-ik.ts",
|
|
1485
|
+
...INPUT_AND_CAMERA,
|
|
1486
|
+
NOTICE
|
|
1487
|
+
],
|
|
1488
|
+
// The player's VRM is written to public/assets/avatar.vrm at install time by
|
|
1489
|
+
// installOwnerAvatar (owner's avatar, or the bundled default) — so it is NOT
|
|
1490
|
+
// a static manifest asset. animation-library.glb (46 clips) still is.
|
|
1491
|
+
assets: ["assets/animation-library.glb"],
|
|
1492
|
+
skill: "genex-threejs-character-controller",
|
|
1493
|
+
sketch: [
|
|
1494
|
+
`const physics = await PhysicsWorld.create();`,
|
|
1495
|
+
`const { scene, vrm } = await loadVrm("./assets/avatar.vrm");`,
|
|
1496
|
+
`const lib = await new GLTFLoader().loadAsync("./assets/animation-library.glb");`,
|
|
1497
|
+
`const character = new CharacterController(physics.world, camera, { ...characterPresets["default"].options, ...capsuleFromModel(scene), position: { x: 0, y: 2, z: 0 } });`,
|
|
1498
|
+
`character.root.add(scene); const anims = new CharacterAnimations(scene, retargetClips(vrm, lib.scene, lib.animations));`,
|
|
1499
|
+
`addEventListener("pointerdown", () => anims.playOneShot("Punch_Jab")); // per frame: anims.update(character, dt); vrm.update(dt);`
|
|
1500
|
+
]
|
|
1501
|
+
},
|
|
1502
|
+
car: {
|
|
1503
|
+
code: [
|
|
1504
|
+
...SHARED,
|
|
1505
|
+
"vehicle/vehicle-controller.ts",
|
|
1506
|
+
"vehicle/wheel.ts",
|
|
1507
|
+
"vehicle/presets.ts",
|
|
1508
|
+
"interact/enter-exit.ts",
|
|
1509
|
+
...INPUT_AND_CAMERA,
|
|
1510
|
+
NOTICE
|
|
1511
|
+
],
|
|
1512
|
+
assets: [],
|
|
1513
|
+
skill: "genex-threejs-vehicle-controllers",
|
|
1514
|
+
sketch: [
|
|
1515
|
+
`const physics = await PhysicsWorld.create(); // then physics.step(delta) every frame`,
|
|
1516
|
+
`const car = new VehicleController({ world: physics.world, position, carConfig: vehiclePresets["arcade-kart"].carConfig }); // + chassis colliders + car.addWheel(...) per preset slot`,
|
|
1517
|
+
`scene.add(car.chassisObject); physics.onBeforeStep(() => { car.setMovement(keyboard.getCarMovement()); car.update(); });`
|
|
1518
|
+
]
|
|
1519
|
+
},
|
|
1520
|
+
drone: {
|
|
1521
|
+
code: [
|
|
1522
|
+
...SHARED,
|
|
1523
|
+
"drone/drone-controller.ts",
|
|
1524
|
+
"drone/presets.ts",
|
|
1525
|
+
"interact/enter-exit.ts",
|
|
1526
|
+
...INPUT_AND_CAMERA,
|
|
1527
|
+
NOTICE
|
|
1528
|
+
],
|
|
1529
|
+
assets: [],
|
|
1530
|
+
skill: "genex-threejs-vehicle-controllers",
|
|
1531
|
+
sketch: [
|
|
1532
|
+
`const physics = await PhysicsWorld.create(); // then physics.step(delta) every frame`,
|
|
1533
|
+
`const drone = new DroneController({ world: physics.world, body, chassis, propellers, config: dronePresets["camera-drone"].config });`,
|
|
1534
|
+
`physics.onBeforeStep(() => { drone.setMovement(keyboard.getDroneMovement()); drone.update(); });`
|
|
1535
|
+
]
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
var CODE_DEST = path12.join("src", "controllers");
|
|
1539
|
+
var ASSETS_DEST = path12.join("public", "assets");
|
|
1540
|
+
async function runController(opts) {
|
|
1541
|
+
const log = createLogger({ quiet: opts.quiet });
|
|
1542
|
+
const kind = opts.kind?.trim();
|
|
1543
|
+
if (!kind || !CONTROLLER_KINDS.includes(kind)) {
|
|
1544
|
+
log.error(
|
|
1545
|
+
`Missing or unknown controller type${kind ? ` "${kind}"` : ""}. Usage: ${c.cyan(
|
|
1546
|
+
"genex controller <character|car|drone> [--force]"
|
|
1547
|
+
)}`
|
|
1548
|
+
);
|
|
1549
|
+
process.exitCode = 1;
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
const srcDir = path12.join(getTemplatesDir(), "controllers");
|
|
1553
|
+
const root = opts.cwd ?? process.cwd();
|
|
1554
|
+
const set = CONTROLLER_FILE_SETS[kind];
|
|
1555
|
+
log.plain(c.bold(`genex controller ${kind}`));
|
|
1556
|
+
log.plain("");
|
|
1557
|
+
log.step(`Installing the ${kind} controller into ${c.cyan(CODE_DEST + path12.sep)}`);
|
|
1558
|
+
const plan = [
|
|
1559
|
+
...set.code.map((rel) => ({ from: rel, rel: path12.join(CODE_DEST, rel) })),
|
|
1560
|
+
...set.assets.map((rel) => ({
|
|
1561
|
+
from: rel,
|
|
1562
|
+
rel: path12.join(ASSETS_DEST, path12.basename(rel))
|
|
1563
|
+
}))
|
|
1564
|
+
];
|
|
1565
|
+
let copied = 0;
|
|
1566
|
+
let skipped = 0;
|
|
1567
|
+
try {
|
|
1568
|
+
for (const file of plan) {
|
|
1569
|
+
const dest = path12.join(root, file.rel);
|
|
1570
|
+
if (!opts.force && await exists2(dest)) {
|
|
1571
|
+
skipped++;
|
|
1572
|
+
log.dim(` skipped ${file.rel} (exists \u2014 use --force to overwrite)`);
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
await fs10.mkdir(path12.dirname(dest), { recursive: true });
|
|
1576
|
+
await fs10.copyFile(path12.join(srcDir, file.from), dest);
|
|
1577
|
+
copied++;
|
|
1578
|
+
log.dim(` ${file.rel}`);
|
|
1579
|
+
}
|
|
1580
|
+
} catch (err) {
|
|
1581
|
+
log.error(`Copy failed: ${String(err)}`);
|
|
1582
|
+
process.exitCode = 1;
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
log.success(
|
|
1586
|
+
`Controller files ready (${copied} copied${skipped > 0 ? `, ${skipped} skipped` : ""}).`
|
|
1587
|
+
);
|
|
1588
|
+
log.plain("");
|
|
1589
|
+
if (kind === "character") {
|
|
1590
|
+
const token = opts.token !== void 0 ? opts.token : await readUserToken();
|
|
1591
|
+
await installOwnerAvatar({ root, srcDir, apiUrl: getApiUrl(opts.apiUrl), token, log });
|
|
1592
|
+
log.plain("");
|
|
1593
|
+
}
|
|
1594
|
+
log.plain(c.bold("Next steps"));
|
|
1595
|
+
log.plain(
|
|
1596
|
+
` 1. ${c.cyan(
|
|
1597
|
+
kind === "character" ? "npm i @dimforge/rapier3d-compat @pixiv/three-vrm" : "npm i @dimforge/rapier3d-compat"
|
|
1598
|
+
)} (three is already in the scaffold).`
|
|
1599
|
+
);
|
|
1600
|
+
log.plain(` 2. Load the ${c.cyan(set.skill)} skill for wiring, presets, and tuning.`);
|
|
1601
|
+
log.plain(" 3. Wiring sketch (controllers update BEFORE the physics step):");
|
|
1602
|
+
for (const line of set.sketch) {
|
|
1603
|
+
log.dim(` ${line}`);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
async function installOwnerAvatar(args) {
|
|
1607
|
+
const { root, srcDir, apiUrl, token, log } = args;
|
|
1608
|
+
const dest = path12.join(root, ASSETS_DEST, "avatar.vrm");
|
|
1609
|
+
await fs10.mkdir(path12.dirname(dest), { recursive: true });
|
|
1610
|
+
if (token) {
|
|
1611
|
+
try {
|
|
1612
|
+
const res = await fetch(`${apiUrl}/api/avatars/me`, {
|
|
1613
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1614
|
+
});
|
|
1615
|
+
if (res.ok) {
|
|
1616
|
+
const me = await res.json();
|
|
1617
|
+
if (me.vrmUrl) {
|
|
1618
|
+
const vrmRes = await fetch(me.vrmUrl);
|
|
1619
|
+
if (vrmRes.ok) {
|
|
1620
|
+
const buf = Buffer.from(await vrmRes.arrayBuffer());
|
|
1621
|
+
await fs10.writeFile(dest, buf);
|
|
1622
|
+
log.dim(` public/assets/avatar.vrm (your avatar \u2014 ${(buf.length / 1e6).toFixed(1)} MB)`);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
log.dim(" couldn't fetch your avatar; using the bundled default.");
|
|
1628
|
+
} catch {
|
|
1629
|
+
log.dim(" avatar fetch failed (offline?); using the bundled default.");
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
await fs10.copyFile(path12.join(srcDir, "assets", "default-avatar.vrm"), dest);
|
|
1633
|
+
log.dim(
|
|
1634
|
+
token ? " public/assets/avatar.vrm (bundled default)" : " public/assets/avatar.vrm (bundled default \u2014 sign in and re-run for your own)"
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
async function exists2(p) {
|
|
1638
|
+
try {
|
|
1639
|
+
await fs10.access(p);
|
|
1640
|
+
return true;
|
|
1641
|
+
} catch {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1453
1646
|
// src/index.ts
|
|
1454
1647
|
var GEN_KINDS = /* @__PURE__ */ new Set(["model", "skybox", "sfx", "texture"]);
|
|
1455
1648
|
function getVersion() {
|
|
1456
1649
|
try {
|
|
1457
|
-
const here =
|
|
1650
|
+
const here = path13.dirname(fileURLToPath2(import.meta.url));
|
|
1458
1651
|
const pkg = JSON.parse(
|
|
1459
|
-
readFileSync(
|
|
1652
|
+
readFileSync(path13.resolve(here, "..", "package.json"), "utf8")
|
|
1460
1653
|
);
|
|
1461
1654
|
return pkg.version ?? "0.0.0";
|
|
1462
1655
|
} catch {
|
|
@@ -1473,6 +1666,8 @@ ${c.bold("Usage")}
|
|
|
1473
1666
|
genex skybox "<prompt>" [options] Generate a skybox (equirect) into public/assets/skybox.
|
|
1474
1667
|
genex sfx "<prompt>" [options] Generate a sound effect (mp3) into public/assets/sfx.
|
|
1475
1668
|
genex texture "<prompt>" [options] Generate a PBR texture into public/assets/textures.
|
|
1669
|
+
genex controller <type> [--force] Install a physics controller (character|car|drone)
|
|
1670
|
+
into src/controllers (+ assets into public/assets).
|
|
1476
1671
|
|
|
1477
1672
|
${c.bold("Options for the generators (`model` `skybox` `sfx` `texture`)")}
|
|
1478
1673
|
--terrain (texture) seamless tiling surface for terrain/ground.
|
|
@@ -1527,6 +1722,7 @@ ${c.bold("Examples")}
|
|
|
1527
1722
|
genex skybox "golden hour over a misty mountain range"
|
|
1528
1723
|
genex sfx "punchy laser zap" --duration 2
|
|
1529
1724
|
genex texture "mossy cracked cobblestone" --terrain
|
|
1725
|
+
genex controller character
|
|
1530
1726
|
`;
|
|
1531
1727
|
function parseArgs(argv) {
|
|
1532
1728
|
const parsed = {
|
|
@@ -1696,6 +1892,9 @@ async function main() {
|
|
|
1696
1892
|
case "init":
|
|
1697
1893
|
await runInit(parsed.options);
|
|
1698
1894
|
break;
|
|
1895
|
+
case "controller":
|
|
1896
|
+
await runController({ ...parsed.options, kind: parsed.options.name });
|
|
1897
|
+
break;
|
|
1699
1898
|
case "preview":
|
|
1700
1899
|
await runPreview(parsed.options);
|
|
1701
1900
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Set up your ~/.claude workspace, authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,12 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"
|
|
39
|
+
"@dimforge/rapier3d-compat": "^0.19.3",
|
|
40
|
+
"@pixiv/three-vrm": "^3.5.4",
|
|
41
|
+
"@types/three": "^0.185.0",
|
|
42
|
+
"three": "^0.185.1",
|
|
43
|
+
"tsup": "^8.0.0",
|
|
44
|
+
"typescript": "^5.8.0"
|
|
40
45
|
},
|
|
41
46
|
"repository": {
|
|
42
47
|
"type": "git",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
The controller code and assets in this directory are vendored into your game
|
|
4
|
+
by `npx genex controller`. They bundle the following third-party work:
|
|
5
|
+
|
|
6
|
+
## ecctrl — MIT License
|
|
7
|
+
|
|
8
|
+
The character, vehicle, and drone controllers are a vanilla-TypeScript port of
|
|
9
|
+
the **ecctrl** character/vehicle controller library (a pmndrs project),
|
|
10
|
+
pinned at upstream commit `e2f4eb8`.
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2023-2026 Erdong Chen
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
|
|
32
|
+
Every ported source file carries its own SPDX header
|
|
33
|
+
(`SPDX-FileCopyrightText: 2023-2026 Erdong Chen`,
|
|
34
|
+
`SPDX-License-Identifier: MIT`).
|
|
35
|
+
|
|
36
|
+
## Quaternius Universal Animation Library — CC0 1.0 Universal
|
|
37
|
+
|
|
38
|
+
- `assets/animation-library.glb` — the Universal Animation Library by
|
|
39
|
+
**Quaternius** (quaternius.com): 46 humanoid animation clips on a
|
|
40
|
+
Blender-Rigify-style skeleton, as bundled by upstream ecctrl.
|
|
41
|
+
|
|
42
|
+
Dedicated to the public domain under the Creative Commons CC0 1.0 Universal
|
|
43
|
+
license (SPDX: `CC0-1.0`). No attribution is required by the license; this
|
|
44
|
+
notice is provided as a courtesy.
|
|
45
|
+
|
|
46
|
+
## Default avatar — CC0 1.0 Universal
|
|
47
|
+
|
|
48
|
+
The character controller plays as your VRM avatar. When you have not chosen one
|
|
49
|
+
(or run offline), the bundled default `assets/default-avatar.vrm` is copied to
|
|
50
|
+
`public/assets/avatar.vrm`. It is **Wizzir** from the **100 Avatars** project by
|
|
51
|
+
**Polygonal Mind** (polygonalmind.com), dedicated to the public domain under
|
|
52
|
+
CC0 1.0 Universal (SPDX: `CC0-1.0`). Attribution is a courtesy, not required.
|
|
53
|
+
|
|
54
|
+
## @pixiv/three-vrm — MIT License
|
|
55
|
+
|
|
56
|
+
The VRM support in `character/vrm/` (loader, animation retargeter, foot IK) is
|
|
57
|
+
built on the **@pixiv/three-vrm** npm package, MIT-licensed by pixiv Inc.
|
|
58
|
+
`vrm-retarget.ts` adapts that package's official humanoid-retarget example (MIT)
|
|
59
|
+
to the Quaternius rig. Install it into your game with `npm i @pixiv/three-vrm`.
|
|
60
|
+
|
|
61
|
+
Copyright (c) 2019-2026 pixiv Inc. — Permission is hereby granted, free of
|
|
62
|
+
charge, to any person obtaining a copy of this software and associated
|
|
63
|
+
documentation files, to deal in the Software without restriction. THE SOFTWARE
|
|
64
|
+
IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. (Full MIT text ships with the
|
|
65
|
+
`@pixiv/three-vrm` package's LICENSE file.)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|