@oxy.so/bloom 7.1.1 → 7.1.3
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/adoption-matrix.mdx +2 -2
- package/docs/agent-avatar.mdx +45 -8
- package/docs/agent-creator.mdx +13 -3
- package/docs/bottom-sheet.mdx +1 -1
- 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/bottom-sheet/BottomSheetBase.js +33 -49
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.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/commonjs/surface/SurfacePaint.js +17 -4
- package/lib/commonjs/surface/SurfacePaint.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/bottom-sheet/BottomSheetBase.js +33 -49
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +1 -1
- package/lib/module/dialog/DialogBottomSheet.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/module/surface/SurfacePaint.js +18 -5
- package/lib/module/surface/SurfacePaint.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/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/types.d.ts +1 -0
- package/lib/typescript/commonjs/bottom-sheet/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/surface/SurfacePaint.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/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/types.d.ts +1 -0
- package/lib/typescript/module/bottom-sheet/types.d.ts.map +1 -1
- package/lib/typescript/module/surface/SurfacePaint.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/bottom-sheet/BottomSheetBase.tsx +24 -34
- package/src/bottom-sheet/types.ts +1 -0
- package/src/dialog/DialogBottomSheet.tsx +1 -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/surface/SurfacePaint.tsx +14 -4
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// Compatibility comes from original native donor meshes, never a fake enabled
|
|
2
|
+
// catalog selection. Source assemblies and the target's activity payload stay
|
|
3
|
+
// in the original renderer.
|
|
4
|
+
import { decodeAppearance, encodeAppearance } from './appearance-codec.mjs';
|
|
5
|
+
import {
|
|
6
|
+
authoredAssemblyRecords,
|
|
7
|
+
authoredEyeRecords,
|
|
8
|
+
assembleAuthoredRecords,
|
|
9
|
+
fitCatalogPartRecords,
|
|
10
|
+
transformAuthoredRecord,
|
|
11
|
+
} from './authored-parts.mjs';
|
|
12
|
+
import { NATIVE_PARTS } from './character-recipe.mjs';
|
|
13
|
+
import { sha256 } from './sha256.mjs';
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
const fail = (message) => {
|
|
16
|
+
throw Error(`Catalog avatar parts: ${message}`);
|
|
17
|
+
};
|
|
18
|
+
const references = new Map();
|
|
19
|
+
let retainedBytes = 0;
|
|
20
|
+
const MAXIMUM_BYTES = 8 * 1024 * 1024;
|
|
21
|
+
const originalPaint = new Map();
|
|
22
|
+
/** Read an unnamed paint from the actual original body material, not a palette approximation. */
|
|
23
|
+
export function originalBodyColor(module, preset) {
|
|
24
|
+
if (originalPaint.has(preset)) return originalPaint.get(preset);
|
|
25
|
+
const result = module.orbitPrepareAssembly(
|
|
26
|
+
module.presetAppearance(preset),
|
|
27
|
+
0,
|
|
28
|
+
`original-paint:${preset}`,
|
|
29
|
+
false,
|
|
30
|
+
);
|
|
31
|
+
if (!result.bytes || result.error)
|
|
32
|
+
fail(result.error || 'original body paint missing');
|
|
33
|
+
const bytes = result.bytes,
|
|
34
|
+
view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
35
|
+
if (
|
|
36
|
+
bytes.length < 48 ||
|
|
37
|
+
view.getUint32(0, true) < 1 ||
|
|
38
|
+
view.getUint32(28, true) !== 1
|
|
39
|
+
)
|
|
40
|
+
fail('unrecognized original body paint layout');
|
|
41
|
+
const rgb = [0, 1, 2].map((k) => view.getFloat32(32 + k * 4, true));
|
|
42
|
+
if (rgb.some((v) => !Number.isFinite(v) || v < 0 || v > 1))
|
|
43
|
+
fail('invalid original body paint');
|
|
44
|
+
const hex =
|
|
45
|
+
'#' +
|
|
46
|
+
rgb
|
|
47
|
+
.map((v) =>
|
|
48
|
+
Math.round(v * 255)
|
|
49
|
+
.toString(16)
|
|
50
|
+
.padStart(2, '0'),
|
|
51
|
+
)
|
|
52
|
+
.join('');
|
|
53
|
+
originalPaint.set(preset, hex);
|
|
54
|
+
return hex;
|
|
55
|
+
}
|
|
56
|
+
/** Editable backing paint, with the original RGB retained for unnamed colors. */
|
|
57
|
+
export function originalPalettePaint(module, preset) {
|
|
58
|
+
const color = decodeAppearance(module.presetAppearance(preset)).color;
|
|
59
|
+
return module.catalog(1).some((item) => item.id === color)
|
|
60
|
+
? { color }
|
|
61
|
+
: { color: 'yellow', bodyColor: originalBodyColor(module, preset) };
|
|
62
|
+
}
|
|
63
|
+
export function recordHasLabel(info, part, label) {
|
|
64
|
+
const needle = encoder.encode(label),
|
|
65
|
+
bytes = info.bytes;
|
|
66
|
+
for (
|
|
67
|
+
let at = part.start + 24;
|
|
68
|
+
at + 4 + needle.length <= part.nameOffset - 4;
|
|
69
|
+
at++
|
|
70
|
+
)
|
|
71
|
+
if (
|
|
72
|
+
new DataView(bytes.buffer, bytes.byteOffset + at, 4).getUint32(
|
|
73
|
+
0,
|
|
74
|
+
true,
|
|
75
|
+
) === needle.length &&
|
|
76
|
+
needle.every((v, i) => bytes[at + 4 + i] === v)
|
|
77
|
+
)
|
|
78
|
+
return true;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
function isCategory(info, part, category) {
|
|
82
|
+
const ids =
|
|
83
|
+
category === 'eyes'
|
|
84
|
+
? [...NATIVE_PARTS.eyes, 'source_integrated_eye', 'brows']
|
|
85
|
+
: NATIVE_PARTS[category].filter((id) => id !== 'none');
|
|
86
|
+
return ids.some((id) => recordHasLabel(info, part, id));
|
|
87
|
+
}
|
|
88
|
+
function record(info, part) {
|
|
89
|
+
return {
|
|
90
|
+
bytes: info.bytes.slice(part.start, part.end),
|
|
91
|
+
name: part.name,
|
|
92
|
+
bounds: part.bounds,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function mappedFaceReference(module, appearance, request) {
|
|
96
|
+
const result = module.orbitPrepareAssembly(
|
|
97
|
+
appearance,
|
|
98
|
+
request.quality,
|
|
99
|
+
`${request.key}:uncovered-donor`,
|
|
100
|
+
false,
|
|
101
|
+
);
|
|
102
|
+
if (!result.bytes || result.error) return null;
|
|
103
|
+
const info = authoredAssemblyRecords(result.bytes.slice());
|
|
104
|
+
return info.records.slice(1).some((part) => isCategory(info, part, 'eyes'))
|
|
105
|
+
? info
|
|
106
|
+
: null;
|
|
107
|
+
}
|
|
108
|
+
async function reference(module, category, id, request) {
|
|
109
|
+
const key = JSON.stringify([category, id, request.quality]);
|
|
110
|
+
if (references.has(key)) {
|
|
111
|
+
const cached = references.get(key);
|
|
112
|
+
references.delete(key);
|
|
113
|
+
references.set(key, cached);
|
|
114
|
+
return cached;
|
|
115
|
+
}
|
|
116
|
+
if (category === 'eyes' && id === 'todd') {
|
|
117
|
+
const result = module.orbitPrepareAssembly(
|
|
118
|
+
module.presetAppearance('lime_frog'),
|
|
119
|
+
request.quality,
|
|
120
|
+
`${request.key}:donor:eyes:todd`,
|
|
121
|
+
false,
|
|
122
|
+
);
|
|
123
|
+
if (!result.bytes || result.error)
|
|
124
|
+
fail(result.error || 'Todd eye donor missing');
|
|
125
|
+
const info = authoredAssemblyRecords(result.bytes.slice());
|
|
126
|
+
if (!authoredEyeRecords(info).length) fail('Todd eye meshes missing');
|
|
127
|
+
return info;
|
|
128
|
+
}
|
|
129
|
+
if (category === 'accessory' && id === 'felipe_beret') {
|
|
130
|
+
const result = module.orbitPrepareAssembly(
|
|
131
|
+
module.presetAppearance('blue_beret'),
|
|
132
|
+
request.quality,
|
|
133
|
+
`${request.key}:donor:accessory:felipe_beret`,
|
|
134
|
+
false,
|
|
135
|
+
);
|
|
136
|
+
if (!result.bytes || result.error)
|
|
137
|
+
fail(result.error || 'Felipe beret donor missing');
|
|
138
|
+
const info = authoredAssemblyRecords(result.bytes.slice());
|
|
139
|
+
if (
|
|
140
|
+
!info.records
|
|
141
|
+
.slice(1)
|
|
142
|
+
.some((part) => recordHasLabel(info, part, 'beret'))
|
|
143
|
+
)
|
|
144
|
+
fail('Felipe beret mesh missing');
|
|
145
|
+
return info;
|
|
146
|
+
}
|
|
147
|
+
const shapes = module.catalog(0).map((item) => item.id),
|
|
148
|
+
eyes =
|
|
149
|
+
category === 'eyes'
|
|
150
|
+
? [id]
|
|
151
|
+
: ['oval', ...NATIVE_PARTS.eyes.filter((id) => id !== 'oval')];
|
|
152
|
+
let donor;
|
|
153
|
+
for (const shape of ['circle', ...shapes.filter((id) => id !== 'circle')]) {
|
|
154
|
+
for (const eye of eyes) {
|
|
155
|
+
const appearance = encodeAppearance({
|
|
156
|
+
version: 1,
|
|
157
|
+
shape,
|
|
158
|
+
color: 'blue',
|
|
159
|
+
eyes: eye,
|
|
160
|
+
eyewear: category === 'eyewear' ? id : 'none',
|
|
161
|
+
accessories: category === 'accessory' ? [id] : [],
|
|
162
|
+
accessoryColors: {},
|
|
163
|
+
constrained: 0,
|
|
164
|
+
depth: 0.5,
|
|
165
|
+
model: null,
|
|
166
|
+
rig: null,
|
|
167
|
+
hereCharacter: null,
|
|
168
|
+
});
|
|
169
|
+
const result = module.orbitPrepareAssembly(
|
|
170
|
+
appearance,
|
|
171
|
+
request.quality,
|
|
172
|
+
`${request.key}:donor:${category}:${id}:${shape}:${eye}`,
|
|
173
|
+
false,
|
|
174
|
+
);
|
|
175
|
+
if (!result.bytes || result.error) continue;
|
|
176
|
+
let info = authoredAssemblyRecords(result.bytes.slice());
|
|
177
|
+
if (!info.records.slice(1).some((part) => recordHasLabel(info, part, id)))
|
|
178
|
+
continue;
|
|
179
|
+
if (
|
|
180
|
+
!info.records.slice(1).some((part) => isCategory(info, part, 'eyes'))
|
|
181
|
+
) {
|
|
182
|
+
const exposed = decodeAppearance(appearance);
|
|
183
|
+
exposed.eyewear = 'none';
|
|
184
|
+
const face = mappedFaceReference(
|
|
185
|
+
module,
|
|
186
|
+
encodeAppearance(exposed),
|
|
187
|
+
request,
|
|
188
|
+
);
|
|
189
|
+
if (!face) continue;
|
|
190
|
+
const records = info.records.map((part) => record(info, part));
|
|
191
|
+
records.splice(
|
|
192
|
+
1,
|
|
193
|
+
0,
|
|
194
|
+
...face.records
|
|
195
|
+
.slice(1)
|
|
196
|
+
.filter((part) => isCategory(face, part, 'eyes'))
|
|
197
|
+
.map((part) => record(face, part)),
|
|
198
|
+
);
|
|
199
|
+
info = authoredAssemblyRecords(
|
|
200
|
+
await assembleAuthoredRecords(info, records),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
donor = info;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (donor) break;
|
|
207
|
+
}
|
|
208
|
+
if (!donor) fail(`no original donor for ${category}:${id}`);
|
|
209
|
+
if (donor.bytes.length <= MAXIMUM_BYTES) {
|
|
210
|
+
references.set(key, donor);
|
|
211
|
+
retainedBytes += donor.bytes.length;
|
|
212
|
+
while (references.size > 24 || retainedBytes > MAXIMUM_BYTES) {
|
|
213
|
+
const first = references.keys().next().value;
|
|
214
|
+
retainedBytes -= references.get(first).bytes.length;
|
|
215
|
+
references.delete(first);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return donor;
|
|
219
|
+
}
|
|
220
|
+
/** Keep actual named-preset geometry/default pieces when only parts or paint
|
|
221
|
+
* change. Original untouched presets never enter this adapter. */
|
|
222
|
+
export async function composePresetBody(module, bytes, request) {
|
|
223
|
+
const preset = request.authoredParts?.bodyPreset;
|
|
224
|
+
if (!preset) return bytes;
|
|
225
|
+
const prepared = module.orbitPrepareAssembly(
|
|
226
|
+
module.presetAppearance(preset),
|
|
227
|
+
request.quality,
|
|
228
|
+
`${request.key}:body:${preset}`,
|
|
229
|
+
request.activities,
|
|
230
|
+
);
|
|
231
|
+
if (!prepared.bytes || prepared.error)
|
|
232
|
+
fail(prepared.error || 'original preset body missing');
|
|
233
|
+
const source = authoredAssemblyRecords(prepared.bytes.slice()),
|
|
234
|
+
target = authoredAssemblyRecords(bytes),
|
|
235
|
+
records = source.records.map((part) => record(source, part));
|
|
236
|
+
if (
|
|
237
|
+
!source.records.slice(1).some((part) => isCategory(source, part, 'eyes'))
|
|
238
|
+
) {
|
|
239
|
+
// Opaque sunglasses legitimately omit the original eye meshes. Recover the
|
|
240
|
+
// engine's actual hidden-eye reference without changing the preserved body.
|
|
241
|
+
const exposed = decodeAppearance(module.presetAppearance(preset));
|
|
242
|
+
exposed.hereCharacter = null;
|
|
243
|
+
exposed.eyewear = 'none';
|
|
244
|
+
if (exposed.model) exposed.model.eyewear = null;
|
|
245
|
+
const result = module.orbitPrepareAssembly(
|
|
246
|
+
encodeAppearance(exposed),
|
|
247
|
+
request.quality,
|
|
248
|
+
`${request.key}:hidden-eyes:${preset}`,
|
|
249
|
+
false,
|
|
250
|
+
);
|
|
251
|
+
if (!result.bytes || result.error)
|
|
252
|
+
fail(result.error || 'hidden original eye reference missing');
|
|
253
|
+
const face = authoredAssemblyRecords(result.bytes.slice()),
|
|
254
|
+
eyes = face.records
|
|
255
|
+
.slice(1)
|
|
256
|
+
.filter((part) => isCategory(face, part, 'eyes'));
|
|
257
|
+
if (!eyes.length) fail('hidden original eye classification missing');
|
|
258
|
+
records.splice(1, 0, ...eyes.map((part) => record(face, part)));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (request.authoredParts.paintBody) {
|
|
262
|
+
const out = new DataView(records[0].bytes.buffer),
|
|
263
|
+
sourceView = new DataView(source.bytes.buffer, source.bytes.byteOffset),
|
|
264
|
+
targetView = new DataView(target.bytes.buffer, target.bytes.byteOffset);
|
|
265
|
+
if (
|
|
266
|
+
sourceView.getUint32(source.body.start + 24, true) !== 1 ||
|
|
267
|
+
targetView.getUint32(target.body.start + 24, true) !== 1
|
|
268
|
+
)
|
|
269
|
+
fail('unrecognized body paint layout');
|
|
270
|
+
for (const at of [28, 60])
|
|
271
|
+
for (let k = 0; k < 3; k++)
|
|
272
|
+
out.setFloat32(
|
|
273
|
+
at + k * 4,
|
|
274
|
+
targetView.getFloat32(target.body.start + at + k * 4, true),
|
|
275
|
+
true,
|
|
276
|
+
);
|
|
277
|
+
const hash = await sha256(records[0].bytes);
|
|
278
|
+
records[0].bytes.set(
|
|
279
|
+
encoder.encode(hash),
|
|
280
|
+
source.body.nameOffset - source.body.start,
|
|
281
|
+
);
|
|
282
|
+
records[0].name = hash + source.body.name.slice(64);
|
|
283
|
+
}
|
|
284
|
+
// Preserve the body's actual authored activity payload and fitted hands,
|
|
285
|
+
// instead of borrowing the neutral backing circle's fit.
|
|
286
|
+
const footer = source.footer.slice(),
|
|
287
|
+
view = new DataView(footer.buffer);
|
|
288
|
+
for (let k = 0; k < 3; k++) {
|
|
289
|
+
view.setFloat32(28 + k * 4, source.body.bounds.min[k], true);
|
|
290
|
+
view.setFloat32(40 + k * 4, source.body.bounds.max[k], true);
|
|
291
|
+
}
|
|
292
|
+
return assembleAuthoredRecords(
|
|
293
|
+
{ ...source, body: { ...source.body, name: records[0].name }, footer },
|
|
294
|
+
records,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
/** Native catalog pieces can be transplanted onto any actual body. */
|
|
298
|
+
export async function composeCatalogParts(
|
|
299
|
+
module,
|
|
300
|
+
bytes,
|
|
301
|
+
request,
|
|
302
|
+
categories = ['eyes', 'eyewear', 'accessory'],
|
|
303
|
+
) {
|
|
304
|
+
let info = authoredAssemblyRecords(bytes);
|
|
305
|
+
for (const category of categories) {
|
|
306
|
+
const id = request.authoredParts?.[category];
|
|
307
|
+
if (!id) continue;
|
|
308
|
+
// An authored body's own eye style already has its exact sculpted layout.
|
|
309
|
+
if (
|
|
310
|
+
category === 'eyes' &&
|
|
311
|
+
request.authoredParts.bodyPreset &&
|
|
312
|
+
authoredEyeRecords(info).length &&
|
|
313
|
+
authoredEyeRecords(info).every((p) => recordHasLabel(info, p, id))
|
|
314
|
+
)
|
|
315
|
+
continue;
|
|
316
|
+
const virtual =
|
|
317
|
+
(category === 'accessory' && id === 'felipe_beret') ||
|
|
318
|
+
(category === 'eyes' && id === 'todd');
|
|
319
|
+
if (!NATIVE_PARTS[category].includes(id) && !virtual) continue;
|
|
320
|
+
const records = info.records.map((part) => record(info, part));
|
|
321
|
+
const removed = new Set(
|
|
322
|
+
info.records
|
|
323
|
+
.slice(1)
|
|
324
|
+
.filter((part) => isCategory(info, part, category))
|
|
325
|
+
.map((part) => part.part),
|
|
326
|
+
);
|
|
327
|
+
if (category === 'eyes' && !removed.size)
|
|
328
|
+
fail('target face classification missing');
|
|
329
|
+
let fitted = [];
|
|
330
|
+
if (id !== 'none') {
|
|
331
|
+
const donor = await reference(module, category, id, request);
|
|
332
|
+
const transforms = fitCatalogPartRecords(
|
|
333
|
+
info,
|
|
334
|
+
donor,
|
|
335
|
+
category,
|
|
336
|
+
id === 'felipe_beret' ? 'beret' : id,
|
|
337
|
+
{
|
|
338
|
+
singleEye: request.authoredParts?.eyes === 'cyclops',
|
|
339
|
+
faceLayout: request.faceLayout,
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
fitted = await Promise.all(
|
|
343
|
+
transforms.map(({ part, ...transform }) =>
|
|
344
|
+
transformAuthoredRecord(donor.bytes, part, transform),
|
|
345
|
+
),
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
const kept = records.filter((_, index) => !removed.has(index));
|
|
349
|
+
const at = category === 'eyes' ? 1 : kept.length;
|
|
350
|
+
kept.splice(at, 0, ...fitted);
|
|
351
|
+
bytes = await assembleAuthoredRecords(info, kept);
|
|
352
|
+
info = authoredAssemblyRecords(bytes);
|
|
353
|
+
}
|
|
354
|
+
return bytes;
|
|
355
|
+
}
|
|
356
|
+
export function catalogPartCacheStats() {
|
|
357
|
+
return {
|
|
358
|
+
entries: references.size,
|
|
359
|
+
bytes: retainedBytes,
|
|
360
|
+
maximumBytes: MAXIMUM_BYTES,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/** Resolve editable defaults without passing virtual catalog IDs to the engine. */
|
|
2
|
+
import { MIGRATED_CONTOURS } from './migrated-contours.mjs';
|
|
3
|
+
export function isLegacyRecipe(props) {
|
|
4
|
+
return !props.config.character || props.config.character.preset === 'bloom';
|
|
5
|
+
}
|
|
6
|
+
export const MIGRATED_SHAPE_IDS = Object.freeze([
|
|
7
|
+
'slender',
|
|
8
|
+
'pocket',
|
|
9
|
+
'petal',
|
|
10
|
+
'star',
|
|
11
|
+
'cloud',
|
|
12
|
+
'shield',
|
|
13
|
+
'pebble',
|
|
14
|
+
'squircle',
|
|
15
|
+
]);
|
|
16
|
+
export function withCharacterGeometry(props) {
|
|
17
|
+
const shape = props.config.character?.selections?.shape;
|
|
18
|
+
if (!Object.hasOwn(MIGRATED_CONTOURS, shape)) return props;
|
|
19
|
+
return {
|
|
20
|
+
...props,
|
|
21
|
+
legacy: props.legacy?.points
|
|
22
|
+
? props.legacy
|
|
23
|
+
: { points: MIGRATED_CONTOURS[shape] },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const PRESET_DEFAULTS = Object.freeze({
|
|
27
|
+
blue_beret: {
|
|
28
|
+
shape: 'six_lobed_flower',
|
|
29
|
+
eyes: 'oval',
|
|
30
|
+
eyewear: 'none',
|
|
31
|
+
accessory: 'felipe_beret',
|
|
32
|
+
},
|
|
33
|
+
alfred: {
|
|
34
|
+
shape: 'rounded_triangle',
|
|
35
|
+
eyes: 'sleepy_lids',
|
|
36
|
+
eyewear: 'tall_oval_frames',
|
|
37
|
+
accessory: 'bow',
|
|
38
|
+
},
|
|
39
|
+
purple_heart: {
|
|
40
|
+
shape: 'heart',
|
|
41
|
+
eyes: 'oval',
|
|
42
|
+
eyewear: 'round_sunglasses',
|
|
43
|
+
accessory: 'none',
|
|
44
|
+
},
|
|
45
|
+
lime_frog: {
|
|
46
|
+
shape: 'rounded_head_two_ears',
|
|
47
|
+
eyes: 'todd',
|
|
48
|
+
eyewear: 'none',
|
|
49
|
+
accessory: 'bow',
|
|
50
|
+
},
|
|
51
|
+
coral_monocle: {
|
|
52
|
+
shape: 'twelve_scalloped_rosette',
|
|
53
|
+
eyes: 'double_highlights',
|
|
54
|
+
eyewear: 'monocle',
|
|
55
|
+
accessory: 'none',
|
|
56
|
+
},
|
|
57
|
+
gus: {
|
|
58
|
+
shape: 'rounded_diamond',
|
|
59
|
+
eyes: 'oval',
|
|
60
|
+
eyewear: 'classic_sunglasses',
|
|
61
|
+
accessory: 'orb',
|
|
62
|
+
},
|
|
63
|
+
blue_spectacles: {
|
|
64
|
+
shape: 'rounded_cube',
|
|
65
|
+
eyes: 'crescent_inset',
|
|
66
|
+
eyewear: 'tall_oval_frames',
|
|
67
|
+
accessory: 'three_lobe',
|
|
68
|
+
},
|
|
69
|
+
lime_headphones: {
|
|
70
|
+
shape: 'circle_two_ears',
|
|
71
|
+
eyes: 'swept_lids',
|
|
72
|
+
eyewear: 'none',
|
|
73
|
+
accessory: 'headphones',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
export function characterRecipe(props) {
|
|
77
|
+
if (props.legacy && isLegacyRecipe(props))
|
|
78
|
+
return {
|
|
79
|
+
preset: 'legacy',
|
|
80
|
+
selections: {
|
|
81
|
+
shape: props.legacy.shape ?? 'circle',
|
|
82
|
+
eyes: props.legacy.eyes ?? 'oval',
|
|
83
|
+
...props.legacy.selections,
|
|
84
|
+
},
|
|
85
|
+
...props.legacy.patch,
|
|
86
|
+
...(props.legacy.eyeSpacing !== undefined
|
|
87
|
+
? { eyeSpacing: props.legacy.eyeSpacing }
|
|
88
|
+
: {}),
|
|
89
|
+
};
|
|
90
|
+
const recipe = props.config.character;
|
|
91
|
+
if (recipe?.preset !== 'clippo') return recipe;
|
|
92
|
+
return {
|
|
93
|
+
...recipe,
|
|
94
|
+
selections: { shape: 'clippo', eyes: 'clippo', ...recipe.selections },
|
|
95
|
+
// A palette selection is an explicit replacement of the preset's grey.
|
|
96
|
+
...(!recipe.bodyColor && !recipe.selections?.color
|
|
97
|
+
? { bodyColor: '#999b9d' }
|
|
98
|
+
: {}),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function authoredPartsFor(props) {
|
|
103
|
+
const recipe = characterRecipe(props);
|
|
104
|
+
const selections = recipe?.selections ?? {};
|
|
105
|
+
const customized = Boolean(
|
|
106
|
+
props.legacy || recipe?.bodyColor || Object.keys(selections).length,
|
|
107
|
+
);
|
|
108
|
+
const defaults = !isLegacyRecipe(props)
|
|
109
|
+
? PRESET_DEFAULTS[recipe.preset]
|
|
110
|
+
: undefined;
|
|
111
|
+
const selectedShape = selections.shape ?? defaults?.shape;
|
|
112
|
+
const shape =
|
|
113
|
+
selectedShape === 'todd' ? 'rounded_head_two_ears' : selectedShape;
|
|
114
|
+
const eyes =
|
|
115
|
+
selections.eyes ??
|
|
116
|
+
defaults?.eyes ??
|
|
117
|
+
(customized && recipe?.preset === 'lime_frog' ? 'todd' : undefined);
|
|
118
|
+
const accessory =
|
|
119
|
+
selections.accessory ??
|
|
120
|
+
defaults?.accessory ??
|
|
121
|
+
(customized && recipe?.preset === 'blue_beret'
|
|
122
|
+
? 'felipe_beret'
|
|
123
|
+
: undefined);
|
|
124
|
+
if (!customized) return {};
|
|
125
|
+
const bodyPreset =
|
|
126
|
+
!shape &&
|
|
127
|
+
!isLegacyRecipe(props) &&
|
|
128
|
+
ORIGINAL_PRESETS.includes(recipe?.preset)
|
|
129
|
+
? recipe.preset
|
|
130
|
+
: undefined;
|
|
131
|
+
return {
|
|
132
|
+
...(bodyPreset ? { bodyPreset } : {}),
|
|
133
|
+
...(bodyPreset &&
|
|
134
|
+
(recipe.bodyColor || selections.color || bodyPreset !== recipe.preset)
|
|
135
|
+
? { paintBody: true }
|
|
136
|
+
: {}),
|
|
137
|
+
...(selections.shape === 'clippo' ? { shape: 'clippo' } : {}),
|
|
138
|
+
...((!isLegacyRecipe(props) || !props.legacy?.points) &&
|
|
139
|
+
(MIGRATED_SHAPE_IDS.includes(shape) || NATIVE_PARTS.shape.includes(shape))
|
|
140
|
+
? { faceShape: shape }
|
|
141
|
+
: {}),
|
|
142
|
+
...(eyes ? { eyes } : {}),
|
|
143
|
+
...((selections.eyewear ?? defaults?.eyewear)
|
|
144
|
+
? { eyewear: selections.eyewear ?? defaults?.eyewear }
|
|
145
|
+
: {}),
|
|
146
|
+
...(accessory ? { accessory } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export const ORIGINAL_PRESETS = Object.freeze([
|
|
150
|
+
'blue_beret',
|
|
151
|
+
'alfred',
|
|
152
|
+
'purple_heart',
|
|
153
|
+
'lime_frog',
|
|
154
|
+
'coral_monocle',
|
|
155
|
+
'gus',
|
|
156
|
+
'blue_spectacles',
|
|
157
|
+
'lime_headphones',
|
|
158
|
+
]);
|
|
159
|
+
// Exact shapes reported by the original engine's eight named appearances.
|
|
160
|
+
const BODY_SIGNATURES = Object.freeze({
|
|
161
|
+
six_lobed_flower: 'blue_beret',
|
|
162
|
+
rounded_triangle: 'alfred',
|
|
163
|
+
heart: 'purple_heart',
|
|
164
|
+
rounded_head_two_ears: 'lime_frog',
|
|
165
|
+
twelve_scalloped_rosette: 'coral_monocle',
|
|
166
|
+
rounded_diamond: 'gus',
|
|
167
|
+
rounded_cube: 'blue_spectacles',
|
|
168
|
+
circle_two_ears: 'lime_headphones',
|
|
169
|
+
todd: 'lime_frog',
|
|
170
|
+
});
|
|
171
|
+
/** The body's authored reaction follows its geometry, independently of its pieces. */
|
|
172
|
+
export function bodySignatureFor(props) {
|
|
173
|
+
const recipe = characterRecipe(props),
|
|
174
|
+
parts = authoredPartsFor(props);
|
|
175
|
+
if (parts.bodyPreset) return parts.bodyPreset;
|
|
176
|
+
if (
|
|
177
|
+
!isLegacyRecipe(props) &&
|
|
178
|
+
!recipe.selections?.shape &&
|
|
179
|
+
ORIGINAL_PRESETS.includes(recipe.preset)
|
|
180
|
+
)
|
|
181
|
+
return recipe.preset;
|
|
182
|
+
// Distinct migrated contours and Clippo use Todd's original signature as the
|
|
183
|
+
// shared standard; they never fall back to the original Wave/maracas.
|
|
184
|
+
if (props.legacy?.points) return 'lime_frog';
|
|
185
|
+
return (
|
|
186
|
+
BODY_SIGNATURES[
|
|
187
|
+
recipe.selections?.shape ?? PRESET_DEFAULTS[recipe.preset]?.shape
|
|
188
|
+
] ?? 'lime_frog'
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
/** Prepared authored bodies retain their actual defaults, not the fitting scene's catalog selections. */
|
|
192
|
+
export function bodySelectionDefaults(preset, appearance) {
|
|
193
|
+
return {
|
|
194
|
+
shape: appearance.shape,
|
|
195
|
+
eyes: preset === 'lime_frog' ? 'todd' : appearance.eyes,
|
|
196
|
+
eyewear: appearance.eyewear ?? 'none',
|
|
197
|
+
accessory:
|
|
198
|
+
preset === 'blue_beret'
|
|
199
|
+
? 'felipe_beret'
|
|
200
|
+
: (appearance.accessories?.[0] ?? 'none'),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
export const NATIVE_PARTS = Object.freeze({
|
|
204
|
+
shape: Object.freeze([
|
|
205
|
+
'circle',
|
|
206
|
+
'rounded_triangle',
|
|
207
|
+
'capsule',
|
|
208
|
+
'rounded_head_two_ears',
|
|
209
|
+
'six_lobed_flower',
|
|
210
|
+
'heart',
|
|
211
|
+
'four_lobed_butterfly',
|
|
212
|
+
'circle_two_ears',
|
|
213
|
+
'twelve_scalloped_rosette',
|
|
214
|
+
'rounded_cube',
|
|
215
|
+
'rounded_diamond',
|
|
216
|
+
]),
|
|
217
|
+
eyes: Object.freeze([
|
|
218
|
+
'swept_lids',
|
|
219
|
+
'oval',
|
|
220
|
+
'sparkle_capsules',
|
|
221
|
+
'highlight_capsules',
|
|
222
|
+
'dots',
|
|
223
|
+
'double_highlights',
|
|
224
|
+
'round_inset',
|
|
225
|
+
'crescent_inset',
|
|
226
|
+
'sleepy_lids',
|
|
227
|
+
]),
|
|
228
|
+
eyewear: Object.freeze([
|
|
229
|
+
'none',
|
|
230
|
+
'monocle',
|
|
231
|
+
'tall_oval_frames',
|
|
232
|
+
'separate_trapezoid_lenses',
|
|
233
|
+
'classic_sunglasses',
|
|
234
|
+
'round_sunglasses',
|
|
235
|
+
]),
|
|
236
|
+
accessory: Object.freeze([
|
|
237
|
+
'none',
|
|
238
|
+
'headphones',
|
|
239
|
+
'bow',
|
|
240
|
+
'beanie',
|
|
241
|
+
'hat',
|
|
242
|
+
'beret',
|
|
243
|
+
'orb',
|
|
244
|
+
'three_lobe',
|
|
245
|
+
'crown',
|
|
246
|
+
]),
|
|
247
|
+
});
|