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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* patchThreeForRN — patches Three.js loaders for React Native compatibility.
|
|
4
|
+
*
|
|
5
|
+
* Problem: GLTFLoader (three-stdlib) calls URL.createObjectURL(blob) when
|
|
6
|
+
* extracting embedded textures from a GLB's buffer views. React Native's
|
|
7
|
+
* Hermes / JSC runtime does not implement URL.createObjectURL, so every
|
|
8
|
+
* texture inside the GLB fails with "Cannot create URL for blob!".
|
|
9
|
+
*
|
|
10
|
+
* Fix strategy (3 layers):
|
|
11
|
+
*
|
|
12
|
+
* Layer 1 — URL.createObjectURL polyfill
|
|
13
|
+
* Instead of throwing, store the Blob in a module-level Map under a
|
|
14
|
+
* synthetic "rnblob://<id>" key and return that key as the "URL".
|
|
15
|
+
* URL.revokeObjectURL deletes the entry.
|
|
16
|
+
*
|
|
17
|
+
* Layer 2 — THREE.ImageLoader patch
|
|
18
|
+
* When ImageLoader.load() receives an "rnblob://" URL, retrieve the
|
|
19
|
+
* stored Blob, read it as base64 via FileReader, build a "data:" URI,
|
|
20
|
+
* then set it as img.src so expo-gl can decode it natively.
|
|
21
|
+
*
|
|
22
|
+
* Layer 3 — THREE.TextureLoader / THREE.FileLoader safety net
|
|
23
|
+
* Same interception for any direct TextureLoader usage and for
|
|
24
|
+
* FileLoader which is also used internally by GLTFLoader.
|
|
25
|
+
*
|
|
26
|
+
* Call installThreeRNPatches() once at module load time, before any
|
|
27
|
+
* useGLTF / GLTFLoader usage. Pass debug=true for verbose logging.
|
|
28
|
+
*/
|
|
29
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- Three.js loader monkey patches must touch dynamic private fields across RN runtimes. */
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
42
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
43
|
+
}) : function(o, v) {
|
|
44
|
+
o["default"] = v;
|
|
45
|
+
});
|
|
46
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
47
|
+
var ownKeys = function(o) {
|
|
48
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
49
|
+
var ar = [];
|
|
50
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
51
|
+
return ar;
|
|
52
|
+
};
|
|
53
|
+
return ownKeys(o);
|
|
54
|
+
};
|
|
55
|
+
return function (mod) {
|
|
56
|
+
if (mod && mod.__esModule) return mod;
|
|
57
|
+
var result = {};
|
|
58
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
59
|
+
__setModuleDefault(result, mod);
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
})();
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.installThreeRNPatches = installThreeRNPatches;
|
|
65
|
+
const react_native_1 = require("react-native");
|
|
66
|
+
const base64_js_1 = require("base64-js");
|
|
67
|
+
const THREE = __importStar(require("three"));
|
|
68
|
+
const blobShim_1 = require("./blobShim");
|
|
69
|
+
let installed = false;
|
|
70
|
+
// blobStore is only used if something still produces rnblob:// URLs (shouldn't happen
|
|
71
|
+
// after blobShim returns data: URIs directly, but kept as safety net)
|
|
72
|
+
const blobStore = new Map();
|
|
73
|
+
/** Re-patch URL.createObjectURL on the current globalThis.URL.
|
|
74
|
+
* Must run every time installThreeRNPatches() is called because libraries like
|
|
75
|
+
* @livekit/react-native call setupURLPolyfill() which replaces globalThis.URL
|
|
76
|
+
* with a new whatwg-url object whose createObjectURL always throws
|
|
77
|
+
* "Cannot create URL for blob!". */
|
|
78
|
+
function repatchUrlCreateObjectURL() {
|
|
79
|
+
const URLObj = globalThis.URL;
|
|
80
|
+
if (!URLObj)
|
|
81
|
+
return;
|
|
82
|
+
if (typeof URLObj.createObjectURL === 'function' &&
|
|
83
|
+
URLObj.createObjectURL.__blobShim) {
|
|
84
|
+
return; // already our patch — no-op
|
|
85
|
+
}
|
|
86
|
+
const patchedCreateObjectURL = (blob) => {
|
|
87
|
+
const bytes = blobShim_1.blobBytesStore.get(blob);
|
|
88
|
+
if (bytes) {
|
|
89
|
+
const mime = blob.type || 'application/octet-stream';
|
|
90
|
+
const b64 = (0, base64_js_1.fromByteArray)(bytes);
|
|
91
|
+
return `data:${mime};base64,${b64}`;
|
|
92
|
+
}
|
|
93
|
+
throw new Error('[BlobShim] createObjectURL: no bytes stashed for this Blob');
|
|
94
|
+
};
|
|
95
|
+
patchedCreateObjectURL.__blobShim = true;
|
|
96
|
+
URLObj.createObjectURL = patchedCreateObjectURL;
|
|
97
|
+
URLObj.revokeObjectURL = (_url) => { };
|
|
98
|
+
}
|
|
99
|
+
function isInlineImageUrl(url) {
|
|
100
|
+
return url.startsWith('rnblob://') || url.startsWith('data:image/');
|
|
101
|
+
}
|
|
102
|
+
function getMimeTypeFromDataUri(dataUri) {
|
|
103
|
+
const mimeMatch = dataUri.match(/^data:([^;,]+)[;,]/i);
|
|
104
|
+
return mimeMatch?.[1] ?? 'image/png';
|
|
105
|
+
}
|
|
106
|
+
function decodeDataUriBytes(dataUri) {
|
|
107
|
+
const marker = ';base64,';
|
|
108
|
+
const markerIndex = dataUri.indexOf(marker);
|
|
109
|
+
if (markerIndex < 0) {
|
|
110
|
+
throw new Error('[ThreeRNPatch] Unsupported data URI encoding');
|
|
111
|
+
}
|
|
112
|
+
const base64 = dataUri.slice(markerIndex + marker.length);
|
|
113
|
+
return (0, base64_js_1.toByteArray)(base64);
|
|
114
|
+
}
|
|
115
|
+
async function bytesFromBlob(blob) {
|
|
116
|
+
if (typeof blob.arrayBuffer === 'function') {
|
|
117
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
118
|
+
}
|
|
119
|
+
const dataUri = await new Promise((resolve, reject) => {
|
|
120
|
+
const reader = new FileReader();
|
|
121
|
+
reader.onload = () => resolve(String(reader.result));
|
|
122
|
+
reader.onerror = () => reject(new Error('[ThreeRNPatch] FileReader failed while decoding blob'));
|
|
123
|
+
reader.readAsDataURL(blob);
|
|
124
|
+
});
|
|
125
|
+
return decodeDataUriBytes(dataUri);
|
|
126
|
+
}
|
|
127
|
+
function installThreeRNPatches(debug = false) {
|
|
128
|
+
if (react_native_1.Platform.OS === 'web')
|
|
129
|
+
return;
|
|
130
|
+
const log = (...args) => {
|
|
131
|
+
if (debug)
|
|
132
|
+
console.log('[ThreeRNPatch]', ...args);
|
|
133
|
+
};
|
|
134
|
+
// Layer 1: Re-patch URL.createObjectURL on the *current* globalThis.URL.
|
|
135
|
+
// This must run every call (not guarded by `installed`) because libraries like
|
|
136
|
+
// @livekit/react-native call setupURLPolyfill() which replaces globalThis.URL
|
|
137
|
+
// with a new whatwg-url object whose createObjectURL always throws.
|
|
138
|
+
// We re-apply our override so GLTFLoader's `const URL = self.URL` gets our patch.
|
|
139
|
+
repatchUrlCreateObjectURL();
|
|
140
|
+
log('URL.createObjectURL re-patched on current globalThis.URL');
|
|
141
|
+
if (installed)
|
|
142
|
+
return;
|
|
143
|
+
installed = true;
|
|
144
|
+
log('Installing Three.js RN patches…');
|
|
145
|
+
// -----------------------------------------------------------------------
|
|
146
|
+
// Layer 2: THREE.ImageLoader patch — patch prototype to avoid ESM live-binding restrictions
|
|
147
|
+
// three.webgpu.js is an ESM file; its named exports are getter-only live bindings
|
|
148
|
+
// from the consumer's perspective, so assigning THREE.ImageLoader = X throws.
|
|
149
|
+
// Patching the prototype achieves the same effect without touching the namespace.
|
|
150
|
+
// -----------------------------------------------------------------------
|
|
151
|
+
const origImageLoaderLoad = THREE.ImageLoader.prototype.load;
|
|
152
|
+
THREE.ImageLoader.prototype.load = function patchedImageLoad(url, onLoad, onProgress, onError) {
|
|
153
|
+
if (!url || !isInlineImageUrl(url)) {
|
|
154
|
+
return origImageLoaderLoad.call(this, url, onLoad, onProgress, onError);
|
|
155
|
+
}
|
|
156
|
+
if (url.startsWith('data:image/')) {
|
|
157
|
+
const img = _makeImageFromDataUri(url, getMimeTypeFromDataUri(url));
|
|
158
|
+
onLoad?.(img);
|
|
159
|
+
return img;
|
|
160
|
+
}
|
|
161
|
+
const blob = blobStore.get(url);
|
|
162
|
+
if (!blob) {
|
|
163
|
+
log(`ImageLoader: no blob found for ${url} — returning placeholder`);
|
|
164
|
+
const placeholder = _makePlaceholder();
|
|
165
|
+
onLoad?.(placeholder);
|
|
166
|
+
return placeholder;
|
|
167
|
+
}
|
|
168
|
+
log(`ImageLoader: reading blob ${url} size=${blob.size} type=${blob.type}`);
|
|
169
|
+
const reader = new FileReader();
|
|
170
|
+
reader.onload = () => {
|
|
171
|
+
const dataUri = reader.result;
|
|
172
|
+
log(`ImageLoader: blob → data URI (${dataUri.length} chars)`);
|
|
173
|
+
const img = _makeImageFromDataUri(dataUri, blob.type || 'image/png');
|
|
174
|
+
onLoad?.(img);
|
|
175
|
+
};
|
|
176
|
+
reader.onerror = () => {
|
|
177
|
+
const err = new Error(`[ThreeRNPatch] FileReader failed for ${url}`);
|
|
178
|
+
log(`ImageLoader: FileReader error for ${url}`);
|
|
179
|
+
onError?.(err);
|
|
180
|
+
};
|
|
181
|
+
reader.readAsDataURL(blob);
|
|
182
|
+
return _makePlaceholder();
|
|
183
|
+
};
|
|
184
|
+
log('Patched THREE.ImageLoader.prototype.load');
|
|
185
|
+
// -----------------------------------------------------------------------
|
|
186
|
+
// Layer 3: THREE.TextureLoader safety net — same prototype-patch approach
|
|
187
|
+
// -----------------------------------------------------------------------
|
|
188
|
+
const origTextureLoaderLoad = THREE.TextureLoader.prototype.load;
|
|
189
|
+
THREE.TextureLoader.prototype.load = function patchedTextureLoad(url, onLoad, onProgress, onError) {
|
|
190
|
+
if (!url || !isInlineImageUrl(url)) {
|
|
191
|
+
return origTextureLoaderLoad.call(this, url, onLoad, onProgress, onError);
|
|
192
|
+
}
|
|
193
|
+
if (url.startsWith('data:image/')) {
|
|
194
|
+
const tex = new THREE.Texture();
|
|
195
|
+
tex.image = _makeImageFromDataUri(url, getMimeTypeFromDataUri(url));
|
|
196
|
+
tex.needsUpdate = true;
|
|
197
|
+
onLoad?.(tex);
|
|
198
|
+
return tex;
|
|
199
|
+
}
|
|
200
|
+
const blob = blobStore.get(url);
|
|
201
|
+
if (!blob) {
|
|
202
|
+
log(`TextureLoader: no blob for ${url} — returning empty texture`);
|
|
203
|
+
const tex = new THREE.Texture();
|
|
204
|
+
setTimeout(() => onLoad?.(tex), 0);
|
|
205
|
+
return tex;
|
|
206
|
+
}
|
|
207
|
+
log(`TextureLoader: reading blob ${url} size=${blob.size}`);
|
|
208
|
+
const tex = new THREE.Texture();
|
|
209
|
+
const reader = new FileReader();
|
|
210
|
+
reader.onload = () => {
|
|
211
|
+
const dataUri = reader.result;
|
|
212
|
+
const img = _makeImageFromDataUri(dataUri, blob.type || 'image/png');
|
|
213
|
+
tex.image = img;
|
|
214
|
+
tex.needsUpdate = true;
|
|
215
|
+
onLoad?.(tex);
|
|
216
|
+
};
|
|
217
|
+
reader.onerror = () => onError?.(new ErrorEvent('error'));
|
|
218
|
+
reader.readAsDataURL(blob);
|
|
219
|
+
return tex;
|
|
220
|
+
};
|
|
221
|
+
log('Patched THREE.TextureLoader.prototype.load');
|
|
222
|
+
const ImageBitmapLoader = THREE.ImageBitmapLoader;
|
|
223
|
+
const origImageBitmapLoad = ImageBitmapLoader?.prototype?.load;
|
|
224
|
+
if (origImageBitmapLoad) {
|
|
225
|
+
ImageBitmapLoader.prototype.load = function patchedImageBitmapLoad(url, onLoad, onProgress, onError) {
|
|
226
|
+
if (!url || !isInlineImageUrl(url)) {
|
|
227
|
+
return origImageBitmapLoad.call(this, url, onLoad, onProgress, onError);
|
|
228
|
+
}
|
|
229
|
+
const loadInlineBitmap = async () => {
|
|
230
|
+
try {
|
|
231
|
+
const bytes = url.startsWith('data:image/')
|
|
232
|
+
? decodeDataUriBytes(url)
|
|
233
|
+
: await (async () => {
|
|
234
|
+
const blob = blobStore.get(url);
|
|
235
|
+
if (!blob)
|
|
236
|
+
return null;
|
|
237
|
+
return await bytesFromBlob(blob);
|
|
238
|
+
})();
|
|
239
|
+
if (!bytes) {
|
|
240
|
+
throw new Error(`[ThreeRNPatch] No blob found for ${url}`);
|
|
241
|
+
}
|
|
242
|
+
if (typeof createImageBitmap !== 'function') {
|
|
243
|
+
throw new Error('[ThreeRNPatch] createImageBitmap is unavailable');
|
|
244
|
+
}
|
|
245
|
+
const imageBitmap = await createImageBitmap(bytes);
|
|
246
|
+
onLoad?.(imageBitmap);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
void loadInlineBitmap();
|
|
253
|
+
return undefined;
|
|
254
|
+
};
|
|
255
|
+
log('Patched THREE.ImageBitmapLoader.prototype.load');
|
|
256
|
+
}
|
|
257
|
+
log('Three.js RN patches installed — embedded GLB textures will now load via FileReader+dataURI');
|
|
258
|
+
}
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// Helpers
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
/**
|
|
263
|
+
* Build a minimal HTMLImageElement-like object from a data URI.
|
|
264
|
+
* expo-gl / react-three-fiber / @react-three/fiber/native accept this shape
|
|
265
|
+
* for texture.image on native (it gets handed to gl.texImage2D via Expo's NativeImageLoader).
|
|
266
|
+
*/
|
|
267
|
+
function _makeImageFromDataUri(dataUri, _mimeType) {
|
|
268
|
+
return {
|
|
269
|
+
src: dataUri,
|
|
270
|
+
width: 0, // Three.js reads these after decoding; 0 is fine pre-decode
|
|
271
|
+
height: 0,
|
|
272
|
+
complete: false,
|
|
273
|
+
naturalWidth: 0,
|
|
274
|
+
naturalHeight: 0,
|
|
275
|
+
crossOrigin: '',
|
|
276
|
+
// expo-gl NativeImageLoader checks for 'uri' as well
|
|
277
|
+
uri: dataUri,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/** 1×1 transparent pixel stand-in (used when we have no blob data at all). */
|
|
281
|
+
function _makePlaceholder() {
|
|
282
|
+
return {
|
|
283
|
+
src: '',
|
|
284
|
+
width: 1,
|
|
285
|
+
height: 1,
|
|
286
|
+
complete: true,
|
|
287
|
+
naturalWidth: 1,
|
|
288
|
+
naturalHeight: 1,
|
|
289
|
+
crossOrigin: '',
|
|
290
|
+
uri: '',
|
|
291
|
+
};
|
|
292
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.computeObjectPlacement = computeObjectPlacement;
|
|
37
|
+
const THREE = __importStar(require("three"));
|
|
38
|
+
function computeObjectPlacement(object, target, desiredMaxDimension = 1.5) {
|
|
39
|
+
const box = new THREE.Box3().setFromObject(object);
|
|
40
|
+
if (box.isEmpty())
|
|
41
|
+
return null;
|
|
42
|
+
const size = box.getSize(new THREE.Vector3());
|
|
43
|
+
const maxDimension = Math.max(size.x, size.y, size.z);
|
|
44
|
+
if (!Number.isFinite(maxDimension) || maxDimension <= 0)
|
|
45
|
+
return null;
|
|
46
|
+
const scale = desiredMaxDimension / maxDimension;
|
|
47
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
48
|
+
const position = target.clone().sub(center.multiplyScalar(scale));
|
|
49
|
+
return { position, scale };
|
|
50
|
+
}
|
|
@@ -15,15 +15,25 @@ 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.useAuthedModelUri =
|
|
36
|
+
exports.useAuthedModelUri = useAuthedModelUri;
|
|
27
37
|
const react_1 = require("react");
|
|
28
38
|
const FileSystem = __importStar(require("expo-file-system/legacy"));
|
|
29
39
|
const studioApi_1 = require("../api/studioApi");
|
|
@@ -51,7 +61,7 @@ function useAuthedModelUri(remoteUrl) {
|
|
|
51
61
|
return;
|
|
52
62
|
}
|
|
53
63
|
let cancelled = false;
|
|
54
|
-
(async () => {
|
|
64
|
+
void (async () => {
|
|
55
65
|
try {
|
|
56
66
|
const token = await (0, studioApi_1.getToken)();
|
|
57
67
|
// Strip query params (access_token changes per session) so the same GLB is cached across sessions
|
|
@@ -83,7 +93,9 @@ function useAuthedModelUri(remoteUrl) {
|
|
|
83
93
|
});
|
|
84
94
|
// Verify we got a valid GLB — check size and magic bytes ("glTF")
|
|
85
95
|
const downloaded = await FileSystem.getInfoAsync(dlResult.uri);
|
|
86
|
-
const downloadedSize = downloaded.size
|
|
96
|
+
const downloadedSize = 'size' in downloaded && typeof downloaded.size === 'number'
|
|
97
|
+
? downloaded.size
|
|
98
|
+
: undefined;
|
|
87
99
|
if (!downloaded.exists || !downloadedSize || downloadedSize < 1000) {
|
|
88
100
|
console.error('[useAuthedModelUri] Download too small (' + (downloadedSize ?? 0) + 'B), likely an error response');
|
|
89
101
|
failedUrls.add(remoteUrl);
|
|
@@ -114,4 +126,3 @@ function useAuthedModelUri(remoteUrl) {
|
|
|
114
126
|
}, [remoteUrl]);
|
|
115
127
|
return result;
|
|
116
128
|
}
|
|
117
|
-
exports.useAuthedModelUri = useAuthedModelUri;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useNativeGLTF = useNativeGLTF;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const loadGLTFFromUri_1 = require("./loadGLTFFromUri");
|
|
6
|
+
const gltfPromiseCache = new Map();
|
|
7
|
+
function getOrLoad(uri) {
|
|
8
|
+
const cached = gltfPromiseCache.get(uri);
|
|
9
|
+
if (cached)
|
|
10
|
+
return cached;
|
|
11
|
+
const promise = (0, loadGLTFFromUri_1.loadGLTFFromUri)(uri);
|
|
12
|
+
gltfPromiseCache.set(uri, promise);
|
|
13
|
+
return promise;
|
|
14
|
+
}
|
|
15
|
+
function useNativeGLTF(uri) {
|
|
16
|
+
const [state, setState] = (0, react_1.useState)({ gltf: null, error: null });
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
if (!uri) {
|
|
19
|
+
setState({ gltf: null, error: null });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
setState({ gltf: null, error: null });
|
|
24
|
+
getOrLoad(uri).then((gltf) => {
|
|
25
|
+
if (!cancelled)
|
|
26
|
+
setState({ gltf, error: null });
|
|
27
|
+
}, (error) => {
|
|
28
|
+
if (!cancelled)
|
|
29
|
+
setState({ gltf: null, error });
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
cancelled = true;
|
|
33
|
+
};
|
|
34
|
+
}, [uri]);
|
|
35
|
+
return state;
|
|
36
|
+
}
|