@oxy.so/bloom 7.1.2 → 7.1.4
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/assets/character-runtime/README.md +114 -19
- package/assets/character-runtime/authored-parts.mjs +754 -0
- package/assets/character-runtime/catalog-parts.mjs +362 -0
- package/assets/character-runtime/character-recipe.mjs +247 -0
- package/assets/character-runtime/clippo-geometry.mjs +679 -0
- package/assets/character-runtime/controller-mode.mjs +208 -0
- package/assets/character-runtime/cyclops-geometry.mjs +180 -0
- package/assets/character-runtime/legacy-engine.mjs +406 -54
- package/assets/character-runtime/legacy-geometry.mjs +242 -85
- package/assets/character-runtime/legacy-worker.mjs +82 -23
- package/assets/character-runtime/managed-avatar.mjs +12 -17
- package/assets/character-runtime/migrated-contours.mjs +2067 -0
- package/assets/character-runtime/migrated-faces.mjs +59 -0
- package/assets/character-runtime/pose-transition.mjs +523 -0
- package/assets/character-runtime/preparation-cache.mjs +60 -0
- package/assets/character-runtime/render-budget.mjs +5 -3
- package/assets/character-runtime/render-errors.mjs +180 -0
- package/assets/character-runtime/resolution.mjs +10 -0
- package/assets/character-runtime/runtime.mjs +744 -295
- package/assets/character-runtime/sha256.mjs +44 -0
- package/assets/character-runtime/shared-programs.mjs +534 -0
- package/assets/character-runtime/shared-surface.mjs +490 -0
- package/assets/character-runtime/shared-textures.mjs +420 -0
- package/assets/character-runtime/thumbnails/eyes/todd.png +0 -0
- package/docs/agent-avatar.mdx +45 -8
- package/docs/agent-creator.mdx +13 -3
- package/lib/commonjs/agent-avatar/AgentAvatar.js +1 -1
- package/lib/commonjs/agent-avatar/AgentAvatar.js.map +1 -1
- package/lib/commonjs/agent-avatar/CharacterAvatar.js +2 -1
- package/lib/commonjs/agent-avatar/CharacterAvatar.js.map +1 -1
- package/lib/commonjs/agent-avatar/character-colors.js +19 -0
- package/lib/commonjs/agent-avatar/character-colors.js.map +1 -0
- package/lib/commonjs/agent-avatar/character-shapes.js +106 -0
- package/lib/commonjs/agent-avatar/character-shapes.js.map +1 -0
- package/lib/commonjs/agent-avatar/config-character.js +4 -0
- package/lib/commonjs/agent-avatar/config-character.js.map +1 -1
- package/lib/commonjs/agent-avatar/legacy-recipe.js +19 -6
- package/lib/commonjs/agent-avatar/legacy-recipe.js.map +1 -1
- package/lib/commonjs/agent-creator/AgentCreatorBase.js +67 -107
- package/lib/commonjs/agent-creator/AgentCreatorBase.js.map +1 -1
- package/lib/commonjs/agent-creator/CharacterControls.js +23 -5
- package/lib/commonjs/agent-creator/CharacterControls.js.map +1 -1
- package/lib/commonjs/agent-creator/ClippoArtwork.js +63 -0
- package/lib/commonjs/agent-creator/ClippoArtwork.js.map +1 -0
- package/lib/commonjs/agent-creator/CyclopsArtwork.js +45 -0
- package/lib/commonjs/agent-creator/CyclopsArtwork.js.map +1 -0
- package/lib/commonjs/agent-creator/EmotionPicker.js +8 -1
- package/lib/commonjs/agent-creator/EmotionPicker.js.map +1 -1
- package/lib/commonjs/agent-creator/ShapeSilhouette.js +8 -1
- package/lib/commonjs/agent-creator/ShapeSilhouette.js.map +1 -1
- package/lib/commonjs/agent-creator/ToddArtwork.js +29 -0
- package/lib/commonjs/agent-creator/ToddArtwork.js.map +1 -0
- package/lib/commonjs/agent-creator/constants.js +16 -17
- package/lib/commonjs/agent-creator/constants.js.map +1 -1
- package/lib/commonjs/agent-creator/messages.js +1 -1
- package/lib/commonjs/agent-creator/messages.js.map +1 -1
- package/lib/commonjs/locale/translations/ar.js +1 -1
- package/lib/commonjs/locale/translations/ar.js.map +1 -1
- package/lib/commonjs/locale/translations/bn.js +1 -1
- package/lib/commonjs/locale/translations/bn.js.map +1 -1
- package/lib/commonjs/locale/translations/ca.js +1 -1
- package/lib/commonjs/locale/translations/ca.js.map +1 -1
- package/lib/commonjs/locale/translations/de.js +1 -1
- package/lib/commonjs/locale/translations/de.js.map +1 -1
- package/lib/commonjs/locale/translations/es.js +1 -1
- package/lib/commonjs/locale/translations/es.js.map +1 -1
- package/lib/commonjs/locale/translations/fr.js +1 -1
- package/lib/commonjs/locale/translations/fr.js.map +1 -1
- package/lib/commonjs/locale/translations/hi.js +1 -1
- package/lib/commonjs/locale/translations/hi.js.map +1 -1
- package/lib/commonjs/locale/translations/id.js +1 -1
- package/lib/commonjs/locale/translations/id.js.map +1 -1
- package/lib/commonjs/locale/translations/it.js +1 -1
- package/lib/commonjs/locale/translations/it.js.map +1 -1
- package/lib/commonjs/locale/translations/ja.js +1 -1
- package/lib/commonjs/locale/translations/ja.js.map +1 -1
- package/lib/commonjs/locale/translations/pt.js +1 -1
- package/lib/commonjs/locale/translations/pt.js.map +1 -1
- package/lib/commonjs/locale/translations/ru.js +1 -1
- package/lib/commonjs/locale/translations/ru.js.map +1 -1
- package/lib/commonjs/locale/translations/tr.js +1 -1
- package/lib/commonjs/locale/translations/tr.js.map +1 -1
- package/lib/commonjs/locale/translations/zh.js +1 -1
- package/lib/commonjs/locale/translations/zh.js.map +1 -1
- package/lib/module/agent-avatar/AgentAvatar.js +1 -1
- package/lib/module/agent-avatar/AgentAvatar.js.map +1 -1
- package/lib/module/agent-avatar/CharacterAvatar.js +2 -1
- package/lib/module/agent-avatar/CharacterAvatar.js.map +1 -1
- package/lib/module/agent-avatar/character-colors.js +15 -0
- package/lib/module/agent-avatar/character-colors.js.map +1 -0
- package/lib/module/agent-avatar/character-shapes.js +97 -0
- package/lib/module/agent-avatar/character-shapes.js.map +1 -0
- package/lib/module/agent-avatar/config-character.js +4 -0
- package/lib/module/agent-avatar/config-character.js.map +1 -1
- package/lib/module/agent-avatar/legacy-recipe.js +19 -6
- package/lib/module/agent-avatar/legacy-recipe.js.map +1 -1
- package/lib/module/agent-creator/AgentCreatorBase.js +69 -109
- package/lib/module/agent-creator/AgentCreatorBase.js.map +1 -1
- package/lib/module/agent-creator/CharacterControls.js +24 -6
- package/lib/module/agent-creator/CharacterControls.js.map +1 -1
- package/lib/module/agent-creator/ClippoArtwork.js +59 -0
- package/lib/module/agent-creator/ClippoArtwork.js.map +1 -0
- package/lib/module/agent-creator/CyclopsArtwork.js +41 -0
- package/lib/module/agent-creator/CyclopsArtwork.js.map +1 -0
- package/lib/module/agent-creator/EmotionPicker.js +8 -1
- package/lib/module/agent-creator/EmotionPicker.js.map +1 -1
- package/lib/module/agent-creator/ShapeSilhouette.js +8 -1
- package/lib/module/agent-creator/ShapeSilhouette.js.map +1 -1
- package/lib/module/agent-creator/ToddArtwork.js +25 -0
- package/lib/module/agent-creator/ToddArtwork.js.map +1 -0
- package/lib/module/agent-creator/constants.js +8 -16
- package/lib/module/agent-creator/constants.js.map +1 -1
- package/lib/module/agent-creator/messages.js +1 -1
- package/lib/module/agent-creator/messages.js.map +1 -1
- package/lib/module/locale/translations/ar.js +1 -1
- package/lib/module/locale/translations/ar.js.map +1 -1
- package/lib/module/locale/translations/bn.js +1 -1
- package/lib/module/locale/translations/bn.js.map +1 -1
- package/lib/module/locale/translations/ca.js +1 -1
- package/lib/module/locale/translations/ca.js.map +1 -1
- package/lib/module/locale/translations/de.js +1 -1
- package/lib/module/locale/translations/de.js.map +1 -1
- package/lib/module/locale/translations/es.js +1 -1
- package/lib/module/locale/translations/es.js.map +1 -1
- package/lib/module/locale/translations/fr.js +1 -1
- package/lib/module/locale/translations/fr.js.map +1 -1
- package/lib/module/locale/translations/hi.js +1 -1
- package/lib/module/locale/translations/hi.js.map +1 -1
- package/lib/module/locale/translations/id.js +1 -1
- package/lib/module/locale/translations/id.js.map +1 -1
- package/lib/module/locale/translations/it.js +1 -1
- package/lib/module/locale/translations/it.js.map +1 -1
- package/lib/module/locale/translations/ja.js +1 -1
- package/lib/module/locale/translations/ja.js.map +1 -1
- package/lib/module/locale/translations/pt.js +1 -1
- package/lib/module/locale/translations/pt.js.map +1 -1
- package/lib/module/locale/translations/ru.js +1 -1
- package/lib/module/locale/translations/ru.js.map +1 -1
- package/lib/module/locale/translations/tr.js +1 -1
- package/lib/module/locale/translations/tr.js.map +1 -1
- package/lib/module/locale/translations/zh.js +1 -1
- package/lib/module/locale/translations/zh.js.map +1 -1
- package/lib/typescript/commonjs/agent-avatar/CharacterAvatar.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-avatar/character-colors.d.ts +13 -0
- package/lib/typescript/commonjs/agent-avatar/character-colors.d.ts.map +1 -0
- package/lib/typescript/commonjs/agent-avatar/character-shapes.d.ts +19 -0
- package/lib/typescript/commonjs/agent-avatar/character-shapes.d.ts.map +1 -0
- package/lib/typescript/commonjs/agent-avatar/config-character.d.ts +4 -0
- package/lib/typescript/commonjs/agent-avatar/config-character.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts +2 -10
- package/lib/typescript/commonjs/agent-avatar/legacy-recipe.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/AgentCreatorBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts +2 -1
- package/lib/typescript/commonjs/agent-creator/CharacterControls.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/ClippoArtwork.d.ts +6 -0
- package/lib/typescript/commonjs/agent-creator/ClippoArtwork.d.ts.map +1 -0
- package/lib/typescript/commonjs/agent-creator/CyclopsArtwork.d.ts +5 -0
- package/lib/typescript/commonjs/agent-creator/CyclopsArtwork.d.ts.map +1 -0
- package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts +1 -0
- package/lib/typescript/commonjs/agent-creator/EmotionPicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/ShapeSilhouette.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/ToddArtwork.d.ts +6 -0
- package/lib/typescript/commonjs/agent-creator/ToddArtwork.d.ts.map +1 -0
- package/lib/typescript/commonjs/agent-creator/constants.d.ts +7 -15
- package/lib/typescript/commonjs/agent-creator/constants.d.ts.map +1 -1
- package/lib/typescript/commonjs/agent-creator/messages.d.ts.map +1 -1
- package/lib/typescript/module/agent-avatar/CharacterAvatar.d.ts.map +1 -1
- package/lib/typescript/module/agent-avatar/character-colors.d.ts +13 -0
- package/lib/typescript/module/agent-avatar/character-colors.d.ts.map +1 -0
- package/lib/typescript/module/agent-avatar/character-shapes.d.ts +19 -0
- package/lib/typescript/module/agent-avatar/character-shapes.d.ts.map +1 -0
- package/lib/typescript/module/agent-avatar/config-character.d.ts +4 -0
- package/lib/typescript/module/agent-avatar/config-character.d.ts.map +1 -1
- package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts +2 -10
- package/lib/typescript/module/agent-avatar/legacy-recipe.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/AgentCreatorBase.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/CharacterControls.d.ts +2 -1
- package/lib/typescript/module/agent-creator/CharacterControls.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/ClippoArtwork.d.ts +6 -0
- package/lib/typescript/module/agent-creator/ClippoArtwork.d.ts.map +1 -0
- package/lib/typescript/module/agent-creator/CyclopsArtwork.d.ts +5 -0
- package/lib/typescript/module/agent-creator/CyclopsArtwork.d.ts.map +1 -0
- package/lib/typescript/module/agent-creator/EmotionPicker.d.ts +1 -0
- package/lib/typescript/module/agent-creator/EmotionPicker.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/ShapeSilhouette.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/ToddArtwork.d.ts +6 -0
- package/lib/typescript/module/agent-creator/ToddArtwork.d.ts.map +1 -0
- package/lib/typescript/module/agent-creator/constants.d.ts +7 -15
- package/lib/typescript/module/agent-creator/constants.d.ts.map +1 -1
- package/lib/typescript/module/agent-creator/messages.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agent-avatar/AgentAvatar.tsx +1 -1
- package/src/agent-avatar/CharacterAvatar.tsx +4 -1
- package/src/agent-avatar/character-colors.ts +12 -0
- package/src/agent-avatar/character-shapes.ts +122 -0
- package/src/agent-avatar/config-character.ts +39 -6
- package/src/agent-avatar/legacy-recipe.ts +34 -6
- package/src/agent-creator/AgentCreatorBase.tsx +121 -152
- package/src/agent-creator/CharacterControls.tsx +39 -14
- package/src/agent-creator/ClippoArtwork.tsx +49 -0
- package/src/agent-creator/CyclopsArtwork.tsx +27 -0
- package/src/agent-creator/EmotionPicker.tsx +55 -16
- package/src/agent-creator/ShapeSilhouette.tsx +16 -1
- package/src/agent-creator/ToddArtwork.tsx +24 -0
- package/src/agent-creator/constants.ts +47 -14
- package/src/agent-creator/messages.ts +2 -1
- package/src/locale/translations/ar.ts +1 -1
- package/src/locale/translations/bn.ts +1 -1
- package/src/locale/translations/ca.ts +1 -1
- package/src/locale/translations/de.ts +1 -1
- package/src/locale/translations/es.ts +1 -1
- package/src/locale/translations/fr.ts +1 -1
- package/src/locale/translations/hi.ts +1 -1
- package/src/locale/translations/id.ts +1 -1
- package/src/locale/translations/it.ts +1 -1
- package/src/locale/translations/ja.ts +1 -1
- package/src/locale/translations/pt.ts +1 -1
- package/src/locale/translations/ru.ts +1 -1
- package/src/locale/translations/tr.ts +1 -1
- package/src/locale/translations/zh.ts +1 -1
- package/src/__tests__/support/adoption-matrix.ts +0 -683
- package/src/__tests__/support/card-surface.ts +0 -25
- package/src/__tests__/support/collision-fixture-barrel.ts +0 -20
- package/src/__tests__/support/commerce-harness.tsx +0 -97
- package/src/__tests__/support/composite.ts +0 -68
- package/src/__tests__/support/constructed-style-sheets.ts +0 -68
- package/src/__tests__/support/messages-in.ts +0 -10
- package/src/__tests__/support/press-host.ts +0 -30
- package/src/__tests__/support/rendered-style.ts +0 -99
- package/src/__tests__/support/unread-hook-fixture.ts +0 -33
- package/src/__tests__/support/worklet-capture-fixture.tsx +0 -28
- package/src/agent-avatar/__tests__/artwork-golden.json +0 -774
- package/src/agent-creator/__tests__/support/character-runtime-fixture.tsx +0 -26
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +0 -9346
- package/src/theme/__tests__/__fixtures__/tonal-glass-primary-failures.json +0 -69
- package/src/theme/__tests__/fixtures/color-engine-golden.json +0 -1
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
// Adapter for the recovered renderer's prepared vertex layout. Packaged meshes,
|
|
2
2
|
// resource manifests, the WASM binary, and the appearance record stay untouched.
|
|
3
|
-
const decoder = new TextDecoder(),
|
|
4
|
-
|
|
3
|
+
const decoder = new TextDecoder(),
|
|
4
|
+
encoder = new TextEncoder();
|
|
5
|
+
const BODY = ":body:no-shadow:studio-fur-v6:";
|
|
5
6
|
// Highlights are separate non-shadow-casting meshes in the original renderer.
|
|
6
|
-
const SURFACES = [
|
|
7
|
+
const SURFACES = [":surface:shadow", ":surface:no-shadow"];
|
|
7
8
|
const SURFACE_KEY = /^[a-f0-9]{64}:surface:(?:shadow|no-shadow)$/;
|
|
8
9
|
const FUR_SURFACE_KEY = /^[a-f0-9]{64}:surface:shadow:studio-fur-v6:/;
|
|
9
|
-
const STRIDE = 96,
|
|
10
|
-
|
|
11
|
-
function
|
|
10
|
+
const STRIDE = 96,
|
|
11
|
+
TAU = Math.PI * 2;
|
|
12
|
+
function invalid(message) {
|
|
13
|
+
throw new Error(`Unsupported prepared avatar geometry: ${message}`);
|
|
14
|
+
}
|
|
15
|
+
function finite(value) {
|
|
16
|
+
if (!Number.isFinite(value)) invalid("non-finite coordinate");
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
12
19
|
function findAll(bytes, text) {
|
|
13
|
-
const needle = encoder.encode(text),
|
|
20
|
+
const needle = encoder.encode(text),
|
|
21
|
+
result = [];
|
|
14
22
|
outer: for (let i = 0; i <= bytes.length - needle.length; i++) {
|
|
15
23
|
if (bytes[i] !== needle[0]) continue;
|
|
16
|
-
for (let j = 1; j < needle.length; j++)
|
|
24
|
+
for (let j = 1; j < needle.length; j++)
|
|
25
|
+
if (bytes[i + j] !== needle[j]) continue outer;
|
|
17
26
|
result.push(i);
|
|
18
27
|
}
|
|
19
28
|
return result;
|
|
@@ -21,82 +30,187 @@ function findAll(bytes, text) {
|
|
|
21
30
|
|
|
22
31
|
/** Inspect the bounded, length-prefixed mesh records; reject unknown layouts. */
|
|
23
32
|
export function inspectLegacyAssembly(bytes) {
|
|
24
|
-
if (!(bytes instanceof Uint8Array) || bytes.length < 400)
|
|
33
|
+
if (!(bytes instanceof Uint8Array) || bytes.length < 400)
|
|
34
|
+
invalid("truncated assembly");
|
|
25
35
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
26
|
-
const uint = at => {
|
|
36
|
+
const uint = (at) => {
|
|
37
|
+
if (at < 0 || at + 4 > bytes.length) invalid("truncated count");
|
|
38
|
+
return view.getUint32(at, true);
|
|
39
|
+
};
|
|
27
40
|
const bodies = findAll(bytes, BODY);
|
|
28
|
-
if (bodies.length !== 1) invalid(
|
|
41
|
+
if (bodies.length !== 1) invalid("expected one furry body");
|
|
29
42
|
const bodyStart = bodies[0] - 64;
|
|
30
|
-
const starts = [
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
const starts = [
|
|
44
|
+
bodyStart,
|
|
45
|
+
...SURFACES.flatMap((kind) =>
|
|
46
|
+
findAll(bytes, kind)
|
|
47
|
+
.map((i) => i - 64)
|
|
48
|
+
.filter((i) => {
|
|
49
|
+
if (i <= bodyStart || i < 4) return false;
|
|
50
|
+
const length = uint(i - 4);
|
|
51
|
+
if (length < 79 || length > 8192 || i + length > bytes.length)
|
|
52
|
+
return false;
|
|
53
|
+
const name = decoder.decode(bytes.subarray(i, i + length));
|
|
54
|
+
return SURFACE_KEY.test(name) || FUR_SURFACE_KEY.test(name);
|
|
55
|
+
}),
|
|
56
|
+
).sort((a, b) => a - b),
|
|
57
|
+
];
|
|
40
58
|
const meshCount = uint(0);
|
|
41
|
-
if (meshCount !== starts.length || meshCount > 40)
|
|
59
|
+
if (meshCount !== starts.length || meshCount > 40)
|
|
60
|
+
invalid("mesh count/key mismatch");
|
|
42
61
|
const parts = starts.map((nameOffset, part) => {
|
|
43
|
-
const nameLength = uint(nameOffset - 4),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
62
|
+
const nameLength = uint(nameOffset - 4),
|
|
63
|
+
name = decoder.decode(
|
|
64
|
+
bytes.subarray(nameOffset, nameOffset + nameLength),
|
|
65
|
+
);
|
|
66
|
+
if (
|
|
67
|
+
nameLength < 79 ||
|
|
68
|
+
nameLength > 8192 ||
|
|
69
|
+
nameOffset + nameLength > bytes.length ||
|
|
70
|
+
!/^[a-f0-9]{64}:/.test(name)
|
|
71
|
+
)
|
|
72
|
+
invalid("mesh identity");
|
|
73
|
+
if (
|
|
74
|
+
part === 0
|
|
75
|
+
? !name.includes(BODY)
|
|
76
|
+
: !SURFACE_KEY.test(name) && !FUR_SURFACE_KEY.test(name)
|
|
77
|
+
)
|
|
78
|
+
invalid("mesh kind");
|
|
79
|
+
const vertexCount = uint(nameOffset + nameLength),
|
|
80
|
+
vertexOffset = nameOffset + nameLength + 4;
|
|
81
|
+
if (vertexCount < 3 || vertexCount > 1000000) invalid("vertex count");
|
|
48
82
|
let at = vertexOffset + vertexCount * STRIDE;
|
|
49
|
-
const count32 = uint(at);
|
|
50
|
-
|
|
51
|
-
|
|
83
|
+
const count32 = uint(at);
|
|
84
|
+
at += 4;
|
|
85
|
+
if (count32 > 6000000 || at + count32 * 4 > bytes.length)
|
|
86
|
+
invalid("32-bit indices");
|
|
87
|
+
for (let i = 0; i < count32; i++)
|
|
88
|
+
if (uint(at + i * 4) >= vertexCount) invalid("index outside mesh");
|
|
52
89
|
at += count32 * 4;
|
|
53
|
-
const count16 = uint(at);
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
const count16 = uint(at);
|
|
91
|
+
at += 4;
|
|
92
|
+
if (count16 > 6000000 || at + count16 * 2 > bytes.length)
|
|
93
|
+
invalid("16-bit indices");
|
|
94
|
+
for (let i = 0; i < count16; i++)
|
|
95
|
+
if (view.getUint16(at + i * 2, true) >= vertexCount)
|
|
96
|
+
invalid("index outside mesh");
|
|
56
97
|
at += count16 * 2;
|
|
57
98
|
// Empty supplemental vertex buffer followed by three boolean mesh flags.
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
99
|
+
if (
|
|
100
|
+
at + 7 > bytes.length ||
|
|
101
|
+
uint(at) !== 0 ||
|
|
102
|
+
bytes[at + 4] > 1 ||
|
|
103
|
+
bytes[at + 5] > 1 ||
|
|
104
|
+
bytes[at + 6] > 1
|
|
105
|
+
)
|
|
106
|
+
invalid("mesh flags");
|
|
107
|
+
const hasFur = bytes[at + 6] === 1;
|
|
108
|
+
at += 7;
|
|
109
|
+
const furCount = hasFur ? uint(at) : 0,
|
|
110
|
+
furOffset = hasFur ? at + 4 : 0;
|
|
111
|
+
if (
|
|
112
|
+
furCount > 1000000 ||
|
|
113
|
+
(hasFur && furOffset + furCount * 64 > bytes.length)
|
|
114
|
+
)
|
|
115
|
+
invalid("fur records");
|
|
62
116
|
const geometryEnd = hasFur ? furOffset + furCount * 64 : at;
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
117
|
+
if (
|
|
118
|
+
geometryEnd >
|
|
119
|
+
(starts[part + 1] === undefined ? bytes.length : starts[part + 1] - 4)
|
|
120
|
+
)
|
|
121
|
+
invalid("overlapping meshes");
|
|
122
|
+
if (part === 0 && !hasFur) invalid("body lacks fur layout");
|
|
123
|
+
if (FUR_SURFACE_KEY.test(name) && !hasFur)
|
|
124
|
+
invalid("accessory lacks fur layout");
|
|
125
|
+
const min = [Infinity, Infinity, Infinity],
|
|
126
|
+
max = [-Infinity, -Infinity, -Infinity];
|
|
127
|
+
for (let i = 0; i < vertexCount; i++)
|
|
128
|
+
for (let k = 0; k < 24; k++) {
|
|
129
|
+
const value = finite(
|
|
130
|
+
view.getFloat32(vertexOffset + i * STRIDE + k * 4, true),
|
|
131
|
+
);
|
|
132
|
+
if (k < 3) {
|
|
133
|
+
min[k] = Math.min(min[k], value);
|
|
134
|
+
max[k] = Math.max(max[k], value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
part,
|
|
139
|
+
name,
|
|
140
|
+
nameOffset,
|
|
141
|
+
nameLength,
|
|
142
|
+
vertexCount,
|
|
143
|
+
vertexOffset,
|
|
144
|
+
furCount,
|
|
145
|
+
furOffset,
|
|
146
|
+
geometryEnd,
|
|
147
|
+
bounds: { min, max },
|
|
148
|
+
};
|
|
72
149
|
});
|
|
73
150
|
return { body: parts[0], parts };
|
|
74
151
|
}
|
|
75
152
|
|
|
76
153
|
function mapper({ points, radii, depthScale = 1 }) {
|
|
77
|
-
if (!Number.isFinite(depthScale) || depthScale <= 0 || depthScale > 4)
|
|
78
|
-
|
|
154
|
+
if (!Number.isFinite(depthScale) || depthScale <= 0 || depthScale > 4)
|
|
155
|
+
invalid("depth scale");
|
|
156
|
+
if ((points && radii) || (!points && !radii))
|
|
157
|
+
invalid("provide points or radii");
|
|
79
158
|
const count = (points || radii).length;
|
|
80
|
-
if (count < 16 || count > 65536) invalid(
|
|
81
|
-
if (points)
|
|
82
|
-
|
|
159
|
+
if (count < 16 || count > 65536) invalid("contour resolution");
|
|
160
|
+
if (points)
|
|
161
|
+
for (const p of points) {
|
|
162
|
+
if (p.length !== 2) invalid("contour point");
|
|
163
|
+
p.forEach(finite);
|
|
164
|
+
}
|
|
165
|
+
else
|
|
166
|
+
for (const radius of radii)
|
|
167
|
+
if (!(finite(radius) > 0)) invalid("contour radius");
|
|
83
168
|
return (x, y, z) => {
|
|
84
|
-
const radius = Math.hypot(x, y),
|
|
85
|
-
|
|
86
|
-
|
|
169
|
+
const radius = Math.hypot(x, y),
|
|
170
|
+
angle = (Math.atan2(y, x) + TAU) % TAU;
|
|
171
|
+
const slot = (angle / TAU) * count,
|
|
172
|
+
index = Math.floor(slot),
|
|
173
|
+
t = slot - index,
|
|
174
|
+
next = (index + 1) % count;
|
|
175
|
+
if (points)
|
|
176
|
+
return [
|
|
177
|
+
radius * (points[index][0] * (1 - t) + points[next][0] * t),
|
|
178
|
+
radius * (points[index][1] * (1 - t) + points[next][1] * t),
|
|
179
|
+
z * depthScale,
|
|
180
|
+
];
|
|
87
181
|
const scale = radii[index] * (1 - t) + radii[next] * t;
|
|
88
182
|
return [x * scale, y * scale, z * depthScale];
|
|
89
183
|
};
|
|
90
184
|
}
|
|
91
185
|
function derivative(map, p) {
|
|
92
|
-
const epsilon = .00005,
|
|
93
|
-
|
|
186
|
+
const epsilon = 0.00005,
|
|
187
|
+
base = map(...p);
|
|
188
|
+
const columns = [0, 1, 2].map((k) => {
|
|
189
|
+
const q = [...p];
|
|
190
|
+
q[k] += epsilon;
|
|
191
|
+
return map(...q).map((v, i) => (v - base[i]) / epsilon);
|
|
192
|
+
});
|
|
94
193
|
const [a, b, c] = columns;
|
|
95
|
-
const cross = (u, v) => [
|
|
194
|
+
const cross = (u, v) => [
|
|
195
|
+
u[1] * v[2] - u[2] * v[1],
|
|
196
|
+
u[2] * v[0] - u[0] * v[2],
|
|
197
|
+
u[0] * v[1] - u[1] * v[0],
|
|
198
|
+
];
|
|
96
199
|
const inverse = [cross(b, c), cross(c, a), cross(a, b)];
|
|
97
200
|
const determinant = a.reduce((sum, v, i) => sum + v * inverse[0][i], 0);
|
|
98
|
-
if (Math.abs(determinant) < .000001) invalid(
|
|
99
|
-
return {
|
|
201
|
+
if (Math.abs(determinant) < 0.000001) invalid("singular contour");
|
|
202
|
+
return {
|
|
203
|
+
base,
|
|
204
|
+
vector: (v) =>
|
|
205
|
+
[0, 1, 2].map((i) =>
|
|
206
|
+
columns.reduce((sum, col, j) => sum + col[i] * v[j], 0),
|
|
207
|
+
),
|
|
208
|
+
normal: (v) =>
|
|
209
|
+
[0, 1, 2].map(
|
|
210
|
+
(i) =>
|
|
211
|
+
inverse.reduce((sum, col, j) => sum + col[i] * v[j], 0) / determinant,
|
|
212
|
+
),
|
|
213
|
+
};
|
|
100
214
|
}
|
|
101
215
|
|
|
102
216
|
/** Rigid attachment anchors on the source circle, in the engine's XY units.
|
|
@@ -105,9 +219,10 @@ function derivative(map, p) {
|
|
|
105
219
|
*/
|
|
106
220
|
export function legacyAttachmentOffset(points, accessory) {
|
|
107
221
|
let anchor;
|
|
108
|
-
if (accessory ===
|
|
109
|
-
else if (accessory ===
|
|
110
|
-
else if ([
|
|
222
|
+
if (accessory === "bow") anchor = [0, -1, 0];
|
|
223
|
+
else if (accessory === "three_lobe") anchor = [-0.5, Math.sqrt(0.75), 0];
|
|
224
|
+
else if (["beanie", "hat", "beret", "orb", "crown"].includes(accessory))
|
|
225
|
+
anchor = [0, 1, 0];
|
|
111
226
|
else return undefined;
|
|
112
227
|
return mapper({ points })(...anchor).map((value, i) => value - anchor[i]);
|
|
113
228
|
}
|
|
@@ -120,47 +235,89 @@ export function legacyAttachmentOffset(points, accessory) {
|
|
|
120
235
|
* Attachments may explicitly transform a numbered part around an origin.
|
|
121
236
|
*/
|
|
122
237
|
export function deformLegacyAssembly(bytes, options) {
|
|
123
|
-
const info = inspectLegacyAssembly(bytes),
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
238
|
+
const info = inspectLegacyAssembly(bytes),
|
|
239
|
+
result = bytes.slice();
|
|
240
|
+
if (!/^[a-f0-9]{64}$/.test(options.geometryKey || ""))
|
|
241
|
+
invalid("missing geometry cache key");
|
|
242
|
+
const view = new DataView(result.buffer),
|
|
243
|
+
map = mapper(options);
|
|
244
|
+
const read = (at) => [0, 1, 2].map((i) => view.getFloat32(at + i * 4, true));
|
|
245
|
+
const write = (at, values) =>
|
|
246
|
+
values.forEach((v, i) => view.setFloat32(at + i * 4, finite(v), true));
|
|
247
|
+
const unit = (v) => {
|
|
248
|
+
const n = Math.hypot(...v);
|
|
249
|
+
return n > 1e-9 ? v.map((x) => x / n) : v;
|
|
250
|
+
};
|
|
129
251
|
function transform(part, mapping) {
|
|
130
252
|
for (let i = 0; i < part.vertexCount; i++) {
|
|
131
|
-
const at = part.vertexOffset + i * STRIDE,
|
|
253
|
+
const at = part.vertexOffset + i * STRIDE,
|
|
254
|
+
p = read(at),
|
|
255
|
+
d = derivative(mapping, p);
|
|
132
256
|
// The two pose positions are DELTAS: preserve zero as zero and deform the
|
|
133
257
|
// target position before subtracting the newly transformed base.
|
|
134
|
-
for (const offset of [24, 48]) {
|
|
135
|
-
|
|
258
|
+
for (const offset of [24, 48]) {
|
|
259
|
+
const delta = read(at + offset),
|
|
260
|
+
target = mapping(...p.map((v, k) => v + delta[k]));
|
|
261
|
+
write(
|
|
262
|
+
at + offset,
|
|
263
|
+
target.map((v, k) => v - d.base[k]),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
for (const offset of [12, 36, 60, 84])
|
|
267
|
+
write(at + offset, unit(d.normal(read(at + offset))));
|
|
136
268
|
write(at, d.base);
|
|
137
269
|
}
|
|
138
270
|
for (let i = 0; i < part.furCount; i++) {
|
|
139
|
-
const at = part.furOffset + i * 64,
|
|
140
|
-
|
|
271
|
+
const at = part.furOffset + i * 64,
|
|
272
|
+
d = derivative(mapping, read(at));
|
|
273
|
+
write(at, d.base);
|
|
274
|
+
write(at + 12, unit(d.normal(read(at + 12))));
|
|
275
|
+
write(at + 24, unit(d.vector(read(at + 24))));
|
|
141
276
|
}
|
|
142
277
|
result.set(encoder.encode(options.geometryKey), part.nameOffset);
|
|
143
278
|
}
|
|
144
279
|
transform(info.body, map);
|
|
145
280
|
for (const attachment of options.attachments || []) {
|
|
146
281
|
const part = info.parts[attachment.part];
|
|
147
|
-
if (!part || part.part === 0) invalid(
|
|
148
|
-
const scale = attachment.scale || [1, 1, 1],
|
|
149
|
-
|
|
150
|
-
|
|
282
|
+
if (!part || part.part === 0) invalid("attachment index");
|
|
283
|
+
const scale = attachment.scale || [1, 1, 1],
|
|
284
|
+
origin = attachment.origin || [0, 0, 0],
|
|
285
|
+
translate = attachment.translate || [0, 0, 0];
|
|
286
|
+
if (
|
|
287
|
+
[scale, origin, translate].some(
|
|
288
|
+
(v) => v.length !== 3 || v.some((x) => !Number.isFinite(x)),
|
|
289
|
+
) ||
|
|
290
|
+
scale.some((x) => x <= 0)
|
|
291
|
+
)
|
|
292
|
+
invalid("attachment transform");
|
|
293
|
+
transform(part, (...p) =>
|
|
294
|
+
p.map((v, k) => (v - origin[k]) * scale[k] + origin[k] + translate[k]),
|
|
295
|
+
);
|
|
151
296
|
// Independent transformed parts must not alias the body's cache identity.
|
|
152
297
|
const key = options.attachmentKeys?.[attachment.part];
|
|
153
|
-
if (!/^[a-f0-9]{64}$/.test(key ||
|
|
298
|
+
if (!/^[a-f0-9]{64}$/.test(key || "")) invalid("attachment cache key");
|
|
154
299
|
result.set(encoder.encode(key), part.nameOffset);
|
|
155
300
|
}
|
|
156
301
|
// Keep original extents while admitting moved accessories and the new body.
|
|
157
|
-
const transformed = [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
302
|
+
const transformed = [
|
|
303
|
+
info.body,
|
|
304
|
+
...(options.attachments || []).map((a) => info.parts[a.part]),
|
|
305
|
+
];
|
|
306
|
+
for (const part of transformed)
|
|
307
|
+
for (let i = 0; i < part.vertexCount; i++) {
|
|
308
|
+
const p = read(part.vertexOffset + i * STRIDE);
|
|
309
|
+
for (let k = 0; k < 3; k++) {
|
|
310
|
+
view.setFloat32(
|
|
311
|
+
4 + k * 4,
|
|
312
|
+
Math.min(view.getFloat32(4 + k * 4, true), p[k]),
|
|
313
|
+
true,
|
|
314
|
+
);
|
|
315
|
+
view.setFloat32(
|
|
316
|
+
16 + k * 4,
|
|
317
|
+
Math.max(view.getFloat32(16 + k * 4, true), p[k]),
|
|
318
|
+
true,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
163
321
|
}
|
|
164
|
-
}
|
|
165
322
|
return result;
|
|
166
323
|
}
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
// the recovered engine, packaged meshes and native manifests remain intact.
|
|
3
3
|
import createModule from './orbit-characters.mjs';
|
|
4
4
|
import { decodeAppearance, encodeAppearance } from './appearance-codec.mjs';
|
|
5
|
+
import { composePresetBody, composeCatalogParts } from './catalog-parts.mjs';
|
|
6
|
+
import {
|
|
7
|
+
fitAuthoredEyeSpacing,
|
|
8
|
+
fitAuthoredHeadwear,
|
|
9
|
+
shapeFaceLayout,
|
|
10
|
+
authoredAssemblyRecords,
|
|
11
|
+
} from './authored-parts.mjs';
|
|
12
|
+
import { sha256 } from './sha256.mjs';
|
|
5
13
|
import {
|
|
6
14
|
deformLegacyAssembly,
|
|
7
15
|
inspectLegacyAssembly,
|
|
@@ -10,13 +18,8 @@ import {
|
|
|
10
18
|
const engine = createModule({
|
|
11
19
|
locateFile: (file) => new URL(file, import.meta.url).href,
|
|
12
20
|
});
|
|
13
|
-
const digest =
|
|
14
|
-
|
|
15
|
-
new Uint8Array(
|
|
16
|
-
await crypto.subtle.digest('SHA-256', typeof value === 'string' ? new TextEncoder().encode(value) : value),
|
|
17
|
-
),
|
|
18
|
-
(byte) => byte.toString(16).padStart(2, '0'),
|
|
19
|
-
).join('');
|
|
21
|
+
const digest = (value) =>
|
|
22
|
+
sha256(typeof value === 'string' ? new TextEncoder().encode(value) : value);
|
|
20
23
|
// Cache only identities, never the large prepared buffers. The same face with
|
|
21
24
|
// no accessories tells us exactly which meshes belong to eyes and eyewear.
|
|
22
25
|
const faceIdentities = new Map();
|
|
@@ -25,8 +28,12 @@ async function accessoryTransforms(module, data, source) {
|
|
|
25
28
|
// The editor selects one accessory. Preserve any unknown multi-accessory
|
|
26
29
|
// state rather than guessing which of its pieces should move together.
|
|
27
30
|
if (appearance.accessories.length !== 1) return {};
|
|
28
|
-
const translate = legacyAttachmentOffset(
|
|
29
|
-
|
|
31
|
+
const translate = legacyAttachmentOffset(
|
|
32
|
+
data.points,
|
|
33
|
+
appearance.accessories[0],
|
|
34
|
+
);
|
|
35
|
+
if (!translate || translate.every((value) => Math.abs(value) < 1e-6))
|
|
36
|
+
return {};
|
|
30
37
|
appearance.accessories = [];
|
|
31
38
|
appearance.accessoryColors = {};
|
|
32
39
|
if (appearance.model) appearance.model.accessories = {};
|
|
@@ -34,22 +41,35 @@ async function accessoryTransforms(module, data, source) {
|
|
|
34
41
|
const key = `${await digest(bytes)}:${data.quality}:${data.activities}`;
|
|
35
42
|
let names = faceIdentities.get(key);
|
|
36
43
|
if (!names) {
|
|
37
|
-
const base = module.orbitPrepareAssembly(
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const base = module.orbitPrepareAssembly(
|
|
45
|
+
bytes,
|
|
46
|
+
data.quality,
|
|
47
|
+
`${data.key}:attachment-base`,
|
|
48
|
+
data.activities,
|
|
49
|
+
);
|
|
50
|
+
if (!base.bytes)
|
|
51
|
+
throw new Error(base.error || 'Accessory reference preparation failed');
|
|
52
|
+
names = new Set(
|
|
53
|
+
inspectLegacyAssembly(base.bytes)
|
|
54
|
+
.parts.slice(1)
|
|
55
|
+
.map((part) => part.name),
|
|
56
|
+
);
|
|
40
57
|
faceIdentities.set(key, names);
|
|
41
|
-
if (faceIdentities.size > 32)
|
|
58
|
+
if (faceIdentities.size > 32)
|
|
59
|
+
faceIdentities.delete(faceIdentities.keys().next().value);
|
|
42
60
|
}
|
|
43
61
|
// Do not move anything unless every original face mesh survives unchanged.
|
|
44
|
-
const current = new Set(source.parts.slice(1).map(part => part.name));
|
|
45
|
-
if ([...names].some(name => !current.has(name))) return {};
|
|
46
|
-
const attachments = source.parts
|
|
47
|
-
.
|
|
48
|
-
.
|
|
62
|
+
const current = new Set(source.parts.slice(1).map((part) => part.name));
|
|
63
|
+
if ([...names].some((name) => !current.has(name))) return {};
|
|
64
|
+
const attachments = source.parts
|
|
65
|
+
.slice(1)
|
|
66
|
+
.filter((part) => !names.has(part.name))
|
|
67
|
+
.map((part) => ({ part: part.part, translate }));
|
|
49
68
|
const attachmentKeys = {};
|
|
50
|
-
for (const attachment of attachments)
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
for (const attachment of attachments)
|
|
70
|
+
attachmentKeys[attachment.part] = await digest(
|
|
71
|
+
JSON.stringify([source.parts[attachment.part].name, translate]),
|
|
72
|
+
);
|
|
53
73
|
return { attachments, attachmentKeys };
|
|
54
74
|
}
|
|
55
75
|
let queue = Promise.resolve();
|
|
@@ -63,7 +83,12 @@ self.onmessage = ({ data }) => {
|
|
|
63
83
|
data.key,
|
|
64
84
|
data.activities,
|
|
65
85
|
);
|
|
66
|
-
|
|
86
|
+
// Native preparation may return a view of temporary storage. Subsequent
|
|
87
|
+
// face/donor reference calls must never overwrite the target scene.
|
|
88
|
+
if (result.bytes) result.bytes = result.bytes.slice();
|
|
89
|
+
if (result.bytes && data.authoredParts?.bodyPreset)
|
|
90
|
+
result.bytes = await composePresetBody(module, result.bytes, data);
|
|
91
|
+
if (result.bytes && data.points) {
|
|
67
92
|
const source = inspectLegacyAssembly(result.bytes);
|
|
68
93
|
const geometryKey = await digest(
|
|
69
94
|
JSON.stringify([source.body.name, data.points]),
|
|
@@ -71,9 +96,43 @@ self.onmessage = ({ data }) => {
|
|
|
71
96
|
result.bytes = deformLegacyAssembly(result.bytes, {
|
|
72
97
|
points: data.points,
|
|
73
98
|
geometryKey,
|
|
74
|
-
...await accessoryTransforms(module, data, source),
|
|
99
|
+
...(await accessoryTransforms(module, data, source)),
|
|
75
100
|
});
|
|
76
101
|
}
|
|
102
|
+
if (result.bytes && data.authoredParts)
|
|
103
|
+
data.faceLayout = shapeFaceLayout(
|
|
104
|
+
authoredAssemblyRecords(result.bytes),
|
|
105
|
+
data,
|
|
106
|
+
module,
|
|
107
|
+
);
|
|
108
|
+
if (
|
|
109
|
+
result.bytes &&
|
|
110
|
+
(data.authoredParts?.shape === 'clippo' ||
|
|
111
|
+
data.authoredParts?.eyes === 'clippo')
|
|
112
|
+
) {
|
|
113
|
+
const { composeClippoAssembly } = await import('./clippo-geometry.mjs');
|
|
114
|
+
result.bytes = await composeClippoAssembly(module, result.bytes, data);
|
|
115
|
+
}
|
|
116
|
+
if (result.bytes && data.authoredParts) {
|
|
117
|
+
result.bytes = await composeCatalogParts(module, result.bytes, data, [
|
|
118
|
+
'eyes',
|
|
119
|
+
'accessory',
|
|
120
|
+
]);
|
|
121
|
+
if (data.authoredParts.eyes === 'cyclops') {
|
|
122
|
+
const { composeCyclopsAssembly } =
|
|
123
|
+
await import('./cyclops-geometry.mjs');
|
|
124
|
+
result.bytes = await composeCyclopsAssembly(
|
|
125
|
+
module,
|
|
126
|
+
result.bytes,
|
|
127
|
+
data,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
result.bytes = await composeCatalogParts(module, result.bytes, data, [
|
|
131
|
+
'eyewear',
|
|
132
|
+
]);
|
|
133
|
+
result.bytes = await fitAuthoredEyeSpacing(result.bytes, data);
|
|
134
|
+
result.bytes = await fitAuthoredHeadwear(result.bytes, data);
|
|
135
|
+
}
|
|
77
136
|
self.postMessage(
|
|
78
137
|
{ id: data.id, ...result },
|
|
79
138
|
result.bytes ? [result.bytes.buffer] : [],
|
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import { renderBudget } from './render-budget.mjs';
|
|
2
|
+
import { characterPixels } from './resolution.mjs';
|
|
2
3
|
|
|
3
4
|
const snapshots = new Map();
|
|
4
5
|
const waiting = new Map();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
64,
|
|
8
|
-
Math.min(
|
|
9
|
-
512,
|
|
10
|
-
Math.round(
|
|
11
|
-
Math.min(canvas.clientWidth, canvas.clientHeight) *
|
|
12
|
-
Math.min(devicePixelRatio || 1, 2),
|
|
13
|
-
),
|
|
14
|
-
),
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
// Presentation lifetime is independent of the GPU lease. Overflow avatars keep
|
|
18
|
-
// their real engine pixels and can reclaim a lease through pointer/command input.
|
|
6
|
+
// Presentation lifetime is independent of the rendering lease. Offscreen and
|
|
7
|
+
// paused avatars retain real engine pixels until they need rendering again.
|
|
19
8
|
export function createManagedAvatar(canvas, initial, callbacks, factory) {
|
|
20
9
|
let props = initial,
|
|
21
10
|
controller,
|
|
@@ -42,7 +31,7 @@ export function createManagedAvatar(canvas, initial, callbacks, factory) {
|
|
|
42
31
|
props.config.character,
|
|
43
32
|
props.legacy,
|
|
44
33
|
props.config.lookAt,
|
|
45
|
-
|
|
34
|
+
characterPixels(canvas),
|
|
46
35
|
]);
|
|
47
36
|
const priority = () =>
|
|
48
37
|
pendingWork || pendingReaction || performance.now() < urgentUntil
|
|
@@ -67,7 +56,8 @@ export function createManagedAvatar(canvas, initial, callbacks, factory) {
|
|
|
67
56
|
refresh();
|
|
68
57
|
}
|
|
69
58
|
function commands() {
|
|
70
|
-
|
|
59
|
+
const state = controller?.diagnostics?.();
|
|
60
|
+
if (disposed || still() || !ready || !state?.ready || state.pending) return;
|
|
71
61
|
if (pendingWork)
|
|
72
62
|
promote(
|
|
73
63
|
2500 * Math.max(1, Math.min(10, props.workingCycles || 1)) + 1000,
|
|
@@ -118,9 +108,13 @@ export function createManagedAvatar(canvas, initial, callbacks, factory) {
|
|
|
118
108
|
for (const receive of waiting.get(cacheKey) ?? []) receive();
|
|
119
109
|
}
|
|
120
110
|
callbacks.onPaint?.();
|
|
111
|
+
// Scope replacements and in-place edits may settle without another
|
|
112
|
+
// onReady event. Deliver retained commands only after their paint.
|
|
113
|
+
if (pendingWork || pendingReaction) queueMicrotask(commands);
|
|
121
114
|
},
|
|
122
115
|
});
|
|
123
116
|
controller = created;
|
|
117
|
+
if (!disposed) callbacks.onPreparationStart?.();
|
|
124
118
|
created.update(props, true);
|
|
125
119
|
return created;
|
|
126
120
|
} finally {
|
|
@@ -178,7 +172,8 @@ export function createManagedAvatar(canvas, initial, callbacks, factory) {
|
|
|
178
172
|
pendingReaction = false;
|
|
179
173
|
}
|
|
180
174
|
appearance();
|
|
181
|
-
const
|
|
175
|
+
const state = controller?.diagnostics?.();
|
|
176
|
+
const queueCommands = !ready || !state?.ready || !!state.pending;
|
|
182
177
|
if (!still() && (work || reaction)) {
|
|
183
178
|
if (queueCommands) {
|
|
184
179
|
pendingWork ||= work;
|