talking-head-studio 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -337
- package/dist/TalkingHead.d.ts +44 -28
- package/dist/TalkingHead.js +21 -2
- package/dist/TalkingHead.web.d.ts +37 -4
- package/dist/TalkingHead.web.js +28 -8
- package/dist/TalkingHeadVisualization.d.ts +22 -0
- package/dist/TalkingHeadVisualization.js +30 -10
- package/dist/api/studioApi.d.ts +12 -1
- package/dist/api/studioApi.js +41 -28
- package/dist/appearance/apply.js +2 -3
- package/dist/appearance/matchers.js +1 -2
- package/dist/appearance/schema.js +1 -2
- package/dist/contract.d.ts +14 -0
- package/dist/contract.js +30 -0
- package/dist/core/avatar/avatarCapabilities.d.ts +60 -0
- package/dist/core/avatar/avatarCapabilities.js +100 -0
- package/dist/core/avatar/backend.d.ts +130 -0
- package/dist/core/avatar/backend.js +4 -0
- package/dist/core/avatar/backends/gaussian.d.ts +49 -0
- package/dist/core/avatar/backends/gaussian.js +293 -0
- package/dist/core/avatar/backends/index.d.ts +3 -0
- package/dist/core/avatar/backends/index.js +7 -0
- package/dist/core/avatar/backends/morphTarget.d.ts +39 -0
- package/dist/core/avatar/backends/morphTarget.js +179 -0
- package/dist/core/avatar/faceControls.d.ts +40 -0
- package/dist/core/avatar/faceControls.js +138 -0
- package/dist/core/avatar/motion.d.ts +1713 -0
- package/dist/core/avatar/motion.js +550 -0
- package/dist/core/avatar/motionRuntime.d.ts +46 -0
- package/dist/core/avatar/motionRuntime.js +84 -0
- package/dist/core/avatar/schema.d.ts +78 -0
- package/dist/core/avatar/schema.js +134 -0
- package/dist/core/avatar/visemes.d.ts +47 -1
- package/dist/core/avatar/visemes.js +114 -1
- package/dist/editor/AvatarCanvas.js +93 -3
- package/dist/editor/AvatarEditor.native.js +19 -9
- package/dist/editor/AvatarModel.js +2 -2
- package/dist/editor/FaceSqueezeEditor.d.ts +3 -1
- package/dist/editor/FaceSqueezeEditor.js +195 -121
- package/dist/editor/FaceSqueezeEditor.web.d.ts +3 -1
- package/dist/editor/FaceSqueezeEditor.web.js +32 -30
- package/dist/editor/RigidAccessory.js +18 -4
- package/dist/editor/SkinnedClothing.js +19 -9
- package/dist/editor/boneLockedDrag.d.ts +11 -0
- package/dist/editor/boneLockedDrag.js +68 -0
- package/dist/editor/boneSnap.js +22 -12
- package/dist/editor/boneSnap.web.d.ts +27 -0
- package/dist/editor/boneSnap.web.js +99 -0
- package/dist/editor/index.web.d.ts +10 -0
- package/dist/editor/index.web.js +26 -0
- package/dist/editor/sounds/haha.wav +0 -0
- package/dist/editor/sounds/owie.wav +0 -0
- package/dist/editor/sounds/stop.wav +0 -0
- package/dist/editor/studioTheme.d.ts +14 -14
- package/dist/editor/studioTheme.js +19 -16
- package/dist/editor/types.d.ts +1 -0
- package/dist/html/accessories.d.ts +7 -0
- package/dist/html/accessories.js +149 -0
- package/dist/html/motion.d.ts +1 -0
- package/dist/html/motion.js +189 -0
- package/dist/html/visemes.d.ts +7 -0
- package/dist/html/visemes.js +348 -0
- package/dist/html.d.ts +1 -1
- package/dist/html.js +56 -734
- package/dist/index.d.ts +19 -1
- package/dist/index.js +44 -5
- package/dist/index.web.d.ts +18 -1
- package/dist/index.web.js +36 -3
- package/dist/platform/api/types.d.ts +10 -0
- package/dist/platform/api/types.js +2 -0
- package/dist/platform/marketplace/types.d.ts +32 -0
- package/dist/platform/marketplace/types.js +2 -0
- package/dist/platform/sdk/unity.d.ts +27 -0
- package/dist/platform/sdk/unity.js +2 -0
- package/dist/platform/sdk/unreal.d.ts +23 -0
- package/dist/platform/sdk/unreal.js +2 -0
- package/dist/platform/sdk/web.d.ts +16 -0
- package/dist/platform/sdk/web.js +2 -0
- package/dist/sketchfab/api.js +5 -5
- package/dist/sketchfab/glbInspect.d.ts +22 -0
- package/dist/sketchfab/glbInspect.js +58 -0
- package/dist/sketchfab/index.d.ts +3 -0
- package/dist/sketchfab/index.js +8 -1
- package/dist/sketchfab/inspectRemote.d.ts +13 -0
- package/dist/sketchfab/inspectRemote.js +77 -0
- package/dist/sketchfab/types.d.ts +10 -0
- package/dist/sketchfab/useSketchfabSearch.js +1 -2
- package/dist/studio/AccessoryBrowserScreen.d.ts +6 -0
- package/dist/studio/AccessoryBrowserScreen.js +626 -0
- package/dist/studio/AccessoryPanel.d.ts +10 -0
- package/dist/studio/AccessoryPanel.js +396 -0
- package/dist/studio/AppearancePanel.d.ts +9 -0
- package/dist/studio/AppearancePanel.js +77 -0
- package/dist/studio/AvatarCreatorScreen.d.ts +5 -0
- package/dist/studio/AvatarCreatorScreen.js +806 -0
- package/dist/studio/AvatarEditorScreen.d.ts +14 -0
- package/dist/studio/AvatarEditorScreen.js +510 -0
- package/dist/studio/AvatarGrid.d.ts +23 -0
- package/dist/studio/AvatarGrid.js +257 -0
- package/dist/studio/ColorSwatch.d.ts +8 -0
- package/dist/studio/ColorSwatch.js +100 -0
- package/dist/studio/CreateVoiceProfileSheet.d.ts +8 -0
- package/dist/studio/CreateVoiceProfileSheet.js +242 -0
- package/dist/studio/DetailsPanel.d.ts +15 -0
- package/dist/studio/DetailsPanel.js +239 -0
- package/dist/studio/FilamentEditor.d.ts +2 -0
- package/dist/studio/FilamentEditor.js +6 -0
- package/dist/studio/PrecisionPanel.d.ts +2 -0
- package/dist/studio/PrecisionPanel.js +7 -0
- package/dist/studio/PublicGalleryScreen.d.ts +5 -0
- package/dist/studio/PublicGalleryScreen.js +358 -0
- package/dist/studio/SketchfabModelCard.d.ts +20 -0
- package/dist/studio/SketchfabModelCard.js +104 -0
- package/dist/studio/StudioBrowseHeader.d.ts +9 -0
- package/dist/studio/StudioBrowseHeader.js +28 -0
- package/dist/studio/StudioEmptyState.d.ts +8 -0
- package/dist/studio/StudioEmptyState.js +29 -0
- package/dist/studio/StudioFloatingAction.d.ts +13 -0
- package/dist/studio/StudioFloatingAction.js +42 -0
- package/dist/studio/StudioSectionHeader.d.ts +7 -0
- package/dist/studio/StudioSectionHeader.js +27 -0
- package/dist/studio/StudioSurfaceCard.d.ts +8 -0
- package/dist/studio/StudioSurfaceCard.js +20 -0
- package/dist/studio/VoicePanel.d.ts +15 -0
- package/dist/studio/VoicePanel.js +305 -0
- package/dist/studio/constants.d.ts +3 -0
- package/dist/studio/constants.js +6 -0
- package/dist/studio/index.d.ts +29 -0
- package/dist/studio/index.js +54 -0
- package/dist/studio/useSketchfabCapabilities.d.ts +31 -0
- package/dist/studio/useSketchfabCapabilities.js +82 -0
- package/dist/tts/useDirectVisemeStream.d.ts +2 -6
- package/dist/tts/useDirectVisemeStream.js +16 -12
- package/dist/tts/useMotionMarkers.d.ts +0 -1
- package/dist/tts/useMotionMarkers.js +1 -2
- package/dist/utils/avatarUtils.js +94 -8
- package/dist/utils/faceLandmarkerToShapeWeights.js +21 -14
- package/dist/voice/convertToWav.js +1 -2
- package/dist/voice/index.d.ts +3 -0
- package/dist/voice/index.js +6 -1
- package/dist/voice/useAudioPlayer.js +18 -6
- package/dist/voice/useAudioRecording.js +1 -2
- package/dist/voice/useFaceControls.d.ts +14 -0
- package/dist/voice/useFaceControls.js +81 -0
- package/dist/voice/useVoicePreview.d.ts +7 -0
- package/dist/voice/useVoicePreview.js +83 -0
- package/dist/wardrobe/index.d.ts +3 -0
- package/dist/wardrobe/index.js +8 -1
- package/dist/wardrobe/useAccessoryGestures.d.ts +20 -0
- package/dist/wardrobe/useAccessoryGestures.js +94 -0
- package/dist/wardrobe/useAvatarWardrobeHydration.js +9 -4
- package/dist/wardrobe/useStudioAvatar.d.ts +29 -0
- package/dist/wardrobe/useStudioAvatar.js +186 -0
- package/dist/wardrobe/wardrobeStore.d.ts +2 -0
- package/dist/wardrobe/wardrobeStore.js +12 -2
- package/dist/wgpu/R3FWebGpuCanvas.d.ts +15 -0
- package/dist/wgpu/R3FWebGpuCanvas.js +176 -0
- package/dist/wgpu/WgpuAvatar.d.ts +26 -2
- package/dist/wgpu/WgpuAvatar.js +313 -46
- package/dist/wgpu/accessoryDefaults.d.ts +12 -0
- package/dist/wgpu/accessoryDefaults.js +19 -0
- package/dist/wgpu/blobShim.d.ts +2 -0
- package/dist/wgpu/blobShim.js +191 -0
- package/dist/wgpu/index.d.ts +1 -0
- package/dist/wgpu/index.js +4 -1
- package/dist/wgpu/loadGLTFFromUri.d.ts +2 -0
- package/dist/wgpu/loadGLTFFromUri.js +75 -0
- package/dist/wgpu/morphTables.js +21 -10
- package/dist/wgpu/motionState.d.ts +20 -0
- package/dist/wgpu/motionState.js +31 -0
- package/dist/wgpu/patchThreeForRN.d.ts +28 -0
- package/dist/wgpu/patchThreeForRN.js +292 -0
- package/dist/wgpu/scenePlacement.d.ts +5 -0
- package/dist/wgpu/scenePlacement.js +50 -0
- package/dist/wgpu/useAuthedModelUri.js +22 -11
- package/dist/wgpu/useNativeGLTF.d.ts +7 -0
- package/dist/wgpu/useNativeGLTF.js +36 -0
- package/package.json +102 -32
|
@@ -15,15 +15,26 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
36
|
+
exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
|
|
37
|
+
exports.FaceSqueezeEditor = FaceSqueezeEditor;
|
|
27
38
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
28
39
|
/**
|
|
29
40
|
* WgpuFaceSqueezeEditor — R3F/Three.js port of FaceSqueezeEditor.
|
|
@@ -37,18 +48,21 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
37
48
|
* Drop-in replacement: same FaceSqueezeEditorProps interface.
|
|
38
49
|
* The .web.tsx stub is shared — import from FaceSqueezeEditor.web.tsx.
|
|
39
50
|
*/
|
|
51
|
+
require("../wgpu/blobShim");
|
|
40
52
|
const react_1 = require("react");
|
|
41
53
|
const react_native_1 = require("react-native");
|
|
42
|
-
const
|
|
43
|
-
const native_2 = require("@react-three/drei/native");
|
|
54
|
+
const fiber_1 = require("@react-three/fiber");
|
|
44
55
|
const THREE = __importStar(require("three"));
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const expo_asset_1 = require("expo-asset");
|
|
56
|
+
const avatarUtils_1 = require("../utils/avatarUtils");
|
|
57
|
+
const R3FWebGpuCanvas_1 = require("../wgpu/R3FWebGpuCanvas");
|
|
58
|
+
const patchThreeForRN_1 = require("../wgpu/patchThreeForRN");
|
|
59
|
+
const useNativeGLTF_1 = require("../wgpu/useNativeGLTF");
|
|
50
60
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
51
61
|
exports.FACE_SQUEEZE_LOCAL_MODULE = require('../assets/face-squeeze-local.glb');
|
|
62
|
+
(0, patchThreeForRN_1.installThreeRNPatches)(false);
|
|
63
|
+
function emitHaptic() {
|
|
64
|
+
// Optional in the example path.
|
|
65
|
+
}
|
|
52
66
|
// ---------------------------------------------------------------------------
|
|
53
67
|
// Constants (identical to Filament version)
|
|
54
68
|
// ---------------------------------------------------------------------------
|
|
@@ -56,17 +70,6 @@ const SPRING_DURATION_MS = 300;
|
|
|
56
70
|
const EMOTION_HOLD_MS = 900;
|
|
57
71
|
const IDLE_EMOTION_INTERVAL_MS = 30000;
|
|
58
72
|
const INITIAL_IDLE_EMOTION_DELAY_MS = 1200;
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
60
|
-
const SOUND_OWIE = require('./sounds/owie.wav');
|
|
61
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
62
|
-
const SOUND_STOP = require('./sounds/stop.wav');
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
64
|
-
const SOUND_HAHA = require('./sounds/haha.wav');
|
|
65
|
-
const REACTION_SOUNDS = [
|
|
66
|
-
SOUND_OWIE,
|
|
67
|
-
SOUND_STOP,
|
|
68
|
-
SOUND_HAHA,
|
|
69
|
-
];
|
|
70
73
|
const EMOTION_PRESETS = {
|
|
71
74
|
owie: {
|
|
72
75
|
eyeBlinkLeft: 1.0, eyeBlinkRight: 1.0,
|
|
@@ -147,12 +150,56 @@ function pickIdleEmotion() {
|
|
|
147
150
|
}
|
|
148
151
|
return IDLE_EMOTIONS[0].key;
|
|
149
152
|
}
|
|
153
|
+
function morphAliases(name) {
|
|
154
|
+
const out = new Set([name, name.toLowerCase()]);
|
|
155
|
+
const pairs = [
|
|
156
|
+
['Left', '_L'],
|
|
157
|
+
['Right', '_R'],
|
|
158
|
+
['Left', 'Left'],
|
|
159
|
+
['Right', 'Right'],
|
|
160
|
+
['left', '_l'],
|
|
161
|
+
['right', '_r'],
|
|
162
|
+
];
|
|
163
|
+
for (const [needle, replacement] of pairs) {
|
|
164
|
+
if (!name.includes(needle))
|
|
165
|
+
continue;
|
|
166
|
+
out.add(name.replace(needle, replacement));
|
|
167
|
+
out.add(name.replace(needle, replacement).toLowerCase());
|
|
168
|
+
out.add(name.replace(needle, replacement.replace('_', '')));
|
|
169
|
+
out.add(name.replace(needle, replacement.replace('_', '')).toLowerCase());
|
|
170
|
+
}
|
|
171
|
+
if (name.endsWith('Left')) {
|
|
172
|
+
const stem = name.slice(0, -4);
|
|
173
|
+
out.add(`${stem}_L`);
|
|
174
|
+
out.add(`${stem}_l`);
|
|
175
|
+
out.add(`${stem}L`);
|
|
176
|
+
out.add(`${stem}Left`);
|
|
177
|
+
}
|
|
178
|
+
if (name.endsWith('Right')) {
|
|
179
|
+
const stem = name.slice(0, -5);
|
|
180
|
+
out.add(`${stem}_R`);
|
|
181
|
+
out.add(`${stem}_r`);
|
|
182
|
+
out.add(`${stem}R`);
|
|
183
|
+
out.add(`${stem}Right`);
|
|
184
|
+
}
|
|
185
|
+
return [...out];
|
|
186
|
+
}
|
|
187
|
+
function resolveMorphIndices(index, name) {
|
|
188
|
+
const found = new Set();
|
|
189
|
+
for (const alias of morphAliases(name)) {
|
|
190
|
+
const i = index.get(alias) ?? index.get(alias.toLowerCase());
|
|
191
|
+
if (i !== undefined)
|
|
192
|
+
found.add(i);
|
|
193
|
+
}
|
|
194
|
+
return [...found];
|
|
195
|
+
}
|
|
150
196
|
// ---------------------------------------------------------------------------
|
|
151
197
|
// Inner R3F scene — runs inside Canvas
|
|
152
198
|
// ---------------------------------------------------------------------------
|
|
153
199
|
function AvatarScene({ uri, morphState, onReady, }) {
|
|
154
|
-
const { camera } = (0,
|
|
155
|
-
const gltf = (0,
|
|
200
|
+
const { camera } = (0, fiber_1.useThree)();
|
|
201
|
+
const { gltf } = (0, useNativeGLTF_1.useNativeGLTF)(uri);
|
|
202
|
+
const scene = gltf?.scene ?? null;
|
|
156
203
|
const readyFiredRef = (0, react_1.useRef)(false);
|
|
157
204
|
// Position camera to match Filament version
|
|
158
205
|
(0, react_1.useLayoutEffect)(() => {
|
|
@@ -165,11 +212,11 @@ function AvatarScene({ uri, morphState, onReady, }) {
|
|
|
165
212
|
}, [camera]);
|
|
166
213
|
// Find best mesh and build morph index
|
|
167
214
|
(0, react_1.useEffect)(() => {
|
|
168
|
-
if (!
|
|
215
|
+
if (!scene)
|
|
169
216
|
return;
|
|
170
217
|
let best = null;
|
|
171
218
|
let bestCount = 0;
|
|
172
|
-
|
|
219
|
+
scene.traverse((node) => {
|
|
173
220
|
if (!(node instanceof THREE.Mesh))
|
|
174
221
|
return;
|
|
175
222
|
const count = Object.keys(node.morphTargetDictionary ?? {}).length;
|
|
@@ -192,33 +239,31 @@ function AvatarScene({ uri, morphState, onReady, }) {
|
|
|
192
239
|
readyFiredRef.current = true;
|
|
193
240
|
onReady();
|
|
194
241
|
}
|
|
195
|
-
}, [
|
|
242
|
+
}, [scene, morphState, onReady]);
|
|
196
243
|
// Apply morphState.weights to mesh every frame
|
|
197
|
-
(0,
|
|
244
|
+
(0, fiber_1.useFrame)(() => {
|
|
198
245
|
const { mesh, weights, index } = morphState.current;
|
|
199
246
|
if (!mesh?.morphTargetInfluences)
|
|
200
247
|
return;
|
|
201
248
|
for (const [name, w] of Object.entries(weights)) {
|
|
202
|
-
const i
|
|
203
|
-
if (i !== undefined)
|
|
249
|
+
for (const i of resolveMorphIndices(index, name)) {
|
|
204
250
|
mesh.morphTargetInfluences[i] = w;
|
|
251
|
+
}
|
|
205
252
|
}
|
|
206
253
|
});
|
|
207
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("ambientLight", { intensity: 0.65 }), (0, jsx_runtime_1.jsx)("directionalLight", { position: [1, 3, 2], intensity: 1.3 }), (0, jsx_runtime_1.jsx)("primitive", { object:
|
|
254
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("ambientLight", { intensity: 0.65 }), (0, jsx_runtime_1.jsx)("directionalLight", { position: [1, 3, 2], intensity: 1.3 }), scene ? (0, jsx_runtime_1.jsx)("primitive", { object: scene }) : null] }));
|
|
208
255
|
}
|
|
209
256
|
// ---------------------------------------------------------------------------
|
|
210
257
|
// Main component
|
|
211
258
|
// ---------------------------------------------------------------------------
|
|
212
|
-
function FaceSqueezeEditor({ onClose }) {
|
|
259
|
+
function FaceSqueezeEditor({ onClose, avatarModule = exports.FACE_SQUEEZE_LOCAL_MODULE, }) {
|
|
213
260
|
const [localUri, setLocalUri] = (0, react_1.useState)(null);
|
|
214
261
|
const [isReady, setIsReady] = (0, react_1.useState)(false);
|
|
215
262
|
(0, react_1.useEffect)(() => {
|
|
216
263
|
let cancelled = false;
|
|
217
|
-
(async () => {
|
|
264
|
+
void (async () => {
|
|
218
265
|
try {
|
|
219
|
-
const
|
|
220
|
-
await asset.downloadAsync();
|
|
221
|
-
const uri = asset.localUri ?? asset.uri;
|
|
266
|
+
const uri = await (0, avatarUtils_1.resolveLocalAssetUrl)(avatarModule);
|
|
222
267
|
if (!cancelled && uri)
|
|
223
268
|
setLocalUri(uri);
|
|
224
269
|
}
|
|
@@ -227,16 +272,13 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
227
272
|
}
|
|
228
273
|
})();
|
|
229
274
|
return () => { cancelled = true; };
|
|
230
|
-
}, []);
|
|
275
|
+
}, [avatarModule]);
|
|
231
276
|
// Shared morph state — mutated directly, never triggers re-render
|
|
232
277
|
const morphState = (0, react_1.useRef)({
|
|
233
278
|
weights: {},
|
|
234
279
|
index: new Map(),
|
|
235
280
|
mesh: null,
|
|
236
281
|
});
|
|
237
|
-
// ---- Audio ----
|
|
238
|
-
const reactionSoundIdx = (0, react_1.useRef)(0);
|
|
239
|
-
const player = (0, expo_audio_1.useAudioPlayer)(REACTION_SOUNDS[0]);
|
|
240
282
|
// ---- Timers ----
|
|
241
283
|
const springRef = (0, react_1.useRef)(null);
|
|
242
284
|
const emotionHoldTimerRef = (0, react_1.useRef)(null);
|
|
@@ -244,19 +286,17 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
244
286
|
const lipSyncIntervalRef = (0, react_1.useRef)(null);
|
|
245
287
|
// ---- View geometry (for gesture zone math) ----
|
|
246
288
|
const viewSize = (0, react_1.useRef)({ width: 1, height: 1 });
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const
|
|
289
|
+
const touchStartRef = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
290
|
+
const touchZoneRef = (0, react_1.useRef)('cheek_left');
|
|
291
|
+
const lastPanUpdateTsRef = (0, react_1.useRef)(0);
|
|
292
|
+
const pinchStartDistanceRef = (0, react_1.useRef)(null);
|
|
293
|
+
const activeTouchesRef = (0, react_1.useRef)(0);
|
|
294
|
+
const hasMovedRef = (0, react_1.useRef)(false);
|
|
253
295
|
const onLayout = (0, react_1.useCallback)((e) => {
|
|
254
296
|
const w = Math.max(1, e.nativeEvent.layout.width);
|
|
255
297
|
const h = Math.max(1, e.nativeEvent.layout.height);
|
|
256
298
|
viewSize.current = { width: w, height: h };
|
|
257
|
-
|
|
258
|
-
viewHeightSv.value = h;
|
|
259
|
-
}, [viewHeightSv, viewWidthSv]);
|
|
299
|
+
}, []);
|
|
260
300
|
// ---- Weight helpers ----
|
|
261
301
|
const applyWeights = (0, react_1.useCallback)((targets) => {
|
|
262
302
|
for (const [name, w] of Object.entries(targets)) {
|
|
@@ -340,15 +380,8 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
340
380
|
}, TICK_MS);
|
|
341
381
|
}, [applyWeights, stopLipSync]);
|
|
342
382
|
const playReaction = (0, react_1.useCallback)((durationMs = 1400) => {
|
|
343
|
-
const idx = reactionSoundIdx.current % REACTION_SOUNDS.length;
|
|
344
|
-
reactionSoundIdx.current += 1;
|
|
345
|
-
try {
|
|
346
|
-
player.replace(REACTION_SOUNDS[idx]);
|
|
347
|
-
player.play();
|
|
348
|
-
}
|
|
349
|
-
catch { /* ignore */ }
|
|
350
383
|
startLipSync(durationMs);
|
|
351
|
-
}, [
|
|
384
|
+
}, [startLipSync]);
|
|
352
385
|
// Cleanup
|
|
353
386
|
(0, react_1.useEffect)(() => () => {
|
|
354
387
|
if (springRef.current !== null)
|
|
@@ -373,10 +406,9 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
373
406
|
};
|
|
374
407
|
}, [isReady, playEmotionBurst]);
|
|
375
408
|
// ---- Gesture callbacks (JS side — called via runOnJS) ----
|
|
376
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
377
409
|
const onPanBeginJs = (0, react_1.useCallback)((_x, _y, _zone) => {
|
|
378
410
|
stopSpring();
|
|
379
|
-
|
|
411
|
+
emitHaptic();
|
|
380
412
|
}, [stopSpring]);
|
|
381
413
|
const onPanUpdateJs = (0, react_1.useCallback)((zone, dx, dy, isLeftHalf) => {
|
|
382
414
|
const u = {};
|
|
@@ -452,7 +484,7 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
452
484
|
}, [springToZero]);
|
|
453
485
|
const onPinchBeginJs = (0, react_1.useCallback)(() => {
|
|
454
486
|
stopSpring();
|
|
455
|
-
|
|
487
|
+
emitHaptic();
|
|
456
488
|
}, [stopSpring]);
|
|
457
489
|
const onPinchUpdateJs = (0, react_1.useCallback)((scale, delta) => {
|
|
458
490
|
if (scale < 1) {
|
|
@@ -471,83 +503,125 @@ function FaceSqueezeEditor({ onClose }) {
|
|
|
471
503
|
const relY = y / Math.max(1, viewSize.current.height);
|
|
472
504
|
if (relY < 0.16 || relY > 0.52)
|
|
473
505
|
return;
|
|
474
|
-
|
|
506
|
+
emitHaptic();
|
|
475
507
|
playReaction();
|
|
476
508
|
playEmotionBurst('owie');
|
|
477
509
|
}, [playEmotionBurst, playReaction]);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
.minDistance(0)
|
|
481
|
-
.onBegin((e) => {
|
|
482
|
-
'worklet';
|
|
483
|
-
touchStartXSv.value = e.x;
|
|
484
|
-
touchStartYSv.value = e.y;
|
|
485
|
-
const relY = e.y / Math.max(1, viewHeightSv.value);
|
|
486
|
-
let zone;
|
|
510
|
+
const getZoneForPoint = (0, react_1.useCallback)((x, y) => {
|
|
511
|
+
const relY = y / Math.max(1, viewSize.current.height);
|
|
487
512
|
if (relY < 0.20)
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
513
|
+
return 'brow';
|
|
514
|
+
if (relY < 0.38)
|
|
515
|
+
return 'eye';
|
|
516
|
+
if (relY > 0.72)
|
|
517
|
+
return 'jaw';
|
|
518
|
+
return x < viewSize.current.width / 2 ? 'cheek_left' : 'cheek_right';
|
|
519
|
+
}, []);
|
|
520
|
+
const distanceBetweenTouches = (0, react_1.useCallback)((event) => {
|
|
521
|
+
const touches = event.nativeEvent.touches;
|
|
522
|
+
if (touches.length < 2)
|
|
523
|
+
return null;
|
|
524
|
+
const [a, b] = [touches[0], touches[1]];
|
|
525
|
+
const dx = b.pageX - a.pageX;
|
|
526
|
+
const dy = b.pageY - a.pageY;
|
|
527
|
+
return Math.hypot(dx, dy);
|
|
528
|
+
}, []);
|
|
529
|
+
const handleResponderGrant = (0, react_1.useCallback)((event) => {
|
|
530
|
+
activeTouchesRef.current = event.nativeEvent.touches.length;
|
|
531
|
+
hasMovedRef.current = false;
|
|
532
|
+
if (event.nativeEvent.touches.length >= 2) {
|
|
533
|
+
pinchStartDistanceRef.current = distanceBetweenTouches(event);
|
|
534
|
+
onPinchBeginJs();
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const { locationX: x, locationY: y } = event.nativeEvent;
|
|
538
|
+
touchStartRef.current = { x, y };
|
|
539
|
+
touchZoneRef.current = getZoneForPoint(x, y);
|
|
540
|
+
lastPanUpdateTsRef.current = 0;
|
|
541
|
+
onPanBeginJs(x, y, touchZoneRef.current);
|
|
542
|
+
}, [distanceBetweenTouches, getZoneForPoint, onPanBeginJs, onPinchBeginJs]);
|
|
543
|
+
const handleResponderMove = (0, react_1.useCallback)((event) => {
|
|
544
|
+
const touchCount = event.nativeEvent.touches.length;
|
|
545
|
+
activeTouchesRef.current = touchCount;
|
|
546
|
+
if (touchCount >= 2) {
|
|
547
|
+
const currentDistance = distanceBetweenTouches(event);
|
|
548
|
+
const startDistance = pinchStartDistanceRef.current;
|
|
549
|
+
if (!currentDistance || !startDistance)
|
|
550
|
+
return;
|
|
551
|
+
hasMovedRef.current = true;
|
|
552
|
+
const scale = currentDistance / startDistance;
|
|
553
|
+
onPinchUpdateJs(scale, Math.abs(1 - scale));
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
500
556
|
const now = Date.now();
|
|
501
|
-
if (now -
|
|
557
|
+
if (now - lastPanUpdateTsRef.current < 33)
|
|
502
558
|
return;
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
const
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
(
|
|
532
|
-
|
|
533
|
-
|
|
559
|
+
lastPanUpdateTsRef.current = now;
|
|
560
|
+
const { locationX: x, locationY: y } = event.nativeEvent;
|
|
561
|
+
const w = Math.max(1, viewSize.current.width);
|
|
562
|
+
const h = Math.max(1, viewSize.current.height);
|
|
563
|
+
const dx = (x - touchStartRef.current.x) / w;
|
|
564
|
+
const dy = (y - touchStartRef.current.y) / h;
|
|
565
|
+
if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01)
|
|
566
|
+
hasMovedRef.current = true;
|
|
567
|
+
onPanUpdateJs(touchZoneRef.current, dx, dy, touchStartRef.current.x < w / 2);
|
|
568
|
+
}, [distanceBetweenTouches, onPanUpdateJs, onPinchUpdateJs]);
|
|
569
|
+
const handleResponderRelease = (0, react_1.useCallback)((event) => {
|
|
570
|
+
if (activeTouchesRef.current >= 2 || pinchStartDistanceRef.current != null) {
|
|
571
|
+
pinchStartDistanceRef.current = null;
|
|
572
|
+
onPinchEndJs();
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
onPanEndJs();
|
|
576
|
+
if (!hasMovedRef.current) {
|
|
577
|
+
const { locationX: x, locationY: y } = event.nativeEvent;
|
|
578
|
+
onEyeTapJs(x, y);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
activeTouchesRef.current = 0;
|
|
582
|
+
hasMovedRef.current = false;
|
|
583
|
+
}, [onEyeTapJs, onPanEndJs, onPinchEndJs]);
|
|
584
|
+
const handleResponderTerminate = (0, react_1.useCallback)(() => {
|
|
585
|
+
if (pinchStartDistanceRef.current != null || activeTouchesRef.current >= 2) {
|
|
586
|
+
pinchStartDistanceRef.current = null;
|
|
587
|
+
onPinchEndJs();
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
onPanEndJs();
|
|
591
|
+
}
|
|
592
|
+
activeTouchesRef.current = 0;
|
|
593
|
+
hasMovedRef.current = false;
|
|
594
|
+
}, [onPanEndJs, onPinchEndJs]);
|
|
534
595
|
const handleReady = (0, react_1.useCallback)(() => setIsReady(true), []);
|
|
535
|
-
|
|
596
|
+
const editorCamera = (0, react_1.useMemo)(() => ({
|
|
597
|
+
fov: 34,
|
|
598
|
+
position: [0, 1.55, 1.05],
|
|
599
|
+
near: 0.01,
|
|
600
|
+
far: 50,
|
|
601
|
+
}), []);
|
|
602
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.scene, children: [localUri ? ((0, jsx_runtime_1.jsx)(R3FWebGpuCanvas_1.R3FWebGpuCanvas, { style: react_native_1.StyleSheet.absoluteFill, camera: editorCamera, clearColor: "#0a0a0a", children: (0, jsx_runtime_1.jsx)(AvatarScene, { uri: localUri, morphState: morphState, onReady: handleReady }) })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.loading, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.loadingText, children: "Loading face\u2026" }) })), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [react_native_1.StyleSheet.absoluteFill, styles.touchOverlay], pointerEvents: "box-only", onLayout: onLayout, collapsable: false, onStartShouldSetResponder: () => true, onStartShouldSetResponderCapture: () => true, onMoveShouldSetResponder: () => true, onMoveShouldSetResponderCapture: () => true, onResponderGrant: handleResponderGrant, onResponderMove: handleResponderMove, onResponderRelease: handleResponderRelease, onResponderTerminate: handleResponderTerminate, onResponderTerminationRequest: () => true })] }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.closeBtn, onPress: onClose, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.closeBtnText, children: "\u2715 Done squeezing" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.hint, children: "Drag to stretch \u00B7 Pinch lips \u00B7 Poke an eye \uD83D\uDC41" })] }));
|
|
536
603
|
}
|
|
537
|
-
exports.FaceSqueezeEditor = FaceSqueezeEditor;
|
|
538
604
|
const styles = react_native_1.StyleSheet.create({
|
|
539
605
|
container: { flex: 1, backgroundColor: '#0a0a0a' },
|
|
540
|
-
scene: { flex: 1 },
|
|
606
|
+
scene: { flex: 1, position: 'relative' },
|
|
607
|
+
touchOverlay: {
|
|
608
|
+
zIndex: 10,
|
|
609
|
+
elevation: 10,
|
|
610
|
+
backgroundColor: 'transparent',
|
|
611
|
+
},
|
|
541
612
|
loading: { flex: 1, alignItems: 'center', justifyContent: 'center' },
|
|
542
613
|
loadingText: { color: '#97a3b5', fontSize: 14 },
|
|
543
614
|
closeBtn: {
|
|
544
615
|
position: 'absolute', top: 52, right: 16,
|
|
545
616
|
backgroundColor: '#6c63ff', borderRadius: 20,
|
|
546
617
|
paddingHorizontal: 16, paddingVertical: 8,
|
|
618
|
+
zIndex: 20,
|
|
619
|
+
elevation: 20,
|
|
547
620
|
},
|
|
548
621
|
closeBtnText: { color: '#fff', fontWeight: '700', fontSize: 14 },
|
|
549
622
|
hint: {
|
|
550
623
|
position: 'absolute', bottom: 40, alignSelf: 'center',
|
|
551
624
|
color: '#97a3b5', fontSize: 13,
|
|
625
|
+
zIndex: 20,
|
|
552
626
|
},
|
|
553
627
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/** Web builds do not bundle the native face-squeeze GLB module. */
|
|
2
|
+
export declare const FACE_SQUEEZE_LOCAL_MODULE: null;
|
|
2
3
|
export interface FaceSqueezeEditorProps {
|
|
3
4
|
onClose: () => void;
|
|
5
|
+
avatarModule?: unknown;
|
|
4
6
|
}
|
|
5
7
|
export declare function FaceSqueezeEditor({ onClose }: FaceSqueezeEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
6
8
|
export default FaceSqueezeEditor;
|
|
@@ -1,54 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
|
|
4
|
+
exports.FaceSqueezeEditor = FaceSqueezeEditor;
|
|
4
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.FACE_SQUEEZE_LOCAL_MODULE = require('../assets/face-squeeze-local.glb');
|
|
6
|
+
/** Web builds do not bundle the native face-squeeze GLB module. */
|
|
7
|
+
exports.FACE_SQUEEZE_LOCAL_MODULE = null;
|
|
8
8
|
function FaceSqueezeEditor({ onClose }) {
|
|
9
|
-
return ((0, jsx_runtime_1.jsx)(
|
|
9
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: styles.container, children: (0, jsx_runtime_1.jsxs)("div", { style: styles.card, children: [(0, jsx_runtime_1.jsx)("h2", { style: styles.title, children: "Face squeeze editor is mobile-only." }), (0, jsx_runtime_1.jsx)("p", { style: styles.subtitle, children: "This placeholder keeps web builds safe while preserving native functionality." }), (0, jsx_runtime_1.jsx)("button", { type: "button", style: styles.closeButton, onClick: onClose, children: "Close" })] }) }));
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
const styles = react_native_1.StyleSheet.create({
|
|
11
|
+
const styles = {
|
|
13
12
|
container: {
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
backgroundColor: '#0f1115',
|
|
15
|
+
boxSizing: 'border-box',
|
|
16
|
+
display: 'flex',
|
|
14
17
|
flex: 1,
|
|
15
|
-
|
|
16
|
-
justifyContent:
|
|
17
|
-
backgroundColor: "#0f1115",
|
|
18
|
+
height: '100%',
|
|
19
|
+
justifyContent: 'center',
|
|
18
20
|
padding: 20,
|
|
21
|
+
width: '100%',
|
|
19
22
|
},
|
|
20
23
|
card: {
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
backgroundColor: '#171b23',
|
|
25
|
+
border: '1px solid #2d3340',
|
|
23
26
|
borderRadius: 12,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
backgroundColor: "#171b23",
|
|
27
|
+
boxSizing: 'border-box',
|
|
28
|
+
maxWidth: 460,
|
|
27
29
|
padding: 16,
|
|
28
|
-
|
|
30
|
+
width: '100%',
|
|
29
31
|
},
|
|
30
32
|
title: {
|
|
31
|
-
color:
|
|
33
|
+
color: '#f1f4f8',
|
|
32
34
|
fontSize: 18,
|
|
33
|
-
fontWeight:
|
|
35
|
+
fontWeight: 700,
|
|
36
|
+
margin: 0,
|
|
34
37
|
},
|
|
35
38
|
subtitle: {
|
|
36
|
-
color:
|
|
39
|
+
color: '#98a1b3',
|
|
37
40
|
fontSize: 14,
|
|
38
|
-
lineHeight:
|
|
41
|
+
lineHeight: '20px',
|
|
42
|
+
margin: '10px 0 0',
|
|
39
43
|
},
|
|
40
44
|
closeButton: {
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
backgroundColor: '#2e8f5b',
|
|
46
|
+
border: 0,
|
|
43
47
|
borderRadius: 8,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
paddingVertical: 8,
|
|
47
|
-
},
|
|
48
|
-
closeButtonText: {
|
|
49
|
-
color: "#ffffff",
|
|
48
|
+
color: '#ffffff',
|
|
49
|
+
cursor: 'pointer',
|
|
50
50
|
fontSize: 14,
|
|
51
|
-
fontWeight:
|
|
51
|
+
fontWeight: 600,
|
|
52
|
+
marginTop: 16,
|
|
53
|
+
padding: '8px 12px',
|
|
52
54
|
},
|
|
53
|
-
}
|
|
55
|
+
};
|
|
54
56
|
exports.default = FaceSqueezeEditor;
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RigidAccessory =
|
|
3
|
+
exports.RigidAccessory = RigidAccessory;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
// @ts-nocheck
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- Accessory attachment walks arbitrary GLTF scene nodes from Three.js loader output. */
|
|
6
7
|
const drei_1 = require("@react-three/drei");
|
|
7
8
|
const react_1 = require("react");
|
|
8
9
|
const three_1 = require("three");
|
|
9
10
|
function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition, offsetRotation, scale = 1, isEditing = false, onPlacementChange, }) {
|
|
10
11
|
const { scene } = (0, drei_1.useGLTF)(url);
|
|
11
|
-
// Clone uniquely for this instance
|
|
12
|
+
// Clone uniquely for this instance — dispose geometry/materials on replacement or unmount
|
|
12
13
|
const clone = (0, react_1.useMemo)(() => scene.clone(true), [scene]);
|
|
14
|
+
(0, react_1.useEffect)(() => {
|
|
15
|
+
return () => {
|
|
16
|
+
clone.traverse((obj) => {
|
|
17
|
+
if (obj instanceof three_1.Mesh) {
|
|
18
|
+
obj.geometry?.dispose();
|
|
19
|
+
if (Array.isArray(obj.material))
|
|
20
|
+
obj.material.forEach((m) => m.dispose());
|
|
21
|
+
else
|
|
22
|
+
obj.material?.dispose();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}, [clone]);
|
|
13
27
|
const [basePos, setBasePos] = (0, react_1.useState)(null);
|
|
14
28
|
(0, react_1.useEffect)(() => {
|
|
15
29
|
if (!avatarScene)
|
|
@@ -44,7 +58,8 @@ function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition,
|
|
|
44
58
|
clone.rotation.set(...offsetRotation);
|
|
45
59
|
}
|
|
46
60
|
if (scale !== undefined) {
|
|
47
|
-
|
|
61
|
+
const s = Math.max(0.001, scale);
|
|
62
|
+
clone.scale.set(s, s, s);
|
|
48
63
|
}
|
|
49
64
|
}, [basePos, offsetPosition, offsetRotation, scale, clone]);
|
|
50
65
|
const handleDragEnd = () => {
|
|
@@ -76,4 +91,3 @@ function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition,
|
|
|
76
91
|
}
|
|
77
92
|
return (0, jsx_runtime_1.jsx)("primitive", { object: clone });
|
|
78
93
|
}
|
|
79
|
-
exports.RigidAccessory = RigidAccessory;
|
|
@@ -15,17 +15,28 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.SkinnedClothing =
|
|
36
|
+
exports.SkinnedClothing = SkinnedClothing;
|
|
27
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
28
38
|
// @ts-nocheck
|
|
39
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- SkeletonUtils.clone and GLTF skeleton rebinding expose untyped Three.js internals. */
|
|
29
40
|
const drei_1 = require("@react-three/drei");
|
|
30
41
|
const react_1 = require("react");
|
|
31
42
|
const THREE = __importStar(require("three"));
|
|
@@ -113,4 +124,3 @@ function SkinnedClothing({ url, avatarSkeleton }) {
|
|
|
113
124
|
}, [scene, avatarSkeleton]);
|
|
114
125
|
return (0, jsx_runtime_1.jsx)("group", { ref: groupRef });
|
|
115
126
|
}
|
|
116
|
-
exports.SkinnedClothing = SkinnedClothing;
|