This commit is contained in:
2026-05-13 00:28:07 +08:00
parent ef4f91a75e
commit 01c5ab985a
101 changed files with 10635 additions and 2292 deletions

View File

@@ -1,7 +1,8 @@
import { Box, Loader2 } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { readAssetBytes } from '../../services/assetReadUrlService';
import { isDebugMode } from '../../config/debugMode';
import { readMatch3DGeneratedModelBytes } from '../../services/match3dGeneratedModelCache';
type ThreeModule = typeof import('three');
type GltfPayload = import('three/examples/jsm/loaders/GLTFLoader.js').GLTF;
@@ -55,6 +56,22 @@ function centerAndScaleModel(three: ThreeModule, model: import('three').Object3D
model.position.sub(center);
}
function shouldLogMatch3DModelPreviewDiagnostics() {
return isDebugMode() && import.meta.env.MODE !== 'test';
}
function warnMatch3DModelPreviewLoadFailure(source: string, error: unknown) {
if (!shouldLogMatch3DModelPreviewDiagnostics()) {
return;
}
const message =
error instanceof Error ? error.message : String(error || 'unknown error');
console.warn('[match3d] model preview load failed', {
source,
message,
});
}
export function Match3DModelPreview({
modelSrc,
className = '',
@@ -87,8 +104,6 @@ export function Match3DModelPreview({
}
let cancelled = false;
let objectUrl: string | null = null;
const teardown = () => {
const runtime = runtimeRef.current;
if (runtime?.animationId != null) {
@@ -98,10 +113,6 @@ export function Match3DModelPreview({
runtime?.renderer.dispose();
runtime?.renderer.domElement.remove();
runtimeRef.current = null;
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
objectUrl = null;
}
canvasHost.replaceChildren();
};
@@ -113,25 +124,19 @@ export function Match3DModelPreview({
setStatus('loading');
try {
const [three, loaderModule, response] = await Promise.all([
const [three, loaderModule, bytes] = await Promise.all([
import('three'),
import('three/examples/jsm/loaders/GLTFLoader.js'),
readAssetBytes(source, { expireSeconds: 600 }),
readMatch3DGeneratedModelBytes(source, { expireSeconds: 600 }),
]);
if (cancelled || !containerRef.current) {
return;
}
const bytes = await response.arrayBuffer();
if (bytes.byteLength === 0) {
throw new Error('empty model');
}
const blob = new Blob([bytes], {
type: 'model/gltf-binary',
});
objectUrl = URL.createObjectURL(blob);
const renderer = new three.WebGLRenderer({
alpha: true,
antialias: true,
@@ -167,16 +172,7 @@ export function Match3DModelPreview({
scene.add(modelRoot);
const loader = new loaderModule.GLTFLoader();
const gltf = await new Promise<GltfPayload>(
(resolve, reject) => {
loader.load(
objectUrl as string,
(loaded: GltfPayload) => resolve(loaded),
undefined,
(error) => reject(error),
);
},
);
const gltf = (await loader.parseAsync(bytes, '')) as GltfPayload;
if (cancelled) {
const cancelledModel = gltf.scene ?? gltf.scenes[0];
if (cancelledModel) {
@@ -277,8 +273,9 @@ export function Match3DModelPreview({
};
setStatus('ready');
} catch {
} catch (caughtError) {
if (!cancelled) {
warnMatch3DModelPreviewLoadFailure(source, caughtError);
setStatus('fallback');
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff