@pieai/swimmer-avatar-kit 0.1.0

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +23 -0
  3. package/README.md +125 -0
  4. package/UPSTREAM.md +47 -0
  5. package/dist/avatar.d.ts +2 -0
  6. package/dist/avatar.js +69 -0
  7. package/dist/avatar.js.map +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.js +3 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/materials.d.ts +20 -0
  12. package/dist/materials.js +61 -0
  13. package/dist/materials.js.map +1 -0
  14. package/dist/react-three-fiber.d.ts +16 -0
  15. package/dist/react-three-fiber.js +52 -0
  16. package/dist/react-three-fiber.js.map +1 -0
  17. package/dist/recipe.d.ts +18 -0
  18. package/dist/recipe.js +118 -0
  19. package/dist/recipe.js.map +1 -0
  20. package/dist/types.d.ts +62 -0
  21. package/dist/types.js +2 -0
  22. package/dist/types.js.map +1 -0
  23. package/package.json +102 -0
  24. package/upstream-lock.json +38 -0
  25. package/vendor/kindergrimm/LICENSE +24 -0
  26. package/vendor/kindergrimm/src/gloss/catmullClark.js +129 -0
  27. package/vendor/kindergrimm/src/gloss/gface.d.ts +25 -0
  28. package/vendor/kindergrimm/src/gloss/gface.js +226 -0
  29. package/vendor/kindergrimm/src/gloss/gform.js +201 -0
  30. package/vendor/kindergrimm/src/gloss/ghair.js +534 -0
  31. package/vendor/kindergrimm/src/gloss/glayout.js +365 -0
  32. package/vendor/kindergrimm/src/gloss/gmedia.d.ts +28 -0
  33. package/vendor/kindergrimm/src/gloss/gmedia.js +470 -0
  34. package/vendor/kindergrimm/src/gloss/gpalette.d.ts +10 -0
  35. package/vendor/kindergrimm/src/gloss/gpalette.js +205 -0
  36. package/vendor/kindergrimm/src/gloss/gparts/blush.js +57 -0
  37. package/vendor/kindergrimm/src/gloss/gparts/body.js +47 -0
  38. package/vendor/kindergrimm/src/gloss/gparts/brows.js +55 -0
  39. package/vendor/kindergrimm/src/gloss/gparts/crest.js +88 -0
  40. package/vendor/kindergrimm/src/gloss/gparts/eyes.js +357 -0
  41. package/vendor/kindergrimm/src/gloss/gparts/frame.js +225 -0
  42. package/vendor/kindergrimm/src/gloss/gparts/hair.js +100 -0
  43. package/vendor/kindergrimm/src/gloss/gparts/hat.js +163 -0
  44. package/vendor/kindergrimm/src/gloss/gparts/index.js +33 -0
  45. package/vendor/kindergrimm/src/gloss/gparts/mark.js +139 -0
  46. package/vendor/kindergrimm/src/gloss/gparts/mouth.js +170 -0
  47. package/vendor/kindergrimm/src/gloss/gparts/nose.js +63 -0
  48. package/vendor/kindergrimm/src/gloss/gparts/specs.js +124 -0
  49. package/vendor/kindergrimm/src/gloss/grig.d.ts +41 -0
  50. package/vendor/kindergrimm/src/gloss/grig.js +286 -0
  51. package/vendor/kindergrimm/src/gloss/gshape.js +421 -0
  52. package/vendor/kindergrimm/src/gloss/gspecies.d.ts +10 -0
  53. package/vendor/kindergrimm/src/gloss/gspecies.js +338 -0
  54. package/vendor/kindergrimm/src/gloss/gtexture.js +406 -0
  55. package/vendor/kindergrimm/src/rng.d.ts +11 -0
  56. package/vendor/kindergrimm/src/rng.js +27 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PieAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,23 @@
1
+ # Third-Party Notice
2
+
3
+ SwimmerAvatarKit includes an unmodified dependency closure from
4
+ [kindergrimm](https://github.com/albertobeiz/kindergrimm):
5
+
6
+ - Source repository: `https://github.com/albertobeiz/kindergrimm.git`
7
+ - Pinned commit: `811214c6dd5de18cc20335cd3d4ab0a06e45ffd4`
8
+ - Commit date: 2026-08-21
9
+ - Imported: 2026-08-22
10
+ - Upstream license: Unlicense (public domain)
11
+
12
+ The imported files live under `vendor/kindergrimm/`. The original license is
13
+ preserved at `vendor/kindergrimm/LICENSE`. It expressly permits copying,
14
+ modification, publication, commercial use, sale, and distribution.
15
+
16
+ `upstream-lock.json` records every imported file's Git blob hash. The two
17
+ upstream browser demo entry points, `src/gloss/gloss.js` and
18
+ `src/gloss/gcrowd.js`, are intentionally excluded; they are applications, not
19
+ part of the reusable generator dependency closure.
20
+
21
+ PieAI's TypeScript adapters, tests, automation, and documentation are licensed
22
+ under the root MIT license. The imported Kindergrimm files remain under the
23
+ Unlicense.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # SwimmerAvatarKit
2
+
3
+ SwimmerAvatarKit is PieAI's shared procedural 3D avatar package. It turns a
4
+ small, reproducible JSON recipe into a glossy chibi Three.js character and
5
+ provides an optional React Three Fiber component.
6
+
7
+ The GLOSS art generator is vendored unchanged from Kindergrimm under the
8
+ Unlicense. SwimmerAvatarKit owns the stable TypeScript API, recipe persistence,
9
+ resource lifecycle, tests, and release process around it.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @pieai/swimmer-avatar-kit three
15
+ ```
16
+
17
+ React Three Fiber consumers also install compatible React and R3F versions:
18
+
19
+ ```bash
20
+ pnpm add react @react-three/fiber
21
+ ```
22
+
23
+ ## React Three Fiber
24
+
25
+ ```tsx
26
+ import { Canvas } from "@react-three/fiber";
27
+ import { randomRecipe } from "@pieai/swimmer-avatar-kit";
28
+ import { Avatar } from "@pieai/swimmer-avatar-kit/react-three-fiber";
29
+
30
+ const recipe = randomRecipe("user-42");
31
+
32
+ export function ProfileAvatar() {
33
+ return (
34
+ <Canvas camera={{ position: [0, 1, 5] }}>
35
+ <ambientLight intensity={1.2} />
36
+ <Avatar recipe={recipe} gaze scale={0.8} position={[0, -1, 0]} />
37
+ </Canvas>
38
+ );
39
+ }
40
+ ```
41
+
42
+ `Avatar` acquires a renderer-scoped material-library lease by default. Many
43
+ avatars on one canvas therefore share materials and one studio environment;
44
+ the last unmount releases them.
45
+
46
+ ## Framework-neutral Three.js
47
+
48
+ ```ts
49
+ import {
50
+ buildAvatar,
51
+ randomRecipe,
52
+ } from "@pieai/swimmer-avatar-kit";
53
+ import {
54
+ createStudioMaterialLibrary,
55
+ } from "@pieai/swimmer-avatar-kit/materials";
56
+
57
+ const materials = createStudioMaterialLibrary(renderer);
58
+ const avatar = buildAvatar(randomRecipe("user-42"), {
59
+ materialFor: materials.materialFor,
60
+ });
61
+
62
+ scene.add(avatar.object);
63
+
64
+ function frame(deltaSeconds: number) {
65
+ avatar.update(deltaSeconds);
66
+ renderer.render(scene, camera);
67
+ }
68
+
69
+ // Avatar geometry and shared materials have separate owners.
70
+ avatar.dispose();
71
+ materials.dispose();
72
+ ```
73
+
74
+ Never dispose mesh materials while another avatar can still share them.
75
+ `avatar.dispose()` intentionally disposes geometry only. The material library
76
+ owns and disposes its cache.
77
+
78
+ ## Reproducible and persistable recipes
79
+
80
+ ```ts
81
+ import {
82
+ deserializeAvatarRecipe,
83
+ randomRecipe,
84
+ serializeAvatarRecipe,
85
+ } from "@pieai/swimmer-avatar-kit";
86
+
87
+ const recipe = randomRecipe("account:42");
88
+ const stored = serializeAvatarRecipe(recipe);
89
+ const restored = deserializeAvatarRecipe(stored);
90
+ ```
91
+
92
+ The serialized form uses a versioned SwimmerAvatarKit envelope. Where that
93
+ string is stored is a product decision: v0.1 supports local or product-owned
94
+ storage and does not couple avatars to SwimmerBackend or declare a cross-product
95
+ identity policy.
96
+
97
+ ## Entry points
98
+
99
+ | Entry point | Purpose | Runtime |
100
+ | --- | --- | --- |
101
+ | `@pieai/swimmer-avatar-kit` | Recipes, metadata, build/update/dispose | Three.js |
102
+ | `@pieai/swimmer-avatar-kit/materials` | Gloss materials and studio environment | Browser/WebGL |
103
+ | `@pieai/swimmer-avatar-kit/react-three-fiber` | `<Avatar />` adapter | React + R3F |
104
+
105
+ ## Current boundary
106
+
107
+ v0.1 supports GLOSS avatars with autonomous blinking, gaze, expressions, and
108
+ breathing. It does not yet promise walking/running poses. Kindergrimm's DRAWN
109
+ poses target a different rig, while the GLOSS biped is currently a static set
110
+ of meshes rather than an articulated locomotion skeleton. That work requires a
111
+ separate rig/retargeting spike before OwnMySpace should depend on it.
112
+
113
+ DRAWN, VOXEL, and OBJECT are also outside this package version. In particular,
114
+ procedural OBJECT plants may become another shared capability, but they should
115
+ not be hidden inside the avatar API.
116
+
117
+ ## Upstream and release
118
+
119
+ - Provenance: [NOTICE.md](NOTICE.md)
120
+ - Update process: [UPSTREAM.md](UPSTREAM.md)
121
+ - Local gate: `pnpm verify`
122
+ - Network update check: `pnpm upstream:check`
123
+
124
+ npm publication is intentionally absent from local scripts. Releases run only
125
+ through the manually triggered Trusted Publishing workflow on `main`.
package/UPSTREAM.md ADDED
@@ -0,0 +1,47 @@
1
+ # Kindergrimm Upstream Maintenance
2
+
3
+ SwimmerAvatarKit owns the stable product contract; Kindergrimm remains the art
4
+ generator upstream. An upstream change is never merged automatically.
5
+
6
+ ## Check for updates
7
+
8
+ ```bash
9
+ pnpm upstream:check
10
+ ```
11
+
12
+ The command compares the pinned commit in `upstream-lock.json` with the current
13
+ tip of Kindergrimm's `main` branch. It exits successfully when they match and
14
+ fails with both commit IDs when review is needed. The scheduled GitHub workflow
15
+ runs the same read-only check weekly.
16
+
17
+ ## Review and import an update
18
+
19
+ 1. Clone Kindergrimm into a temporary directory and check out the candidate
20
+ commit. Do not add it as a mutable Git submodule.
21
+ 2. Review the full range from the pinned commit to the candidate, especially
22
+ `src/gloss/**`, `src/rng.js`, recipe shape, material callback arguments,
23
+ returned rig fields, and browser-only API use.
24
+ 3. Replace only the imported dependency closure under
25
+ `vendor/kindergrimm/`. Keep `src/gloss/gloss.js` and
26
+ `src/gloss/gcrowd.js` excluded unless the package contract deliberately
27
+ changes.
28
+ 4. Do not repair compatibility inside vendored JavaScript. Adapt the typed
29
+ boundary under `src/**` and add regression tests.
30
+ 5. Update the commit metadata and Git blob hashes in `upstream-lock.json`, then
31
+ run `pnpm vendor:verify` before any other test.
32
+ 6. Run `pnpm verify`, `pnpm docs:check`, and inspect `pnpm pack --dry-run`.
33
+ 7. Choose semver from the public package impact, not from the upstream commit
34
+ label. A compatible art/bug refresh is normally a patch; changed recipes or
35
+ APIs may require a minor or major release.
36
+ 8. Merge the reviewed package change, then publish only through the trusted
37
+ GitHub Actions workflow. Products upgrade to that released version in their
38
+ own PRs.
39
+
40
+ This creates a deliberate two-step buffer:
41
+
42
+ ```text
43
+ Kindergrimm update -> reviewed SwimmerAvatarKit release -> product dependency update
44
+ ```
45
+
46
+ An upstream bug fix can therefore be adopted quickly without giving an
47
+ unreviewed external commit authority over every PieAI product.
@@ -0,0 +1,2 @@
1
+ import type { AvatarBuildOptions, AvatarHandle, AvatarRecipe } from "./types.js";
2
+ export declare function buildAvatar(recipe: AvatarRecipe, options: AvatarBuildOptions): AvatarHandle;
package/dist/avatar.js ADDED
@@ -0,0 +1,69 @@
1
+ import { createGlossFace } from "../vendor/kindergrimm/src/gloss/gface.js";
2
+ import { buildGloss } from "../vendor/kindergrimm/src/gloss/grig.js";
3
+ import { completeRecipe, EXPRESSIONS } from "./recipe.js";
4
+ const expressionIds = new Set(EXPRESSIONS);
5
+ function disposeGeometries(root) {
6
+ const geometries = new Set();
7
+ root.traverse((child) => {
8
+ const mesh = child;
9
+ if (mesh.isMesh)
10
+ geometries.add(mesh.geometry);
11
+ });
12
+ for (const geometry of geometries)
13
+ geometry.dispose();
14
+ }
15
+ export function buildAvatar(recipe, options) {
16
+ if (typeof options?.materialFor !== "function") {
17
+ throw new TypeError("buildAvatar requires a materialFor function.");
18
+ }
19
+ const completedRecipe = completeRecipe(recipe);
20
+ const built = buildGloss(completedRecipe, {
21
+ materialFor: options.materialFor,
22
+ });
23
+ const life = createGlossFace(built, { gaze: options.gaze ?? true });
24
+ const restY = Number(built.head.userData.restY ?? built.L.cy);
25
+ let elapsedSeconds = 0;
26
+ let disposed = false;
27
+ return {
28
+ object: built.group,
29
+ recipe: completedRecipe,
30
+ stats: { ...built.stats },
31
+ bounds: { ...built.bounds },
32
+ update(deltaSeconds) {
33
+ if (disposed)
34
+ return;
35
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
36
+ throw new RangeError("Avatar deltaSeconds must be a finite non-negative number.");
37
+ }
38
+ elapsedSeconds += deltaSeconds;
39
+ const head = life.update(elapsedSeconds, deltaSeconds);
40
+ const breath = 1 + Math.sin(elapsedSeconds * 1.8) * 0.007;
41
+ built.group.scale.set(1 / Math.sqrt(breath), breath, 1 / Math.sqrt(breath));
42
+ built.head.position.set(head.x, restY + head.y, 0);
43
+ built.head.rotation.set(head.pitch, head.yaw, head.rot);
44
+ },
45
+ getExpression() {
46
+ return life.face();
47
+ },
48
+ setExpression(expression) {
49
+ if (disposed)
50
+ return;
51
+ if (!expressionIds.has(expression)) {
52
+ throw new RangeError(`Unknown avatar expression: ${expression}`);
53
+ }
54
+ life.setFace(expression);
55
+ },
56
+ isDisposed() {
57
+ return disposed;
58
+ },
59
+ dispose() {
60
+ if (disposed)
61
+ return;
62
+ disposed = true;
63
+ built.group.removeFromParent();
64
+ disposeGeometries(built.group);
65
+ built.group.clear();
66
+ },
67
+ };
68
+ }
69
+ //# sourceMappingURL=avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.js","sourceRoot":"","sources":["../src/avatar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { buildAvatar } from "./avatar.js";
2
+ export { assertAvatarRecipe, AVATAR_RECIPE_SCHEMA, AVATAR_RECIPE_VERSION, cloneAvatarRecipe, completeRecipe, createAvatarRecipe, createAvatarRecipeDocument, deserializeAvatarRecipe, EXPRESSIONS, fillRecipe, PALETTES, parseAvatarRecipeDocument, PARTS, randomRecipe, rerollPart, serializeAvatarRecipe, SPECIES, } from "./recipe.js";
3
+ export type { AvatarBounds, AvatarBuildOptions, AvatarBuildStats, AvatarExpression, AvatarHandle, AvatarMaterialFor, AvatarOption, AvatarPaletteOption, AvatarPartRecipe, AvatarRecipe, AvatarRecipeDocument, AvatarRecipeDocumentV1, AvatarSeed, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { buildAvatar } from "./avatar.js";
2
+ export { assertAvatarRecipe, AVATAR_RECIPE_SCHEMA, AVATAR_RECIPE_VERSION, cloneAvatarRecipe, completeRecipe, createAvatarRecipe, createAvatarRecipeDocument, deserializeAvatarRecipe, EXPRESSIONS, fillRecipe, PALETTES, parseAvatarRecipeDocument, PARTS, randomRecipe, rerollPart, serializeAvatarRecipe, SPECIES, } from "./recipe.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,yBAAyB,EACzB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,OAAO,GACR,MAAM,aAAa,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Texture, WebGLRenderer } from "three";
2
+ import type { AvatarMaterialFor, AvatarOption } from "./types.js";
3
+ export interface AvatarMaterialLibrary {
4
+ readonly environment: Texture;
5
+ readonly materialFor: AvatarMaterialFor;
6
+ readonly disposed: boolean;
7
+ dispose(): void;
8
+ }
9
+ export interface AvatarMaterialLibraryOptions {
10
+ disposeEnvironment?: boolean;
11
+ }
12
+ export interface AvatarMaterialLibraryLease {
13
+ readonly library: AvatarMaterialLibrary;
14
+ readonly released: boolean;
15
+ release(): void;
16
+ }
17
+ export declare const FINISHES: readonly AvatarOption[];
18
+ export declare function createGlossMaterialLibrary(environment: Texture, options?: AvatarMaterialLibraryOptions): AvatarMaterialLibrary;
19
+ export declare function createStudioMaterialLibrary(renderer: WebGLRenderer): AvatarMaterialLibrary;
20
+ export declare function acquireStudioMaterialLibrary(renderer: WebGLRenderer): AvatarMaterialLibraryLease;
@@ -0,0 +1,61 @@
1
+ import { makeMaterialFactory, MATERIALS, studioEnv, } from "../vendor/kindergrimm/src/gloss/gmedia.js";
2
+ export const FINISHES = Object.freeze(MATERIALS.map((material) => ({ id: material.id, label: material.label })));
3
+ export function createGlossMaterialLibrary(environment, options = {}) {
4
+ const materialForUpstream = makeMaterialFactory(environment);
5
+ const materials = new Set();
6
+ let disposed = false;
7
+ const materialFor = (finish, color, shell, print) => {
8
+ if (disposed)
9
+ throw new Error("Avatar material library has been disposed.");
10
+ const material = materialForUpstream(finish, color, shell, print);
11
+ materials.add(material);
12
+ return material;
13
+ };
14
+ return {
15
+ environment,
16
+ materialFor,
17
+ get disposed() {
18
+ return disposed;
19
+ },
20
+ dispose() {
21
+ if (disposed)
22
+ return;
23
+ disposed = true;
24
+ for (const material of materials)
25
+ material.dispose();
26
+ materials.clear();
27
+ if (options.disposeEnvironment === true)
28
+ environment.dispose();
29
+ },
30
+ };
31
+ }
32
+ export function createStudioMaterialLibrary(renderer) {
33
+ return createGlossMaterialLibrary(studioEnv(renderer), { disposeEnvironment: true });
34
+ }
35
+ const rendererPool = new WeakMap();
36
+ export function acquireStudioMaterialLibrary(renderer) {
37
+ let entry = rendererPool.get(renderer);
38
+ if (!entry || entry.library.disposed) {
39
+ entry = { library: createStudioMaterialLibrary(renderer), references: 0 };
40
+ rendererPool.set(renderer, entry);
41
+ }
42
+ entry.references += 1;
43
+ let released = false;
44
+ return {
45
+ library: entry.library,
46
+ get released() {
47
+ return released;
48
+ },
49
+ release() {
50
+ if (released)
51
+ return;
52
+ released = true;
53
+ entry.references -= 1;
54
+ if (entry.references === 0) {
55
+ entry.library.dispose();
56
+ rendererPool.delete(renderer);
57
+ }
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=materials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materials.js","sourceRoot":"","sources":["../src/materials.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,SAAS,GACV,MAAM,2CAA2C,CAAC;AAqBnD,MAAM,CAAC,MAAM,QAAQ,GAA4B,MAAM,CAAC,MAAM,CAC5D,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAC1E,CAAC;AAEF,MAAM,UAAU,0BAA0B,CACxC,WAAoB,EACpB,UAAwC,EAAE;IAE1C,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAC;IACtC,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,WAAW,GAAsB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACrE,IAAI,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAClE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO;QACL,WAAW;QACX,WAAW;QACX,IAAI,QAAQ;YACV,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,MAAM,QAAQ,IAAI,SAAS;gBAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrD,SAAS,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI;gBAAE,WAAW,CAAC,OAAO,EAAE,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAuB;IACjE,OAAO,0BAA0B,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AAOD,MAAM,YAAY,GAAG,IAAI,OAAO,EAAoC,CAAC;AAErE,MAAM,UAAU,4BAA4B,CAAC,QAAuB;IAClE,IAAI,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,KAAK,GAAG,EAAE,OAAO,EAAE,2BAA2B,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QAC1E,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;IACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,QAAQ;YACV,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;YACtB,IAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;gBAC3B,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxB,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type ThreeElements } from "@react-three/fiber";
2
+ import type { Group } from "three";
3
+ import { type AvatarMaterialLibrary } from "./materials.js";
4
+ import type { AvatarHandle, AvatarRecipe } from "./types.js";
5
+ export type AvatarProps = Omit<ThreeElements["group"], "children" | "ref"> & {
6
+ recipe: AvatarRecipe;
7
+ gaze?: boolean;
8
+ materialLibrary?: AvatarMaterialLibrary;
9
+ onBuilt?: (avatar: AvatarHandle) => void;
10
+ };
11
+ export declare const Avatar: import("react").ForwardRefExoticComponent<Omit<import("@react-three/fiber/dist/declarations/src/core/utils.js").Mutable<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Partial<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Group<import("three").Object3DEventMap>, import("@react-three/fiber").MathProps<Group<import("three").Object3DEventMap>> & import("@react-three/fiber").ReactProps<Group<import("three").Object3DEventMap>> & Partial<import("@react-three/fiber").EventHandlers>>>, Omit<import("@react-three/fiber").InstanceProps<Group<import("three").Object3DEventMap>, typeof Group>, "object">>>, "children" | "ref"> & {
12
+ recipe: AvatarRecipe;
13
+ gaze?: boolean;
14
+ materialLibrary?: AvatarMaterialLibrary;
15
+ onBuilt?: (avatar: AvatarHandle) => void;
16
+ } & import("react").RefAttributes<AvatarHandle>>;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
3
+ import { useFrame, useThree } from "@react-three/fiber";
4
+ import { buildAvatar } from "./avatar.js";
5
+ import { acquireStudioMaterialLibrary } from "./materials.js";
6
+ export const Avatar = forwardRef(function Avatar({ recipe, gaze = true, materialLibrary, onBuilt, ...groupProps }, forwardedRef) {
7
+ const { gl } = useThree();
8
+ const [ownedLibrary, setOwnedLibrary] = useState(null);
9
+ const [avatar, setAvatar] = useState(null);
10
+ const [object, setObject] = useState(null);
11
+ const avatarRef = useRef(null);
12
+ const onBuiltRef = useRef(onBuilt);
13
+ onBuiltRef.current = onBuilt;
14
+ useEffect(() => {
15
+ if (materialLibrary) {
16
+ setOwnedLibrary(null);
17
+ return;
18
+ }
19
+ const lease = acquireStudioMaterialLibrary(gl);
20
+ setOwnedLibrary(lease.library);
21
+ return () => {
22
+ setOwnedLibrary(null);
23
+ lease.release();
24
+ };
25
+ }, [gl, materialLibrary]);
26
+ const activeLibrary = materialLibrary ?? ownedLibrary;
27
+ useLayoutEffect(() => {
28
+ if (!activeLibrary)
29
+ return;
30
+ const next = buildAvatar(recipe, {
31
+ gaze,
32
+ materialFor: activeLibrary.materialFor,
33
+ });
34
+ avatarRef.current = next;
35
+ setAvatar(next);
36
+ setObject(next.object);
37
+ onBuiltRef.current?.(next);
38
+ return () => {
39
+ if (avatarRef.current === next)
40
+ avatarRef.current = null;
41
+ setAvatar(null);
42
+ setObject(null);
43
+ next.dispose();
44
+ };
45
+ }, [activeLibrary, gaze, recipe]);
46
+ useFrame((_, deltaSeconds) => {
47
+ avatarRef.current?.update(deltaSeconds);
48
+ });
49
+ useImperativeHandle(forwardedRef, () => avatar, [avatar]);
50
+ return (_jsx("group", { ...groupProps, children: object ? _jsx("primitive", { object: object, dispose: null }) : null }));
51
+ });
52
+ //# sourceMappingURL=react-three-fiber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-three-fiber.js","sourceRoot":"","sources":["../src/react-three-fiber.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { AvatarExpression, AvatarOption, AvatarPaletteOption, AvatarRecipe, AvatarRecipeDocument, AvatarSeed } from "./types.js";
2
+ export declare const AVATAR_RECIPE_SCHEMA: "@pieai/swimmer-avatar-kit/recipe";
3
+ export declare const AVATAR_RECIPE_VERSION: 1;
4
+ export declare const SPECIES: readonly AvatarOption[];
5
+ export declare const PALETTES: readonly AvatarPaletteOption[];
6
+ export declare const PARTS: readonly AvatarOption[];
7
+ export declare const EXPRESSIONS: readonly AvatarExpression[];
8
+ export declare function cloneAvatarRecipe(recipe: AvatarRecipe): AvatarRecipe;
9
+ export declare function assertAvatarRecipe(value: unknown): asserts value is AvatarRecipe;
10
+ export declare function completeRecipe(recipe: AvatarRecipe): AvatarRecipe;
11
+ export declare const fillRecipe: typeof completeRecipe;
12
+ export declare function randomRecipe(seed?: AvatarSeed): AvatarRecipe;
13
+ export declare const createAvatarRecipe: typeof randomRecipe;
14
+ export declare function rerollPart(recipe: AvatarRecipe, partId: string): AvatarRecipe;
15
+ export declare function createAvatarRecipeDocument(recipe: AvatarRecipe): AvatarRecipeDocument;
16
+ export declare function parseAvatarRecipeDocument(value: unknown): AvatarRecipeDocument;
17
+ export declare function serializeAvatarRecipe(recipe: AvatarRecipe): string;
18
+ export declare function deserializeAvatarRecipe(serialized: string): AvatarRecipe;
package/dist/recipe.js ADDED
@@ -0,0 +1,118 @@
1
+ import { GLOSS_FACES } from "../vendor/kindergrimm/src/gloss/gface.js";
2
+ import { ensureGParams, GPARTS, newGRecipe, rerollGPart, } from "../vendor/kindergrimm/src/gloss/grig.js";
3
+ import { PALETTES as GLOSS_PALETTES } from "../vendor/kindergrimm/src/gloss/gpalette.js";
4
+ import { GSPECIES, GSPECIES_IDS } from "../vendor/kindergrimm/src/gloss/gspecies.js";
5
+ import { hashStr } from "../vendor/kindergrimm/src/rng.js";
6
+ export const AVATAR_RECIPE_SCHEMA = "@pieai/swimmer-avatar-kit/recipe";
7
+ export const AVATAR_RECIPE_VERSION = 1;
8
+ export const SPECIES = Object.freeze(GSPECIES_IDS.map((id) => ({ id, label: GSPECIES[id]?.label ?? id })));
9
+ export const PALETTES = Object.freeze(GLOSS_PALETTES.map((palette) => ({
10
+ id: palette.id,
11
+ label: palette.label,
12
+ colors: Object.freeze([...palette.colors]),
13
+ })));
14
+ export const PARTS = Object.freeze(GPARTS.map((part) => ({ id: part.id, label: part.label })));
15
+ export const EXPRESSIONS = Object.freeze([...GLOSS_FACES]);
16
+ const partIds = new Set(PARTS.map((part) => part.id));
17
+ function isRecord(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ function isNullableString(value) {
21
+ return typeof value === "string" || value === null;
22
+ }
23
+ function seedToNumber(seed) {
24
+ if (seed === undefined)
25
+ return (Math.random() * 1_000_000_000) | 0;
26
+ if (typeof seed === "number") {
27
+ if (!Number.isFinite(seed))
28
+ throw new TypeError("Avatar seed must be a finite number.");
29
+ return Math.trunc(seed) >>> 0;
30
+ }
31
+ if (/^\d+$/.test(seed))
32
+ return Number(BigInt(seed) & 0xffffffffn);
33
+ return hashStr(seed);
34
+ }
35
+ export function cloneAvatarRecipe(recipe) {
36
+ return structuredClone(recipe);
37
+ }
38
+ export function assertAvatarRecipe(value) {
39
+ if (!isRecord(value))
40
+ throw new TypeError("Avatar recipe must be an object.");
41
+ if (!Number.isInteger(value.seed) || Number(value.seed) < 0 || Number(value.seed) > 0xffff_ffff) {
42
+ throw new TypeError("Avatar recipe seed must be an unsigned 32-bit integer.");
43
+ }
44
+ for (const field of ["species", "body", "stance", "palette", "material"]) {
45
+ if (!isNullableString(value[field])) {
46
+ throw new TypeError(`Avatar recipe ${field} must be a string or null.`);
47
+ }
48
+ }
49
+ if (value.colorIx !== null && (!Number.isInteger(value.colorIx) || Number(value.colorIx) < 0)) {
50
+ throw new TypeError("Avatar recipe colorIx must be a non-negative integer or null.");
51
+ }
52
+ if (!isRecord(value.parts))
53
+ throw new TypeError("Avatar recipe parts must be an object.");
54
+ for (const [partId, part] of Object.entries(value.parts)) {
55
+ if (!isRecord(part))
56
+ throw new TypeError(`Avatar recipe part ${partId} must be an object.`);
57
+ if (part.rr !== undefined && (!Number.isInteger(part.rr) || Number(part.rr) < 0)) {
58
+ throw new TypeError(`Avatar recipe part ${partId}.rr must be a non-negative integer.`);
59
+ }
60
+ }
61
+ }
62
+ export function completeRecipe(recipe) {
63
+ assertAvatarRecipe(recipe);
64
+ const next = cloneAvatarRecipe(recipe);
65
+ ensureGParams(next);
66
+ return next;
67
+ }
68
+ export const fillRecipe = completeRecipe;
69
+ export function randomRecipe(seed) {
70
+ const recipe = newGRecipe(seedToNumber(seed));
71
+ ensureGParams(recipe);
72
+ return cloneAvatarRecipe(recipe);
73
+ }
74
+ export const createAvatarRecipe = randomRecipe;
75
+ export function rerollPart(recipe, partId) {
76
+ if (!partIds.has(partId))
77
+ throw new RangeError(`Unknown avatar part: ${partId}`);
78
+ const next = completeRecipe(recipe);
79
+ rerollGPart(next, partId);
80
+ return next;
81
+ }
82
+ export function createAvatarRecipeDocument(recipe) {
83
+ return {
84
+ schema: AVATAR_RECIPE_SCHEMA,
85
+ version: AVATAR_RECIPE_VERSION,
86
+ renderer: "gloss",
87
+ recipe: completeRecipe(recipe),
88
+ };
89
+ }
90
+ export function parseAvatarRecipeDocument(value) {
91
+ if (!isRecord(value))
92
+ throw new TypeError("Avatar recipe document must be an object.");
93
+ if (value.schema !== AVATAR_RECIPE_SCHEMA) {
94
+ throw new TypeError(`Unsupported avatar recipe schema: ${String(value.schema)}`);
95
+ }
96
+ if (value.version !== AVATAR_RECIPE_VERSION) {
97
+ throw new TypeError(`Unsupported avatar recipe version: ${String(value.version)}`);
98
+ }
99
+ if (value.renderer !== "gloss") {
100
+ throw new TypeError(`Unsupported avatar renderer: ${String(value.renderer)}`);
101
+ }
102
+ assertAvatarRecipe(value.recipe);
103
+ return createAvatarRecipeDocument(value.recipe);
104
+ }
105
+ export function serializeAvatarRecipe(recipe) {
106
+ return JSON.stringify(createAvatarRecipeDocument(recipe));
107
+ }
108
+ export function deserializeAvatarRecipe(serialized) {
109
+ let value;
110
+ try {
111
+ value = JSON.parse(serialized);
112
+ }
113
+ catch (error) {
114
+ throw new TypeError(`Avatar recipe document is not valid JSON: ${String(error)}`);
115
+ }
116
+ return parseAvatarRecipeDocument(value).recipe;
117
+ }
118
+ //# sourceMappingURL=recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe.js","sourceRoot":"","sources":["../src/recipe.ts"],"names":[],"mappings":"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"}