@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
@@ -0,0 +1,201 @@
1
+ // ---------------------------------------------------------------
2
+ // THE MODELED BODIES — `rock` and `slime`, built the way a modeller
3
+ // would build them: a low-poly control CAGE run through Catmull-Clark.
4
+ //
5
+ // This is the third technique tried on these two shapes and the first
6
+ // that works, and the sequence is the lesson:
7
+ //
8
+ // 1. a RADIAL DISPLACEMENT on the ball. Scaling a sphere in and out
9
+ // along the ray moves its surface but keeps its topology of
10
+ // extents — the bottom stays as round as the top — so nothing
11
+ // could sit flat and no peak could close.
12
+ // 2. a PROFILE, a surface of revolution over a silhouette function.
13
+ // Better: it can be flat-bottomed and it can come to a point. But
14
+ // a silhouette is only the outline; everything a shape does that
15
+ // is not its outline has to be smuggled in as a fudge on top.
16
+ // 3. a CAGE. Eight lines of numbers, and the shape is simply drawn.
17
+ //
18
+ // The lab already knew this. It is written down against the chibi
19
+ // skull that was here before: **a shape you can name the parts of
20
+ // wants a cage, not an exponent.** A rock has a foot and a dome; a
21
+ // slime has a base, a waist and a tip. Those are parts, so they get a
22
+ // cage.
23
+ //
24
+ // The layout's contract survives untouched: a body only has to provide
25
+ // `at(ax, ay)` → point + normal, and this provides it by RAY CASTING
26
+ // its own mesh from the centre. The mesh is star-shaped about the
27
+ // origin, so a ray hits exactly one front face, and the normal is
28
+ // interpolated from smooth vertex normals — features land on the real
29
+ // subdivided surface rather than on an idea of it. The same arrays the
30
+ // layout cast against are what get stamped, so the surface the face
31
+ // sits on and the surface that is drawn are one object.
32
+ // ---------------------------------------------------------------
33
+ import { subdivideN } from './catmullClark.js';
34
+
35
+ const lerp = (a, b, t) => a + (b - a) * t;
36
+
37
+ // A cage is a stack of RINGS: [y, radius], bottom to top, in a unit
38
+ // box. Deliberately few — the whole point of a cage is that you can
39
+ // read the shape off the numbers.
40
+ const CAGE = {
41
+ // FLAT ON THE BOTTOM, ROUNDED UP. The foot is wide and the two rings
42
+ // above it are nearly as wide, so the side leaves the ground almost
43
+ // vertically and the base reads as a face it is resting on. Then it
44
+ // domes.
45
+ // FEWER SIDES ON PURPOSE. Twelve sides and a small jitter subdivide
46
+ // into a smooth dome — which is a pebble, not a rock. Eight sides
47
+ // leave facets big enough to survive Catmull-Clark, and the jitter
48
+ // has to be large enough to be a FORM rather than a wobble.
49
+ rock: {
50
+ sides: 8, subdiv: 2, jitter: .17,
51
+ levels: [
52
+ [-1.00, .74], [-.95, .93], [-.72, 1.02], [-.30, 1.04],
53
+ [.12, .98], [.52, .82], [.82, .54], [1.00, .18],
54
+ ],
55
+ },
56
+ // A DROP. A wide round base, the widest point low, and a long taper
57
+ // to a soft point — the tip ring is small rather than collapsed, so
58
+ // subdivision rounds it instead of pinching it.
59
+ slime: {
60
+ sides: 12, subdiv: 2, jitter: 0,
61
+ levels: [
62
+ [-1.00, .46], [-.92, .80], [-.68, .98], [-.34, 1.02],
63
+ [.06, .90], [.44, .64], [.76, .34], [1.00, .07],
64
+ ],
65
+ },
66
+ };
67
+
68
+ export const FORM_MESHES = Object.keys(CAGE);
69
+ export const isMeshForm = f => f in CAGE;
70
+
71
+ /** deterministic per-vertex nudge. From the indices, never from `rng`:
72
+ * a body is rebuilt on every boil frame and anything rolled per frame
73
+ * would shimmer — the same rule as `wobble` in gshape.js. */
74
+ const jit = (i, k) => Math.sin(i * 12.9898 + k * 78.233) * .5 + .5;
75
+
76
+ function buildCage(p) {
77
+ const { sides, levels, jitter } = p;
78
+ const verts = [], rings = [];
79
+ for (let j = 0; j < levels.length; j++) {
80
+ const [y, r] = levels[j];
81
+ const ring = [];
82
+ for (let i = 0; i < sides; i++) {
83
+ const a = i / sides * Math.PI * 2;
84
+ // the lumps are in the CAGE, so subdivision softens them into
85
+ // swells rather than leaving them as dents
86
+ const k = jitter ? 1 + jitter * (jit(i, j) - .5) * 2 : 1;
87
+ const ky = jitter ? y + jitter * .25 * (jit(i + 7, j) - .5) * 2 : y;
88
+ verts.push([Math.sin(a) * r * k, ky, Math.cos(a) * r * k]);
89
+ ring.push(verts.length - 1);
90
+ }
91
+ rings.push(ring);
92
+ }
93
+ const faces = [];
94
+ for (let j = 0; j < rings.length - 1; j++)
95
+ for (let i = 0; i < sides; i++) {
96
+ const k = (i + 1) % sides;
97
+ faces.push([rings[j][i], rings[j][k], rings[j + 1][k], rings[j + 1][i]]);
98
+ }
99
+ // both ends closed with one n-gon. Catmull-Clark takes any arity, and
100
+ // an n-gon cap is what rounds the rock's foot and the slime's tip.
101
+ faces.push(rings[rings.length - 1].slice());
102
+ faces.push(rings[0].slice().reverse());
103
+ return { verts, faces };
104
+ }
105
+
106
+ /**
107
+ * form id + multipliers → the surface.
108
+ *
109
+ * Returns the subdivided mesh (pure arrays, for `meshGeometry`) plus
110
+ * everything the LAYOUT needs from a body: `pick(dir)`, which `at` is
111
+ * built on, the bounds, and the silhouette half-width by height.
112
+ */
113
+ export function formSurface(form, { scale = 1, width = 1, height = 1, depth = 1 } = {}) {
114
+ const p = CAGE[form];
115
+ const mesh = subdivideN(buildCage(p), p.subdiv);
116
+ const { verts, faces } = mesh;
117
+ for (const v of verts) {
118
+ v[0] *= scale * width; v[1] *= scale * height; v[2] *= scale * depth;
119
+ }
120
+
121
+ // ---- smooth vertex normals -----------------------------------------
122
+ const vn = verts.map(() => [0, 0, 0]);
123
+ for (const f of faces) {
124
+ // Newell's method: robust for any planar-ish n-gon
125
+ let nx = 0, ny = 0, nz = 0;
126
+ for (let i = 0; i < f.length; i++) {
127
+ const a = verts[f[i]], b = verts[f[(i + 1) % f.length]];
128
+ nx += (a[1] - b[1]) * (a[2] + b[2]);
129
+ ny += (a[2] - b[2]) * (a[0] + b[0]);
130
+ nz += (a[0] - b[0]) * (a[1] + b[1]);
131
+ }
132
+ for (const vi of f) { vn[vi][0] += nx; vn[vi][1] += ny; vn[vi][2] += nz; }
133
+ }
134
+ for (const n of vn) {
135
+ const l = Math.hypot(n[0], n[1], n[2]) || 1;
136
+ n[0] /= l; n[1] /= l; n[2] /= l;
137
+ }
138
+
139
+ // ---- bounds, and the half-width binned by height --------------------
140
+ let minY = 1e9, maxY = -1e9, maxX = 0, maxZ = 0;
141
+ for (const v of verts) {
142
+ minY = Math.min(minY, v[1]); maxY = Math.max(maxY, v[1]);
143
+ maxX = Math.max(maxX, Math.abs(v[0])); maxZ = Math.max(maxZ, Math.abs(v[2]));
144
+ }
145
+ const BINS = 24, hw = new Array(BINS).fill(0);
146
+ for (const v of verts) {
147
+ const b = Math.min(BINS - 1, Math.max(0,
148
+ ((v[1] - minY) / (maxY - minY) * BINS) | 0));
149
+ hw[b] = Math.max(hw[b], Math.abs(v[0]));
150
+ }
151
+ for (let i = 0; i < BINS; i++) hw[i] = Math.max(hw[i], hw[i - 1] ?? 0, hw[i + 1] ?? 0);
152
+
153
+ // ---- triangles for the raycast --------------------------------------
154
+ const tris = [];
155
+ for (const f of faces)
156
+ for (let i = 2; i < f.length; i++) tris.push([f[0], f[i - 1], f[i]]);
157
+
158
+ /** ray from the origin along `dir` → { p, n }. Möller–Trumbore over
159
+ * every triangle; the mesh is star-shaped about the origin so
160
+ * exactly one front face is hit. */
161
+ function pick(dx, dy, dz) {
162
+ let best = Infinity, hit = null;
163
+ for (const [ia, ib, ic] of tris) {
164
+ const a = verts[ia], b = verts[ib], c = verts[ic];
165
+ const e1x = b[0] - a[0], e1y = b[1] - a[1], e1z = b[2] - a[2];
166
+ const e2x = c[0] - a[0], e2y = c[1] - a[1], e2z = c[2] - a[2];
167
+ const px = dy * e2z - dz * e2y, py = dz * e2x - dx * e2z, pz = dx * e2y - dy * e2x;
168
+ const det = e1x * px + e1y * py + e1z * pz;
169
+ if (Math.abs(det) < 1e-12) continue;
170
+ const inv = 1 / det;
171
+ const u = -(a[0] * px + a[1] * py + a[2] * pz) * inv;
172
+ if (u < -1e-6 || u > 1 + 1e-6) continue;
173
+ const qx = -a[1] * e1z + a[2] * e1y, qy = -a[2] * e1x + a[0] * e1z,
174
+ qz = -a[0] * e1y + a[1] * e1x;
175
+ const v = (dx * qx + dy * qy + dz * qz) * inv;
176
+ if (v < -1e-6 || u + v > 1 + 1e-6) continue;
177
+ const t = (e2x * qx + e2y * qy + e2z * qz) * inv;
178
+ if (t <= 1e-9 || t >= best) continue;
179
+ best = t; hit = { ia, ib, ic, u, v, t };
180
+ }
181
+ if (!hit) return null;
182
+ const { ia, ib, ic, u, v, t } = hit, w = 1 - u - v;
183
+ const na = vn[ia], nb = vn[ib], nc = vn[ic];
184
+ let nx = w * na[0] + u * nb[0] + v * nc[0];
185
+ let ny = w * na[1] + u * nb[1] + v * nc[1];
186
+ let nz = w * na[2] + u * nb[2] + v * nc[2];
187
+ const l = Math.hypot(nx, ny, nz) || 1;
188
+ return { p: [dx * t, dy * t, dz * t], n: [nx / l, ny / l, nz / l] };
189
+ }
190
+
191
+ return {
192
+ mesh, minY, maxY, maxX, maxZ, pick,
193
+ /** silhouette half-width at height y, in the mesh's own frame. */
194
+ halfWidthAt(y) {
195
+ const f = (y - minY) / (maxY - minY) * BINS - .5;
196
+ const i = Math.min(BINS - 1, Math.max(0, f | 0));
197
+ const j = Math.min(BINS - 1, i + 1);
198
+ return lerp(hw[i], hw[j], Math.min(1, Math.max(0, f - i)));
199
+ },
200
+ };
201
+ }