sa2kit 1.6.18 → 1.6.19
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/dist/analytics/index.js +1 -1
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +1 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/auth/components/index.js +1 -1
- package/dist/auth/components/index.js.map +1 -1
- package/dist/auth/components/index.mjs +1 -1
- package/dist/auth/components/index.mjs.map +1 -1
- package/dist/calendar/index.js +10 -10
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/index.mjs +3 -3
- package/dist/calendar/index.mjs.map +1 -1
- package/dist/{chunk-FDHCX2BY.mjs → chunk-NQ4JDU24.mjs} +3 -3
- package/dist/chunk-NQ4JDU24.mjs.map +1 -0
- package/dist/{chunk-ASL3QC22.js → chunk-PXVELWYK.js} +3 -3
- package/dist/chunk-PXVELWYK.js.map +1 -0
- package/dist/index.js +114 -114
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -7
- package/dist/index.mjs.map +1 -1
- package/dist/mmd/index.d.mts +8 -11
- package/dist/mmd/index.d.ts +8 -11
- package/dist/mmd/index.js +562 -464
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +563 -466
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/universalExport/index.js +4 -4
- package/dist/universalExport/index.js.map +1 -1
- package/dist/universalExport/index.mjs +4 -4
- package/dist/universalExport/index.mjs.map +1 -1
- package/dist/universalFile/index.js +2 -2
- package/dist/universalFile/index.js.map +1 -1
- package/dist/universalFile/index.mjs +2 -2
- package/dist/universalFile/index.mjs.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-ASL3QC22.js.map +0 -1
- package/dist/chunk-FDHCX2BY.mjs.map +0 -1
package/dist/mmd/index.mjs
CHANGED
|
@@ -6,8 +6,9 @@ import '../chunk-BJTO5JO5.mjs';
|
|
|
6
6
|
import React10, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback, useMemo } from 'react';
|
|
7
7
|
import * as THREE2 from 'three';
|
|
8
8
|
import { OutlineEffect, OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
|
|
9
|
-
import {
|
|
9
|
+
import { SkipBack, Pause, Play, SkipForward, Camera, Repeat, Repeat1, Shuffle, ListMusic, Music, X, Search, Loader2, Grid3x3, Settings, Minimize, Maximize, Video, Check, User, Image } from 'lucide-react';
|
|
10
10
|
import { createPortal } from 'react-dom';
|
|
11
|
+
import Script from 'next/script';
|
|
11
12
|
|
|
12
13
|
// src/mmd/pmx/editor/PMXEditor.ts
|
|
13
14
|
var PMXEditor = class {
|
|
@@ -2297,7 +2298,7 @@ var SettingsPanel = ({
|
|
|
2297
2298
|
};
|
|
2298
2299
|
const renderOptionsMode = () => {
|
|
2299
2300
|
if (!options) return null;
|
|
2300
|
-
return /* @__PURE__ */ React10.createElement("div", { className: "p-4" }, renderOptionGroup("\u6A21\u578B", /* @__PURE__ */ React10.createElement(User, { size: 16 }), "models", options.models, currentSelection?.modelId), renderOptionGroup("\u52A8\u4F5C", /* @__PURE__ */ React10.createElement(Video, { size: 16 }), "motions", options.motions, currentSelection?.motionId), renderOptionGroup("\u955C\u5934", /* @__PURE__ */ React10.createElement(Image
|
|
2301
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "p-4" }, renderOptionGroup("\u6A21\u578B", /* @__PURE__ */ React10.createElement(User, { size: 16 }), "models", options.models, currentSelection?.modelId), renderOptionGroup("\u52A8\u4F5C", /* @__PURE__ */ React10.createElement(Video, { size: 16 }), "motions", options.motions, currentSelection?.motionId), renderOptionGroup("\u955C\u5934", /* @__PURE__ */ React10.createElement(Image, { size: 16 }), "cameras", options.cameras, currentSelection?.cameraId), renderOptionGroup("\u97F3\u9891", /* @__PURE__ */ React10.createElement(Music, { size: 16 }), "audios", options.audios, currentSelection?.audioId), renderOptionGroup("\u821E\u53F0", /* @__PURE__ */ React10.createElement(Image, { size: 16 }), "stages", options.stages, currentSelection?.stageId));
|
|
2301
2302
|
};
|
|
2302
2303
|
return /* @__PURE__ */ React10.createElement("div", { className: "absolute right-0 top-0 h-full w-full max-w-sm transform bg-[#1a1a1e]/95 backdrop-blur-md shadow-2xl transition-transform duration-300 ease-in-out overflow-y-auto z-20 border-l border-white/10" }, /* @__PURE__ */ React10.createElement("div", { className: "sticky top-0 z-10 flex items-center justify-between border-b border-white/10 bg-[#1a1a1e]/95 p-4 backdrop-blur-md" }, /* @__PURE__ */ React10.createElement("h2", { className: "text-lg font-semibold text-white" }, mode === "list" ? "\u64AD\u653E\u5217\u8868" : "\u81EA\u5B9A\u4E49\u573A\u666F"), /* @__PURE__ */ React10.createElement(
|
|
2303
2304
|
"button",
|
|
@@ -4389,7 +4390,7 @@ var VNModal = ({ title, show, onClose, children }) => {
|
|
|
4389
4390
|
return /* @__PURE__ */ React10.createElement(
|
|
4390
4391
|
"div",
|
|
4391
4392
|
{
|
|
4392
|
-
className: "fixed inset-0 flex items-center justify-center backdrop-blur-xl z-[
|
|
4393
|
+
className: "fixed inset-0 flex items-center justify-center backdrop-blur-xl z-[1000] pointer-events-auto transition-all animate-in fade-in zoom-in-95 duration-300 px-4",
|
|
4393
4394
|
style: { background: "rgba(100, 116, 139, 0.3)" },
|
|
4394
4395
|
onClick: onClose
|
|
4395
4396
|
},
|
|
@@ -6312,499 +6313,595 @@ var MMDMusicPlayer = forwardRef(
|
|
|
6312
6313
|
}
|
|
6313
6314
|
);
|
|
6314
6315
|
MMDMusicPlayer.displayName = "MMDMusicPlayer";
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
var ARMode = /* @__PURE__ */ ((ARMode2) => {
|
|
6318
|
-
ARMode2["Overlay"] = "overlay";
|
|
6319
|
-
ARMode2["WorldFixed"] = "world-fixed";
|
|
6320
|
-
return ARMode2;
|
|
6321
|
-
})(ARMode || {});
|
|
6322
|
-
function Select({
|
|
6323
|
-
label,
|
|
6324
|
-
options,
|
|
6325
|
-
value,
|
|
6326
|
-
onChange,
|
|
6327
|
-
placeholder = "\u8BF7\u9009\u62E9...",
|
|
6328
|
-
allowEmpty = false,
|
|
6329
|
-
emptyLabel = "\u65E0"
|
|
6330
|
-
}) {
|
|
6331
|
-
const selectedOption = options.find((opt) => opt.id === value);
|
|
6332
|
-
const showPlaceholder = !selectedOption && !allowEmpty && value !== "";
|
|
6333
|
-
return /* @__PURE__ */ React10.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React10.createElement("label", { className: "block text-xs font-medium text-white/50 ml-1 uppercase tracking-wider" }, label), /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(
|
|
6334
|
-
"select",
|
|
6335
|
-
{
|
|
6336
|
-
value,
|
|
6337
|
-
onChange: (e) => onChange(e.target.value),
|
|
6338
|
-
className: "w-full appearance-none bg-white/5 border border-white/10 rounded-xl px-4 py-3 pr-10 text-sm text-white focus:outline-none focus:border-cyan-500/50 transition-colors cursor-pointer hover:bg-white/10"
|
|
6339
|
-
},
|
|
6340
|
-
showPlaceholder && /* @__PURE__ */ React10.createElement("option", { value: "", disabled: true, className: "bg-gray-900 text-white/50" }, placeholder),
|
|
6341
|
-
allowEmpty && /* @__PURE__ */ React10.createElement("option", { value: "", className: "bg-gray-900 text-white/60" }, emptyLabel),
|
|
6342
|
-
options.map((option) => /* @__PURE__ */ React10.createElement(
|
|
6343
|
-
"option",
|
|
6344
|
-
{
|
|
6345
|
-
key: option.id,
|
|
6346
|
-
value: option.id,
|
|
6347
|
-
className: "bg-gray-900 text-white"
|
|
6348
|
-
},
|
|
6349
|
-
option.name
|
|
6350
|
-
))
|
|
6351
|
-
), /* @__PURE__ */ React10.createElement(ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/40 pointer-events-none" })));
|
|
6352
|
-
}
|
|
6353
|
-
function ARModeSwitch({ mode, onChange, gyroSupported }) {
|
|
6354
|
-
return /* @__PURE__ */ React10.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React10.createElement("label", { className: "block text-xs font-medium text-white/50 ml-1 uppercase tracking-wider" }, "AR \u6A21\u5F0F"), /* @__PURE__ */ React10.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React10.createElement(
|
|
6355
|
-
"button",
|
|
6356
|
-
{
|
|
6357
|
-
onClick: () => onChange("overlay" /* Overlay */),
|
|
6358
|
-
className: `p-3 rounded-xl border transition-all flex flex-col items-center gap-1.5 ${mode === "overlay" /* Overlay */ ? "bg-cyan-500/20 border-cyan-500/50 text-cyan-400" : "bg-white/5 border-white/10 text-white/60 hover:bg-white/10"}`
|
|
6359
|
-
},
|
|
6360
|
-
/* @__PURE__ */ React10.createElement(Layers, { className: "w-5 h-5" }),
|
|
6361
|
-
/* @__PURE__ */ React10.createElement("span", { className: "text-xs font-medium" }, "\u53E0\u52A0\u6A21\u5F0F")
|
|
6362
|
-
), /* @__PURE__ */ React10.createElement(
|
|
6363
|
-
"button",
|
|
6364
|
-
{
|
|
6365
|
-
onClick: () => gyroSupported && onChange("world-fixed" /* WorldFixed */),
|
|
6366
|
-
disabled: !gyroSupported,
|
|
6367
|
-
className: `p-3 rounded-xl border transition-all flex flex-col items-center gap-1.5 ${mode === "world-fixed" /* WorldFixed */ ? "bg-purple-500/20 border-purple-500/50 text-purple-400" : gyroSupported ? "bg-white/5 border-white/10 text-white/60 hover:bg-white/10" : "bg-white/5 border-white/10 text-white/30 cursor-not-allowed"}`,
|
|
6368
|
-
title: gyroSupported ? "\u4E16\u754C\u56FA\u5B9A\u6A21\u5F0F" : "\u8BBE\u5907\u4E0D\u652F\u6301\u9640\u87BA\u4EEA"
|
|
6369
|
-
},
|
|
6370
|
-
/* @__PURE__ */ React10.createElement(Compass, { className: "w-5 h-5" }),
|
|
6371
|
-
/* @__PURE__ */ React10.createElement("span", { className: "text-xs font-medium" }, "\u4E16\u754C\u56FA\u5B9A"),
|
|
6372
|
-
!gyroSupported && /* @__PURE__ */ React10.createElement("span", { className: "text-[10px] text-red-400" }, "\u4E0D\u652F\u6301")
|
|
6373
|
-
)), /* @__PURE__ */ React10.createElement("p", { className: "text-[10px] text-white/40 ml-1 mt-1" }, mode === "overlay" /* Overlay */ ? "\u6A21\u578B\u56FA\u5B9A\u5728\u5C4F\u5E55\u4E0A" : "\u6A21\u578B\u56FA\u5B9A\u5728\u4E16\u754C\u7A7A\u95F4\uFF0C\u79FB\u52A8\u8BBE\u5907\u67E5\u770B"));
|
|
6316
|
+
if (typeof window !== "undefined") {
|
|
6317
|
+
window.THREE = THREE2;
|
|
6374
6318
|
}
|
|
6375
|
-
var MMDARPlayer = forwardRef((
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
showSettings = true,
|
|
6382
|
-
modelPresets,
|
|
6383
|
-
motionPresets,
|
|
6384
|
-
audioPresets = [],
|
|
6385
|
-
defaultModelId,
|
|
6386
|
-
defaultMotionId,
|
|
6387
|
-
defaultAudioId,
|
|
6388
|
-
initialModelVisible = false,
|
|
6389
|
-
placementText = "TOUCH!",
|
|
6390
|
-
defaultARMode = "overlay" /* Overlay */,
|
|
6391
|
-
autoPlay = true,
|
|
6392
|
-
loop = true,
|
|
6393
|
-
onCameraReady,
|
|
6394
|
-
onCameraError,
|
|
6395
|
-
onResourcesChange,
|
|
6396
|
-
onModelPlaced,
|
|
6397
|
-
onARModeChange,
|
|
6398
|
-
onLoad,
|
|
6399
|
-
onError,
|
|
6400
|
-
className,
|
|
6401
|
-
style
|
|
6402
|
-
} = props;
|
|
6403
|
-
const initialModelId = defaultModelId || modelPresets[0]?.id || "";
|
|
6404
|
-
const initialMotionId = defaultMotionId || motionPresets[0]?.id || "";
|
|
6405
|
-
const initialAudioId = defaultAudioId || audioPresets[0]?.id || "";
|
|
6406
|
-
const videoRef = useRef(null);
|
|
6407
|
-
const playerRef = useRef(null);
|
|
6408
|
-
const streamRef = useRef(null);
|
|
6319
|
+
var MMDARPlayer = forwardRef(({
|
|
6320
|
+
width = 800,
|
|
6321
|
+
height = 600,
|
|
6322
|
+
onReady,
|
|
6323
|
+
onError
|
|
6324
|
+
}, ref) => {
|
|
6409
6325
|
const containerRef = useRef(null);
|
|
6326
|
+
const canvasRef = useRef(null);
|
|
6327
|
+
const sceneRef = useRef();
|
|
6328
|
+
const cameraRef = useRef();
|
|
6329
|
+
const rendererRef = useRef();
|
|
6330
|
+
const arToolkitSourceRef = useRef();
|
|
6331
|
+
const arToolkitContextRef = useRef();
|
|
6332
|
+
const markerRootRef = useRef();
|
|
6333
|
+
const markerControlsRef = useRef();
|
|
6334
|
+
const modelRootRef = useRef();
|
|
6335
|
+
const modelRef = useRef();
|
|
6336
|
+
const [state, setState] = useState({
|
|
6337
|
+
isLoading: true,
|
|
6338
|
+
cameraReady: false,
|
|
6339
|
+
arReady: false,
|
|
6340
|
+
error: null,
|
|
6341
|
+
showSettings: false,
|
|
6342
|
+
modelPlaced: false,
|
|
6343
|
+
markerDetected: false,
|
|
6344
|
+
selectedModel: "sphere",
|
|
6345
|
+
selectedMotion: "idle",
|
|
6346
|
+
selectedAudio: "none",
|
|
6347
|
+
cameraFacing: "environment",
|
|
6348
|
+
markerType: "barcode",
|
|
6349
|
+
showWireframe: false,
|
|
6350
|
+
lightingEnabled: true,
|
|
6351
|
+
quality: "medium"
|
|
6352
|
+
});
|
|
6410
6353
|
const gyroDataRef = useRef({ alpha: 0, beta: 0, gamma: 0 });
|
|
6411
|
-
const
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
if (typeof window === "undefined") return;
|
|
6440
|
-
if (!("DeviceOrientationEvent" in window)) {
|
|
6441
|
-
setGyroSupported(false);
|
|
6442
|
-
return;
|
|
6354
|
+
const initializeThreeJS = useCallback(() => {
|
|
6355
|
+
try {
|
|
6356
|
+
const scene = new THREE2.Scene();
|
|
6357
|
+
sceneRef.current = scene;
|
|
6358
|
+
const camera = new THREE2.Camera();
|
|
6359
|
+
cameraRef.current = camera;
|
|
6360
|
+
scene.add(camera);
|
|
6361
|
+
const renderer = new THREE2.WebGLRenderer({
|
|
6362
|
+
canvas: canvasRef.current,
|
|
6363
|
+
antialias: true,
|
|
6364
|
+
alpha: true
|
|
6365
|
+
});
|
|
6366
|
+
renderer.setSize(width, height);
|
|
6367
|
+
renderer.setClearColor(0, 0);
|
|
6368
|
+
rendererRef.current = renderer;
|
|
6369
|
+
console.log("Three.js initialized successfully");
|
|
6370
|
+
return true;
|
|
6371
|
+
} catch (error) {
|
|
6372
|
+
console.error("Failed to initialize Three.js:", error);
|
|
6373
|
+
setState((prev) => ({ ...prev, error: "Three.js \u521D\u59CB\u5316\u5931\u8D25" }));
|
|
6374
|
+
return false;
|
|
6375
|
+
}
|
|
6376
|
+
}, [width, height]);
|
|
6377
|
+
const requestCameraPermission = useCallback(async () => {
|
|
6378
|
+
try {
|
|
6379
|
+
console.log("Checking camera support...");
|
|
6380
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
6381
|
+
throw new Error("\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u6444\u50CF\u5934\u8BBF\u95EE");
|
|
6443
6382
|
}
|
|
6444
|
-
|
|
6383
|
+
console.log("Camera API supported, checking permissions...");
|
|
6384
|
+
if (navigator.permissions) {
|
|
6445
6385
|
try {
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6386
|
+
console.log("Querying camera permission status...");
|
|
6387
|
+
const permissionPromise = navigator.permissions.query({ name: "camera" });
|
|
6388
|
+
const timeoutPromise2 = new Promise((_, reject) => {
|
|
6389
|
+
setTimeout(() => reject(new Error("Permission query timeout")), 5e3);
|
|
6390
|
+
});
|
|
6391
|
+
const permissionStatus = await Promise.race([permissionPromise, timeoutPromise2]);
|
|
6392
|
+
console.log("Permission status:", permissionStatus.state);
|
|
6393
|
+
if (permissionStatus.state === "denied") {
|
|
6394
|
+
throw new Error("\u6444\u50CF\u5934\u6743\u9650\u5DF2\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u5728\u6D4F\u89C8\u5668\u8BBE\u7F6E\u4E2D\u5141\u8BB8\u8BBF\u95EE\u6444\u50CF\u5934");
|
|
6395
|
+
}
|
|
6396
|
+
} catch (permissionError) {
|
|
6397
|
+
console.warn("Permission query failed or timed out, proceeding with getUserMedia:", permissionError);
|
|
6451
6398
|
}
|
|
6452
|
-
} else {
|
|
6453
|
-
setGyroSupported(true);
|
|
6454
6399
|
}
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
if (!initialOrientationRef.current) {
|
|
6464
|
-
initialOrientationRef.current = { alpha, beta, gamma };
|
|
6465
|
-
}
|
|
6466
|
-
const initial = initialOrientationRef.current;
|
|
6467
|
-
const deltaAlpha = alpha - initial.alpha;
|
|
6468
|
-
const deltaBeta = beta - initial.beta;
|
|
6469
|
-
const deltaGamma = gamma - initial.gamma;
|
|
6470
|
-
gyroDataRef.current = { alpha: deltaAlpha, beta: deltaBeta, gamma: deltaGamma };
|
|
6471
|
-
setModelRotation({
|
|
6472
|
-
x: -deltaBeta * (Math.PI / 180) * 0.5,
|
|
6473
|
-
// 俯仰
|
|
6474
|
-
y: -deltaAlpha * (Math.PI / 180) * 0.5,
|
|
6475
|
-
// 偏航
|
|
6476
|
-
z: deltaGamma * (Math.PI / 180) * 0.3
|
|
6477
|
-
// 翻滚
|
|
6400
|
+
console.log("Requesting camera access...");
|
|
6401
|
+
const cameraPromise = navigator.mediaDevices.getUserMedia({
|
|
6402
|
+
video: {
|
|
6403
|
+
width: { ideal: width },
|
|
6404
|
+
height: { ideal: height },
|
|
6405
|
+
facingMode: "environment"
|
|
6406
|
+
// 优先使用后置摄像头
|
|
6407
|
+
}
|
|
6478
6408
|
});
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6409
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
6410
|
+
setTimeout(() => reject(new Error("Camera access timeout - \u8BF7\u68C0\u67E5\u6444\u50CF\u5934\u6743\u9650")), 1e4);
|
|
6411
|
+
});
|
|
6412
|
+
const testStream = await Promise.race([cameraPromise, timeoutPromise]);
|
|
6413
|
+
console.log("Camera access granted, stopping test stream...");
|
|
6414
|
+
testStream.getTracks().forEach((track) => track.stop());
|
|
6415
|
+
console.log("Camera permission granted successfully");
|
|
6416
|
+
return true;
|
|
6417
|
+
} catch (error) {
|
|
6418
|
+
console.error("Camera permission denied:", error);
|
|
6419
|
+
const errorMessage = error instanceof Error ? error.message : "\u65E0\u6CD5\u8BBF\u95EE\u6444\u50CF\u5934";
|
|
6420
|
+
setState((prev) => ({ ...prev, error: errorMessage, isLoading: false }));
|
|
6421
|
+
onError?.(errorMessage);
|
|
6422
|
+
return false;
|
|
6423
|
+
}
|
|
6424
|
+
}, [width, height, onError]);
|
|
6425
|
+
const createModel = useCallback((modelType) => {
|
|
6426
|
+
let geometry;
|
|
6427
|
+
let material;
|
|
6428
|
+
let mesh;
|
|
6429
|
+
switch (modelType) {
|
|
6430
|
+
case "sphere":
|
|
6431
|
+
geometry = new THREE2.SphereGeometry(0.5, 32, 32);
|
|
6432
|
+
material = new THREE2.MeshPhongMaterial({
|
|
6433
|
+
color: 16738740,
|
|
6434
|
+
shininess: 100,
|
|
6435
|
+
specular: 1118481
|
|
6436
|
+
});
|
|
6437
|
+
mesh = new THREE2.Mesh(geometry, material);
|
|
6438
|
+
break;
|
|
6439
|
+
case "cube":
|
|
6440
|
+
geometry = new THREE2.BoxGeometry(0.8, 0.8, 0.8);
|
|
6441
|
+
material = new THREE2.MeshPhongMaterial({
|
|
6442
|
+
color: 65407,
|
|
6443
|
+
shininess: 100,
|
|
6444
|
+
specular: 1118481
|
|
6445
|
+
});
|
|
6446
|
+
mesh = new THREE2.Mesh(geometry, material);
|
|
6447
|
+
break;
|
|
6448
|
+
case "torus":
|
|
6449
|
+
geometry = new THREE2.TorusGeometry(0.4, 0.2, 16, 100);
|
|
6450
|
+
material = new THREE2.MeshPhongMaterial({
|
|
6451
|
+
color: 16753920,
|
|
6452
|
+
shininess: 100,
|
|
6453
|
+
specular: 1118481
|
|
6454
|
+
});
|
|
6455
|
+
mesh = new THREE2.Mesh(geometry, material);
|
|
6456
|
+
break;
|
|
6457
|
+
default:
|
|
6458
|
+
geometry = new THREE2.SphereGeometry(0.5, 32, 32);
|
|
6459
|
+
material = new THREE2.MeshPhongMaterial({
|
|
6460
|
+
color: 16738740,
|
|
6461
|
+
shininess: 100,
|
|
6462
|
+
specular: 1118481
|
|
6463
|
+
});
|
|
6464
|
+
mesh = new THREE2.Mesh(geometry, material);
|
|
6495
6465
|
}
|
|
6496
|
-
|
|
6466
|
+
mesh.rotation.x = Math.PI / 4;
|
|
6467
|
+
mesh.rotation.y = Math.PI / 4;
|
|
6468
|
+
return mesh;
|
|
6497
6469
|
}, []);
|
|
6498
|
-
const setARMode = useCallback(async (mode) => {
|
|
6499
|
-
if (mode === "world-fixed" /* WorldFixed */) {
|
|
6500
|
-
const hasPermission = await requestGyroPermission();
|
|
6501
|
-
if (!hasPermission) {
|
|
6502
|
-
console.warn("[MMDARPlayer] Gyro permission denied, staying in Overlay mode");
|
|
6503
|
-
return;
|
|
6504
|
-
}
|
|
6505
|
-
initialOrientationRef.current = null;
|
|
6506
|
-
}
|
|
6507
|
-
setARModeState(mode);
|
|
6508
|
-
onARModeChange?.(mode);
|
|
6509
|
-
}, [requestGyroPermission, onARModeChange]);
|
|
6510
6470
|
const placeModel = useCallback(() => {
|
|
6511
|
-
if (
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
initialOrientationRef.current = null;
|
|
6515
|
-
setTimeout(() => {
|
|
6516
|
-
setIsModelPlaced(true);
|
|
6517
|
-
setPlacementAnimation(false);
|
|
6518
|
-
onModelPlaced?.();
|
|
6519
|
-
}, 300);
|
|
6520
|
-
}, [isModelPlaced, onModelPlaced]);
|
|
6521
|
-
const removeModel = useCallback(() => {
|
|
6522
|
-
setIsModelPlaced(false);
|
|
6523
|
-
setIsLoading(false);
|
|
6524
|
-
initialOrientationRef.current = null;
|
|
6525
|
-
}, []);
|
|
6526
|
-
const switchModel = useCallback((newResources) => {
|
|
6527
|
-
const matchedModel = modelPresets.find((m) => m.modelPath === newResources.modelPath);
|
|
6528
|
-
const matchedMotion = motionPresets.find((m) => m.motionPath === newResources.motionPath);
|
|
6529
|
-
const matchedAudio = audioPresets.find((a) => a.audioPath === newResources.audioPath);
|
|
6530
|
-
if (matchedModel) setSelectedModelId(matchedModel.id);
|
|
6531
|
-
if (matchedMotion) setSelectedMotionId(matchedMotion.id);
|
|
6532
|
-
if (matchedAudio) setSelectedAudioId(matchedAudio.id);
|
|
6533
|
-
onResourcesChange?.(newResources);
|
|
6534
|
-
if (isModelPlaced) {
|
|
6535
|
-
setIsLoading(true);
|
|
6536
|
-
}
|
|
6537
|
-
}, [isModelPlaced, onResourcesChange, modelPresets, motionPresets, audioPresets]);
|
|
6538
|
-
const applySettings = useCallback(() => {
|
|
6539
|
-
onResourcesChange?.(currentResources);
|
|
6540
|
-
setIsSettingsOpen(false);
|
|
6541
|
-
if (isModelPlaced) {
|
|
6542
|
-
setIsLoading(true);
|
|
6471
|
+
if (!markerRootRef.current || !modelRootRef.current || !sceneRef.current) {
|
|
6472
|
+
console.error("Cannot place model: missing required references");
|
|
6473
|
+
return;
|
|
6543
6474
|
}
|
|
6544
|
-
}, [currentResources, isModelPlaced, onResourcesChange]);
|
|
6545
|
-
const resetPosition = useCallback(() => {
|
|
6546
|
-
setIsModelPlaced(false);
|
|
6547
|
-
setIsLoading(false);
|
|
6548
|
-
setIsSettingsOpen(false);
|
|
6549
|
-
initialOrientationRef.current = null;
|
|
6550
|
-
setModelRotation({ x: 0, y: 0, z: 0 });
|
|
6551
|
-
}, []);
|
|
6552
|
-
const startCamera = useCallback(async (mode = facingMode) => {
|
|
6553
6475
|
try {
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
if (videoRef.current) {
|
|
6569
|
-
videoRef.current.srcObject = stream;
|
|
6570
|
-
await videoRef.current.play();
|
|
6571
|
-
}
|
|
6572
|
-
setIsCameraStarted(true);
|
|
6573
|
-
setFacingMode(mode);
|
|
6574
|
-
onCameraReady?.(stream);
|
|
6575
|
-
} catch (err) {
|
|
6576
|
-
console.error("[MMDARPlayer] Camera access error:", err);
|
|
6577
|
-
const errorMsg = err.name === "NotAllowedError" ? "\u6444\u50CF\u5934\u6743\u9650\u88AB\u62D2\u7EDD" : `\u65E0\u6CD5\u8BBF\u95EE\u6444\u50CF\u5934: ${err.message}`;
|
|
6578
|
-
setCameraError(errorMsg);
|
|
6579
|
-
onCameraError?.(err instanceof Error ? err : new Error(errorMsg));
|
|
6580
|
-
}
|
|
6581
|
-
}, [facingMode, cameraConfig, onCameraReady, onCameraError]);
|
|
6582
|
-
const stopCamera = useCallback(() => {
|
|
6583
|
-
if (streamRef.current) {
|
|
6584
|
-
streamRef.current.getTracks().forEach((track) => track.stop());
|
|
6585
|
-
streamRef.current = null;
|
|
6476
|
+
if (modelRef.current) {
|
|
6477
|
+
modelRootRef.current.remove(modelRef.current);
|
|
6478
|
+
}
|
|
6479
|
+
const model = createModel(state.selectedModel);
|
|
6480
|
+
modelRef.current = model;
|
|
6481
|
+
modelRootRef.current.position.copy(markerRootRef.current.position);
|
|
6482
|
+
modelRootRef.current.quaternion.copy(markerRootRef.current.quaternion);
|
|
6483
|
+
modelRootRef.current.add(model);
|
|
6484
|
+
modelRootRef.current.visible = true;
|
|
6485
|
+
setState((prev) => ({ ...prev, modelPlaced: true }));
|
|
6486
|
+
console.log("Model placed successfully at marker position");
|
|
6487
|
+
} catch (error) {
|
|
6488
|
+
console.error("Failed to place model:", error);
|
|
6489
|
+
setState((prev) => ({ ...prev, error: "\u653E\u7F6E\u6A21\u578B\u5931\u8D25" }));
|
|
6586
6490
|
}
|
|
6587
|
-
|
|
6588
|
-
|
|
6491
|
+
}, [state.selectedModel, createModel]);
|
|
6492
|
+
const handleARSettingChange = useCallback((setting, value) => {
|
|
6493
|
+
setState((prev) => ({ ...prev, [setting]: value }));
|
|
6494
|
+
if (setting === "cameraFacing" || setting === "markerType" || setting === "quality") {
|
|
6495
|
+
setTimeout(() => {
|
|
6496
|
+
alert("\u6B64\u8BBE\u7F6E\u53D8\u66F4\u9700\u8981\u91CD\u65B0\u542F\u52A8 AR \u7CFB\u7EDF\u3002\u8BF7\u5237\u65B0\u9875\u9762\u4EE5\u5E94\u7528\u65B0\u8BBE\u7F6E\u3002");
|
|
6497
|
+
}, 100);
|
|
6589
6498
|
}
|
|
6590
|
-
setIsCameraStarted(false);
|
|
6591
6499
|
}, []);
|
|
6592
|
-
const
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
const snapshot = useCallback(async () => {
|
|
6597
|
-
if (!videoRef.current || !playerRef.current) return "";
|
|
6598
|
-
const canvas = document.createElement("canvas");
|
|
6599
|
-
const video = videoRef.current;
|
|
6600
|
-
canvas.width = video.videoWidth;
|
|
6601
|
-
canvas.height = video.videoHeight;
|
|
6602
|
-
const ctx = canvas.getContext("2d");
|
|
6603
|
-
if (!ctx) return "";
|
|
6604
|
-
if (shouldMirror) {
|
|
6605
|
-
ctx.translate(canvas.width, 0);
|
|
6606
|
-
ctx.scale(-1, 1);
|
|
6607
|
-
}
|
|
6608
|
-
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
6609
|
-
if (shouldMirror) {
|
|
6610
|
-
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
6611
|
-
}
|
|
6612
|
-
if (isModelPlaced) {
|
|
6613
|
-
const mmdBase64 = playerRef.current.snapshot();
|
|
6614
|
-
const mmdImg = new Image();
|
|
6615
|
-
mmdImg.src = mmdBase64;
|
|
6616
|
-
await new Promise((resolve) => {
|
|
6617
|
-
mmdImg.onload = () => {
|
|
6618
|
-
ctx.drawImage(mmdImg, 0, 0, canvas.width, canvas.height);
|
|
6619
|
-
resolve(null);
|
|
6620
|
-
};
|
|
6621
|
-
});
|
|
6500
|
+
const takePhoto = useCallback(() => {
|
|
6501
|
+
if (!rendererRef.current || !sceneRef.current || !cameraRef.current) {
|
|
6502
|
+
console.error("Cannot take photo: missing required references");
|
|
6503
|
+
return;
|
|
6622
6504
|
}
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6505
|
+
try {
|
|
6506
|
+
const canvas = document.createElement("canvas");
|
|
6507
|
+
const context = canvas.getContext("2d");
|
|
6508
|
+
if (!context) {
|
|
6509
|
+
throw new Error("Cannot create canvas context");
|
|
6510
|
+
}
|
|
6511
|
+
canvas.width = width;
|
|
6512
|
+
canvas.height = height;
|
|
6513
|
+
rendererRef.current.render(sceneRef.current, cameraRef.current);
|
|
6514
|
+
const rendererCanvas = rendererRef.current.domElement;
|
|
6515
|
+
context.drawImage(rendererCanvas, 0, 0, width, height);
|
|
6516
|
+
if (arToolkitSourceRef.current && arToolkitSourceRef.current.domElement) {
|
|
6517
|
+
const videoElement = arToolkitSourceRef.current.domElement;
|
|
6518
|
+
context.globalCompositeOperation = "source-over";
|
|
6519
|
+
context.drawImage(videoElement, 0, 0, width, height);
|
|
6520
|
+
}
|
|
6521
|
+
canvas.toBlob((blob) => {
|
|
6522
|
+
if (blob) {
|
|
6523
|
+
const url = URL.createObjectURL(blob);
|
|
6524
|
+
const a = document.createElement("a");
|
|
6525
|
+
a.href = url;
|
|
6526
|
+
a.download = `ar-photo-${Date.now()}.png`;
|
|
6527
|
+
document.body.appendChild(a);
|
|
6528
|
+
a.click();
|
|
6529
|
+
document.body.removeChild(a);
|
|
6530
|
+
URL.revokeObjectURL(url);
|
|
6531
|
+
console.log("Photo saved successfully");
|
|
6532
|
+
}
|
|
6533
|
+
}, "image/png");
|
|
6534
|
+
} catch (error) {
|
|
6535
|
+
console.error("Failed to take photo:", error);
|
|
6536
|
+
setState((prev) => ({ ...prev, error: "\u62CD\u7167\u5931\u8D25" }));
|
|
6639
6537
|
}
|
|
6640
|
-
|
|
6641
|
-
|
|
6538
|
+
}, [width, height]);
|
|
6539
|
+
const initializeAR = useCallback(async () => {
|
|
6540
|
+
try {
|
|
6541
|
+
console.log("Starting AR initialization...");
|
|
6542
|
+
if (!window.__arjs_ready) {
|
|
6543
|
+
console.log("Waiting for AR.js initialization...");
|
|
6544
|
+
await new Promise((resolve, reject) => {
|
|
6545
|
+
const check = setInterval(() => {
|
|
6546
|
+
if (window.__arjs_ready) {
|
|
6547
|
+
clearInterval(check);
|
|
6548
|
+
console.log("AR.js initialization complete!");
|
|
6549
|
+
resolve();
|
|
6550
|
+
}
|
|
6551
|
+
}, 50);
|
|
6552
|
+
setTimeout(() => {
|
|
6553
|
+
clearInterval(check);
|
|
6554
|
+
reject(new Error("AR.js initialization timeout"));
|
|
6555
|
+
}, 15e3);
|
|
6556
|
+
});
|
|
6557
|
+
}
|
|
6558
|
+
console.log("Getting THREEx...");
|
|
6559
|
+
const THREEx = window.THREEx;
|
|
6560
|
+
if (!THREEx) {
|
|
6561
|
+
console.error("THREEx not found, available window properties:", Object.keys(window).filter((key) => key.toLowerCase().includes("ar") || key.toLowerCase().includes("three")));
|
|
6562
|
+
throw new Error("THREEx not found after AR.js loaded");
|
|
6563
|
+
}
|
|
6564
|
+
console.log("THREEx loaded successfully:", Object.keys(THREEx));
|
|
6565
|
+
console.log("THREEx loaded, requesting camera permission...");
|
|
6566
|
+
const hasPermission = await requestCameraPermission();
|
|
6567
|
+
if (!hasPermission) {
|
|
6568
|
+
console.log("Camera permission denied");
|
|
6569
|
+
return false;
|
|
6570
|
+
}
|
|
6571
|
+
console.log("Camera permission granted, initializing AR Toolkit Source...");
|
|
6572
|
+
const arToolkitSource = new THREEx.ArToolkitSource({
|
|
6573
|
+
sourceType: "webcam",
|
|
6574
|
+
sourceWidth: width,
|
|
6575
|
+
sourceHeight: height,
|
|
6576
|
+
// 使用用户选择的摄像头朝向
|
|
6577
|
+
...state.cameraFacing && { facingMode: state.cameraFacing }
|
|
6578
|
+
});
|
|
6579
|
+
arToolkitSourceRef.current = arToolkitSource;
|
|
6580
|
+
const arToolkitContext = new THREEx.ArToolkitContext({
|
|
6581
|
+
cameraParametersUrl: "data/camera_para.dat",
|
|
6582
|
+
// 使用内建相机参数
|
|
6583
|
+
detectionMode: "mono",
|
|
6584
|
+
// 根据质量设置调整检测参数
|
|
6585
|
+
...state.quality && {
|
|
6586
|
+
maxDetectionRate: state.quality === "high" ? 60 : state.quality === "medium" ? 30 : 15
|
|
6587
|
+
}
|
|
6588
|
+
});
|
|
6589
|
+
arToolkitContextRef.current = arToolkitContext;
|
|
6590
|
+
arToolkitContext.init(() => {
|
|
6591
|
+
cameraRef.current.projectionMatrix.copy(arToolkitContext.getProjectionMatrix());
|
|
6592
|
+
const markerRoot = new THREE2.Group();
|
|
6593
|
+
sceneRef.current.add(markerRoot);
|
|
6594
|
+
markerRootRef.current = markerRoot;
|
|
6595
|
+
const markerGeometry = new THREE2.BoxGeometry(1, 1, 0.1);
|
|
6596
|
+
const markerMaterial = new THREE2.MeshBasicMaterial({
|
|
6597
|
+
color: 65280,
|
|
6598
|
+
transparent: true,
|
|
6599
|
+
opacity: 0.7,
|
|
6600
|
+
wireframe: state.showWireframe
|
|
6601
|
+
// 根据设置显示线框
|
|
6602
|
+
});
|
|
6603
|
+
const markerMesh = new THREE2.Mesh(markerGeometry, markerMaterial);
|
|
6604
|
+
markerMesh.position.set(0, 0, 0);
|
|
6605
|
+
markerRoot.add(markerMesh);
|
|
6606
|
+
if (!state.showWireframe) {
|
|
6607
|
+
const edges = new THREE2.EdgesGeometry(markerGeometry);
|
|
6608
|
+
const lineMaterial = new THREE2.LineBasicMaterial({ color: 16777215 });
|
|
6609
|
+
const wireframe = new THREE2.LineSegments(edges, lineMaterial);
|
|
6610
|
+
markerRoot.add(wireframe);
|
|
6611
|
+
}
|
|
6612
|
+
const markerControls = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, {
|
|
6613
|
+
type: state.markerType,
|
|
6614
|
+
...state.markerType === "barcode" ? { barcodeValue: 0 } : { patternUrl: "data/patt.hiro" }
|
|
6615
|
+
});
|
|
6616
|
+
markerControlsRef.current = markerControls;
|
|
6617
|
+
if (state.lightingEnabled) {
|
|
6618
|
+
const ambientLight = new THREE2.AmbientLight(4210752, 0.6);
|
|
6619
|
+
sceneRef.current.add(ambientLight);
|
|
6620
|
+
const directionalLight = new THREE2.DirectionalLight(16777215, 0.8);
|
|
6621
|
+
directionalLight.position.set(1, 1, 1);
|
|
6622
|
+
sceneRef.current.add(directionalLight);
|
|
6623
|
+
}
|
|
6624
|
+
const modelRoot = new THREE2.Group();
|
|
6625
|
+
modelRoot.visible = false;
|
|
6626
|
+
sceneRef.current.add(modelRoot);
|
|
6627
|
+
modelRootRef.current = modelRoot;
|
|
6628
|
+
setState((prev) => ({
|
|
6629
|
+
...prev,
|
|
6630
|
+
arReady: true,
|
|
6631
|
+
isLoading: false
|
|
6632
|
+
}));
|
|
6633
|
+
onReady?.();
|
|
6634
|
+
console.log("AR.js and marker system initialized successfully");
|
|
6635
|
+
});
|
|
6636
|
+
arToolkitSource.init(() => {
|
|
6637
|
+
arToolkitSource.domElement.style.display = "none";
|
|
6638
|
+
setState((prev) => ({ ...prev, cameraReady: true }));
|
|
6639
|
+
console.log("Camera initialized successfully");
|
|
6640
|
+
});
|
|
6641
|
+
return true;
|
|
6642
|
+
} catch (error) {
|
|
6643
|
+
console.error("Failed to initialize AR.js:", error);
|
|
6644
|
+
const errorMessage = error instanceof Error ? error.message : "AR.js \u521D\u59CB\u5316\u5931\u8D25";
|
|
6645
|
+
setState((prev) => ({
|
|
6646
|
+
...prev,
|
|
6647
|
+
error: errorMessage,
|
|
6648
|
+
isLoading: false
|
|
6649
|
+
}));
|
|
6650
|
+
onError?.(errorMessage);
|
|
6651
|
+
return false;
|
|
6652
|
+
}
|
|
6653
|
+
}, [width, height, requestCameraPermission, onReady, onError]);
|
|
6654
|
+
const render = useCallback(() => {
|
|
6655
|
+
if (!rendererRef.current || !sceneRef.current || !cameraRef.current) return;
|
|
6656
|
+
requestAnimationFrame(render);
|
|
6657
|
+
if (arToolkitSourceRef.current && arToolkitSourceRef.current.ready) {
|
|
6658
|
+
arToolkitContextRef.current.update(arToolkitSourceRef.current.domElement);
|
|
6659
|
+
if (markerRootRef.current && markerRootRef.current.visible !== state.markerDetected) {
|
|
6660
|
+
setState((prev) => ({ ...prev, markerDetected: markerRootRef.current.visible }));
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
if (modelRef.current && state.modelPlaced) {
|
|
6664
|
+
modelRef.current.rotation.y += 0.01;
|
|
6665
|
+
}
|
|
6666
|
+
rendererRef.current.render(sceneRef.current, cameraRef.current);
|
|
6667
|
+
}, [state.markerDetected]);
|
|
6642
6668
|
useEffect(() => {
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6669
|
+
const initialize = async () => {
|
|
6670
|
+
try {
|
|
6671
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
6672
|
+
if (!initializeThreeJS()) return;
|
|
6673
|
+
await initializeAR();
|
|
6674
|
+
if (window.DeviceOrientationEvent) {
|
|
6675
|
+
const handleOrientation = (event) => {
|
|
6676
|
+
gyroDataRef.current = {
|
|
6677
|
+
alpha: event.alpha || 0,
|
|
6678
|
+
beta: event.beta || 0,
|
|
6679
|
+
gamma: event.gamma || 0
|
|
6680
|
+
};
|
|
6681
|
+
if (state.modelPlaced && modelRef.current) {
|
|
6682
|
+
}
|
|
6683
|
+
};
|
|
6684
|
+
window.addEventListener("deviceorientation", handleOrientation);
|
|
6685
|
+
const cleanupGyro = () => {
|
|
6686
|
+
window.removeEventListener("deviceorientation", handleOrientation);
|
|
6687
|
+
};
|
|
6688
|
+
window.__gyroCleanup = cleanupGyro;
|
|
6689
|
+
}
|
|
6690
|
+
render();
|
|
6691
|
+
} catch (error) {
|
|
6692
|
+
console.error("Initialization failed:", error);
|
|
6693
|
+
setState((prev) => ({
|
|
6694
|
+
...prev,
|
|
6695
|
+
error: "\u7EC4\u4EF6\u521D\u59CB\u5316\u5931\u8D25",
|
|
6696
|
+
isLoading: false
|
|
6697
|
+
}));
|
|
6698
|
+
onError?.("\u7EC4\u4EF6\u521D\u59CB\u5316\u5931\u8D25");
|
|
6699
|
+
}
|
|
6651
6700
|
};
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
ref: containerRef,
|
|
6657
|
-
className: `relative w-full h-full bg-black overflow-hidden ${className}`,
|
|
6658
|
-
style
|
|
6659
|
-
},
|
|
6660
|
-
/* @__PURE__ */ React10.createElement(
|
|
6661
|
-
"video",
|
|
6662
|
-
{
|
|
6663
|
-
ref: videoRef,
|
|
6664
|
-
autoPlay: true,
|
|
6665
|
-
playsInline: true,
|
|
6666
|
-
muted: true,
|
|
6667
|
-
className: `absolute inset-0 w-full h-full object-cover transition-transform duration-500 ${shouldMirror ? "scale-x-[-1]" : ""}`,
|
|
6668
|
-
style: { zIndex: 0 }
|
|
6701
|
+
initialize();
|
|
6702
|
+
return () => {
|
|
6703
|
+
if (rendererRef.current) {
|
|
6704
|
+
rendererRef.current.dispose();
|
|
6669
6705
|
}
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
className: `absolute inset-0 w-full h-full transition-all duration-500 ${placementAnimation ? "scale-110 opacity-0" : "scale-100 opacity-100"}`,
|
|
6675
|
-
style: {
|
|
6676
|
-
zIndex: 1,
|
|
6677
|
-
...modelContainerStyle
|
|
6678
|
-
}
|
|
6679
|
-
},
|
|
6680
|
-
/* @__PURE__ */ React10.createElement(
|
|
6681
|
-
MMDPlayerBase,
|
|
6682
|
-
{
|
|
6683
|
-
key: `${selectedModelId}-${selectedMotionId}-${selectedAudioId}-${arMode}`,
|
|
6684
|
-
ref: playerRef,
|
|
6685
|
-
resources: currentResources,
|
|
6686
|
-
stage: {
|
|
6687
|
-
...stage,
|
|
6688
|
-
backgroundColor: "transparent",
|
|
6689
|
-
cameraPosition: stage.cameraPosition || { x: 0, y: 15, z: 40 }
|
|
6690
|
-
},
|
|
6691
|
-
mobileOptimization,
|
|
6692
|
-
autoPlay: true,
|
|
6693
|
-
loop,
|
|
6694
|
-
onLoad: () => {
|
|
6695
|
-
setIsLoading(false);
|
|
6696
|
-
onLoad?.();
|
|
6697
|
-
},
|
|
6698
|
-
onError
|
|
6706
|
+
if (arToolkitSourceRef.current) {
|
|
6707
|
+
if (arToolkitSourceRef.current.domElement?.srcObject) {
|
|
6708
|
+
const stream = arToolkitSourceRef.current.domElement.srcObject;
|
|
6709
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
6699
6710
|
}
|
|
6700
|
-
|
|
6701
|
-
),
|
|
6702
|
-
!isModelPlaced && isCameraStarted && /* @__PURE__ */ React10.createElement(
|
|
6703
|
-
"div",
|
|
6704
|
-
{
|
|
6705
|
-
className: "absolute inset-0 z-5 flex items-center justify-center",
|
|
6706
|
-
onClick: placeModel
|
|
6707
|
-
},
|
|
6708
|
-
/* @__PURE__ */ React10.createElement(
|
|
6709
|
-
"button",
|
|
6710
|
-
{
|
|
6711
|
-
onClick: placeModel,
|
|
6712
|
-
className: `
|
|
6713
|
-
relative group cursor-pointer
|
|
6714
|
-
transition-all duration-300 ease-out
|
|
6715
|
-
hover:scale-110 active:scale-95
|
|
6716
|
-
${placementAnimation ? "scale-150 opacity-0" : "scale-100 opacity-100"}
|
|
6717
|
-
`
|
|
6718
|
-
},
|
|
6719
|
-
/* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 -m-4 rounded-2xl bg-cyan-400/20 animate-ping" }),
|
|
6720
|
-
/* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 -m-2 rounded-xl bg-cyan-400/30 animate-pulse" }),
|
|
6721
|
-
/* @__PURE__ */ React10.createElement("div", { className: "relative bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 p-1 rounded-2xl shadow-2xl shadow-cyan-500/50" }, /* @__PURE__ */ React10.createElement("div", { className: "bg-black/80 backdrop-blur-xl px-8 py-6 rounded-xl flex flex-col items-center gap-3" }, /* @__PURE__ */ React10.createElement("div", { className: "relative" }, /* @__PURE__ */ React10.createElement(Sparkles, { className: "w-10 h-10 text-cyan-400 animate-pulse" }), /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 w-10 h-10 bg-cyan-400/30 blur-xl" })), /* @__PURE__ */ React10.createElement("span", { className: "text-2xl font-black text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-purple-400 tracking-widest" }, placementText), /* @__PURE__ */ React10.createElement("span", { className: "text-xs text-white/50 font-medium" }, arMode === "world-fixed" /* WorldFixed */ ? "\u70B9\u51FB\u653E\u7F6E\u5230\u4E16\u754C\u7A7A\u95F4 \u{1F30D}" : "\u70B9\u51FB\u53EC\u5524 Miku \u2728"))),
|
|
6722
|
-
/* @__PURE__ */ React10.createElement("div", { className: "absolute -top-2 -right-2 w-4 h-4 bg-yellow-400 rounded-full animate-bounce shadow-lg shadow-yellow-400/50" }),
|
|
6723
|
-
/* @__PURE__ */ React10.createElement("div", { className: "absolute -bottom-1 -left-1 w-3 h-3 bg-pink-400 rounded-full animate-bounce delay-100 shadow-lg shadow-pink-400/50" })
|
|
6724
|
-
)
|
|
6725
|
-
),
|
|
6726
|
-
/* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-6" }, /* @__PURE__ */ React10.createElement("div", { className: "flex justify-between items-start pointer-events-auto" }, cameraError ? /* @__PURE__ */ React10.createElement("div", { className: "bg-red-500/80 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React10.createElement(AlertCircle, { className: "w-4 h-4" }), cameraError, /* @__PURE__ */ React10.createElement("button", { onClick: () => startCamera(), className: "ml-2 underline" }, "\u91CD\u8BD5")) : /* @__PURE__ */ React10.createElement("div", { className: `backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm ${arMode === "world-fixed" /* WorldFixed */ ? "bg-purple-500/40" : "bg-black/40"}` }, arMode === "world-fixed" /* WorldFixed */ ? /* @__PURE__ */ React10.createElement(Compass, { className: "w-4 h-4 text-purple-400" }) : /* @__PURE__ */ React10.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? isModelPlaced ? arMode === "world-fixed" /* WorldFixed */ ? "\u4E16\u754C\u56FA\u5B9A AR" : "\u53E0\u52A0 AR \u6A21\u5F0F" : "\u70B9\u51FB\u653E\u7F6E\u6A21\u578B" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React10.createElement(
|
|
6727
|
-
"button",
|
|
6728
|
-
{
|
|
6729
|
-
onClick: () => setIsSettingsOpen(!isSettingsOpen),
|
|
6730
|
-
className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 ${isSettingsOpen ? "bg-cyan-500" : "bg-white/10 hover:bg-white/20"}`
|
|
6731
|
-
},
|
|
6732
|
-
/* @__PURE__ */ React10.createElement(Settings, { className: "w-5 h-5" })
|
|
6733
|
-
), /* @__PURE__ */ React10.createElement(
|
|
6734
|
-
"button",
|
|
6735
|
-
{
|
|
6736
|
-
onClick: switchCamera,
|
|
6737
|
-
className: "p-3 bg-white/10 hover:bg-white/20 backdrop-blur-md rounded-full text-white transition-all active:scale-95"
|
|
6738
|
-
},
|
|
6739
|
-
/* @__PURE__ */ React10.createElement(RefreshCw, { className: "w-5 h-5" })
|
|
6740
|
-
), /* @__PURE__ */ React10.createElement(
|
|
6741
|
-
"button",
|
|
6742
|
-
{
|
|
6743
|
-
onClick: isCameraStarted ? stopCamera : () => startCamera(),
|
|
6744
|
-
className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 ${isCameraStarted ? "bg-red-500/20 hover:bg-red-500/40" : "bg-green-500/20 hover:bg-green-500/40"}`
|
|
6745
|
-
},
|
|
6746
|
-
isCameraStarted ? /* @__PURE__ */ React10.createElement(CameraOff, { className: "w-5 h-5" }) : /* @__PURE__ */ React10.createElement(Camera, { className: "w-5 h-5" })
|
|
6747
|
-
))), isSettingsOpen && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-20 right-6 w-72 max-h-[75vh] overflow-y-auto bg-black/90 backdrop-blur-xl border border-white/10 rounded-2xl p-5 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-between mb-5" }, /* @__PURE__ */ React10.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React10.createElement(Settings, { className: "w-4 h-4 text-cyan-400" }), "AR \u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement(
|
|
6748
|
-
"button",
|
|
6749
|
-
{
|
|
6750
|
-
onClick: () => setIsSettingsOpen(false),
|
|
6751
|
-
className: "p-1.5 hover:bg-white/10 rounded-full transition-colors text-white/60"
|
|
6752
|
-
},
|
|
6753
|
-
/* @__PURE__ */ React10.createElement(X, { className: "w-4 h-4" })
|
|
6754
|
-
)), /* @__PURE__ */ React10.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React10.createElement(
|
|
6755
|
-
ARModeSwitch,
|
|
6756
|
-
{
|
|
6757
|
-
mode: arMode,
|
|
6758
|
-
onChange: setARMode,
|
|
6759
|
-
gyroSupported
|
|
6711
|
+
arToolkitSourceRef.current.domElement?.remove();
|
|
6760
6712
|
}
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
{
|
|
6764
|
-
label: "\u9009\u62E9\u6A21\u578B",
|
|
6765
|
-
options: modelPresets,
|
|
6766
|
-
value: selectedModelId,
|
|
6767
|
-
onChange: setSelectedModelId
|
|
6713
|
+
if (window.__gyroCleanup) {
|
|
6714
|
+
window.__gyroCleanup();
|
|
6768
6715
|
}
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6716
|
+
};
|
|
6717
|
+
}, [initializeThreeJS, initializeAR, render, onError]);
|
|
6718
|
+
return /* @__PURE__ */ React10.createElement("div", { ref: containerRef, className: "relative w-full h-full bg-gray-900 overflow-hidden" }, /* @__PURE__ */ React10.createElement(
|
|
6719
|
+
Script,
|
|
6720
|
+
{
|
|
6721
|
+
src: "https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js",
|
|
6722
|
+
strategy: "afterInteractive",
|
|
6723
|
+
onLoad: () => {
|
|
6724
|
+
console.log("AR.js script loaded, checking THREEx...");
|
|
6725
|
+
const checkTHREEx = () => {
|
|
6726
|
+
if (window.THREEx) {
|
|
6727
|
+
console.log("THREEx found! Properties:", Object.keys(window.THREEx));
|
|
6728
|
+
window.__arjs_ready = true;
|
|
6729
|
+
console.log("AR.js and THREEx ready!");
|
|
6730
|
+
} else {
|
|
6731
|
+
console.log("THREEx not ready yet, checking window object:", Object.keys(window).filter((key) => key.includes("THREEx") || key.includes("AR")));
|
|
6732
|
+
setTimeout(checkTHREEx, 100);
|
|
6733
|
+
}
|
|
6734
|
+
};
|
|
6735
|
+
checkTHREEx();
|
|
6736
|
+
},
|
|
6737
|
+
onError: (error) => {
|
|
6738
|
+
console.error("Failed to load AR.js:", error);
|
|
6739
|
+
setState((prev) => ({
|
|
6740
|
+
...prev,
|
|
6741
|
+
error: "AR.js \u52A0\u8F7D\u5931\u8D25",
|
|
6742
|
+
isLoading: false
|
|
6743
|
+
}));
|
|
6776
6744
|
}
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6745
|
+
}
|
|
6746
|
+
), /* @__PURE__ */ React10.createElement(
|
|
6747
|
+
"canvas",
|
|
6748
|
+
{
|
|
6749
|
+
ref: canvasRef,
|
|
6750
|
+
className: "absolute inset-0 w-full h-full",
|
|
6751
|
+
style: { display: state.arReady ? "block" : "none" }
|
|
6752
|
+
}
|
|
6753
|
+
), state.isLoading && /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-900 text-white" }, /* @__PURE__ */ React10.createElement("div", { className: "text-center" }, /* @__PURE__ */ React10.createElement("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto mb-4" }), /* @__PURE__ */ React10.createElement("p", null, "\u6B63\u5728\u521D\u59CB\u5316 AR \u73AF\u5883..."))), state.error && /* @__PURE__ */ React10.createElement("div", { className: "absolute inset-0 flex items-center justify-center bg-red-900 text-white" }, /* @__PURE__ */ React10.createElement("div", { className: "text-center max-w-md" }, /* @__PURE__ */ React10.createElement("h2", { className: "text-xl font-bold mb-4" }, "\u521D\u59CB\u5316\u5931\u8D25"), /* @__PURE__ */ React10.createElement("p", { className: "text-red-200 mb-4" }, state.error), /* @__PURE__ */ React10.createElement(
|
|
6754
|
+
"button",
|
|
6755
|
+
{
|
|
6756
|
+
onClick: () => window.location.reload(),
|
|
6757
|
+
className: "px-4 py-2 bg-red-600 hover:bg-red-700 rounded-lg transition-colors"
|
|
6758
|
+
},
|
|
6759
|
+
"\u91CD\u65B0\u52A0\u8F7D"
|
|
6760
|
+
))), state.arReady && !state.error && /* @__PURE__ */ React10.createElement(React10.Fragment, null, !state.modelPlaced && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-4 left-4 right-4 bg-black/70 text-white p-4 rounded-lg" }, /* @__PURE__ */ React10.createElement("h3", { className: "font-bold mb-2" }, "AR \u653E\u7F6E\u8BF4\u660E"), /* @__PURE__ */ React10.createElement("p", { className: "text-sm text-gray-300" }, "1. \u5141\u8BB8\u6444\u50CF\u5934\u8BBF\u95EE\u6743\u9650", /* @__PURE__ */ React10.createElement("br", null), "2. \u51C6\u5907\u4E00\u4E2A\u6761\u7801\u6807\u8BB0 (\u503C: 0) \u6216 Hiro \u6807\u8BB0\u56FE\u6848", /* @__PURE__ */ React10.createElement("br", null), "3. \u5C06\u6444\u50CF\u5934\u5BF9\u51C6\u6807\u8BB0\uFF0C\u7EFF\u8272\u7ACB\u65B9\u4F53\u5C06\u51FA\u73B0\u5728\u6807\u8BB0\u4F4D\u7F6E", /* @__PURE__ */ React10.createElement("br", null), state.markerDetected ? /* @__PURE__ */ React10.createElement("span", { className: "text-green-400 font-bold" }, "\u2713 \u6807\u8BB0\u5DF2\u68C0\u6D4B\u5230\uFF01") : /* @__PURE__ */ React10.createElement("span", { className: "text-yellow-400" }, "\u7B49\u5F85\u6807\u8BB0\u68C0\u6D4B..."), /* @__PURE__ */ React10.createElement("br", null), '4. \u70B9\u51FB"\u653E\u7F6E\u6A21\u578B"\u6309\u94AE\u56FA\u5B9A\u6A21\u578B\u4F4D\u7F6E')), state.showSettings && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-4 right-4 bg-black/90 text-white p-4 rounded-lg min-w-80 max-w-sm max-h-96 overflow-y-auto" }, /* @__PURE__ */ React10.createElement("h3", { className: "font-bold mb-4 text-lg" }, "\u2699\uFE0F \u8BBE\u7F6E\u9762\u677F"), /* @__PURE__ */ React10.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React10.createElement("div", { className: "border-b border-gray-600 pb-3" }, /* @__PURE__ */ React10.createElement("h4", { className: "font-semibold mb-2 text-blue-300" }, "\u{1F4F7} \u6444\u50CF\u5934\u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u6444\u50CF\u5934\u671D\u5411"), /* @__PURE__ */ React10.createElement(
|
|
6761
|
+
"select",
|
|
6762
|
+
{
|
|
6763
|
+
value: state.cameraFacing,
|
|
6764
|
+
onChange: (e) => handleARSettingChange("cameraFacing", e.target.value),
|
|
6765
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6766
|
+
},
|
|
6767
|
+
/* @__PURE__ */ React10.createElement("option", { value: "environment" }, "\u540E\u7F6E\u6444\u50CF\u5934"),
|
|
6768
|
+
/* @__PURE__ */ React10.createElement("option", { value: "user" }, "\u524D\u7F6E\u6444\u50CF\u5934")
|
|
6769
|
+
))), /* @__PURE__ */ React10.createElement("div", { className: "border-b border-gray-600 pb-3" }, /* @__PURE__ */ React10.createElement("h4", { className: "font-semibold mb-2 text-green-300" }, "\u{1F3AF} AR \u68C0\u6D4B\u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u6807\u8BB0\u7C7B\u578B"), /* @__PURE__ */ React10.createElement(
|
|
6770
|
+
"select",
|
|
6771
|
+
{
|
|
6772
|
+
value: state.markerType,
|
|
6773
|
+
onChange: (e) => handleARSettingChange("markerType", e.target.value),
|
|
6774
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6775
|
+
},
|
|
6776
|
+
/* @__PURE__ */ React10.createElement("option", { value: "barcode" }, "\u6761\u7801 (Barcode)"),
|
|
6777
|
+
/* @__PURE__ */ React10.createElement("option", { value: "pattern" }, "\u56FE\u6848 (Hiro)")
|
|
6778
|
+
)), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u68C0\u6D4B\u8D28\u91CF"), /* @__PURE__ */ React10.createElement(
|
|
6779
|
+
"select",
|
|
6780
|
+
{
|
|
6781
|
+
value: state.quality,
|
|
6782
|
+
onChange: (e) => handleARSettingChange("quality", e.target.value),
|
|
6783
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6784
|
+
},
|
|
6785
|
+
/* @__PURE__ */ React10.createElement("option", { value: "low" }, "\u4F4E\u8D28\u91CF (15fps)"),
|
|
6786
|
+
/* @__PURE__ */ React10.createElement("option", { value: "medium" }, "\u4E2D\u7B49\u8D28\u91CF (30fps)"),
|
|
6787
|
+
/* @__PURE__ */ React10.createElement("option", { value: "high" }, "\u9AD8\u8D28\u91CF (60fps)")
|
|
6788
|
+
)))), /* @__PURE__ */ React10.createElement("div", { className: "border-b border-gray-600 pb-3" }, /* @__PURE__ */ React10.createElement("h4", { className: "font-semibold mb-2 text-purple-300" }, "\u{1F441}\uFE0F \u89C6\u89C9\u8BBE\u7F6E"), /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React10.createElement("label", { className: "flex items-center" }, /* @__PURE__ */ React10.createElement(
|
|
6789
|
+
"input",
|
|
6790
|
+
{
|
|
6791
|
+
type: "checkbox",
|
|
6792
|
+
checked: state.showWireframe,
|
|
6793
|
+
onChange: (e) => setState((prev) => ({ ...prev, showWireframe: e.target.checked })),
|
|
6794
|
+
className: "mr-2"
|
|
6795
|
+
}
|
|
6796
|
+
), /* @__PURE__ */ React10.createElement("span", { className: "text-sm" }, "\u663E\u793A\u7EBF\u6846")), /* @__PURE__ */ React10.createElement("label", { className: "flex items-center" }, /* @__PURE__ */ React10.createElement(
|
|
6797
|
+
"input",
|
|
6798
|
+
{
|
|
6799
|
+
type: "checkbox",
|
|
6800
|
+
checked: state.lightingEnabled,
|
|
6801
|
+
onChange: (e) => setState((prev) => ({ ...prev, lightingEnabled: e.target.checked })),
|
|
6802
|
+
className: "mr-2"
|
|
6803
|
+
}
|
|
6804
|
+
), /* @__PURE__ */ React10.createElement("span", { className: "text-sm" }, "\u542F\u7528\u5149\u7167")))), /* @__PURE__ */ React10.createElement("div", { className: "border-b border-gray-600 pb-3" }, /* @__PURE__ */ React10.createElement("h4", { className: "font-semibold mb-2 text-orange-300" }, "\u{1F3AD} \u6A21\u578B\u4E0E\u52A8\u753B"), /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u6A21\u578B\u9009\u62E9"), /* @__PURE__ */ React10.createElement(
|
|
6805
|
+
"select",
|
|
6806
|
+
{
|
|
6807
|
+
value: state.selectedModel,
|
|
6808
|
+
onChange: (e) => setState((prev) => ({ ...prev, selectedModel: e.target.value })),
|
|
6809
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6810
|
+
},
|
|
6811
|
+
/* @__PURE__ */ React10.createElement("option", { value: "sphere" }, "\u{1F310} \u7403\u4F53"),
|
|
6812
|
+
/* @__PURE__ */ React10.createElement("option", { value: "cube" }, "\u2B1C \u7ACB\u65B9\u4F53"),
|
|
6813
|
+
/* @__PURE__ */ React10.createElement("option", { value: "torus" }, "\u2B55 \u5706\u73AF")
|
|
6814
|
+
)), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u52A8\u4F5C\u9009\u62E9"), /* @__PURE__ */ React10.createElement(
|
|
6815
|
+
"select",
|
|
6816
|
+
{
|
|
6817
|
+
value: state.selectedMotion,
|
|
6818
|
+
onChange: (e) => setState((prev) => ({ ...prev, selectedMotion: e.target.value })),
|
|
6819
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6820
|
+
},
|
|
6821
|
+
/* @__PURE__ */ React10.createElement("option", { value: "idle" }, "\u{1F9D8} \u5F85\u673A"),
|
|
6822
|
+
/* @__PURE__ */ React10.createElement("option", { value: "dance" }, "\u{1F483} \u821E\u8E48"),
|
|
6823
|
+
/* @__PURE__ */ React10.createElement("option", { value: "wave" }, "\u{1F44B} \u6325\u624B")
|
|
6824
|
+
)), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium mb-1" }, "\u97F3\u4E50\u9009\u62E9"), /* @__PURE__ */ React10.createElement(
|
|
6825
|
+
"select",
|
|
6826
|
+
{
|
|
6827
|
+
value: state.selectedAudio,
|
|
6828
|
+
onChange: (e) => setState((prev) => ({ ...prev, selectedAudio: e.target.value })),
|
|
6829
|
+
className: "w-full px-3 py-2 bg-gray-700 rounded border border-gray-600 text-sm"
|
|
6830
|
+
},
|
|
6831
|
+
/* @__PURE__ */ React10.createElement("option", { value: "none" }, "\u{1F507} \u65E0\u97F3\u4E50"),
|
|
6832
|
+
/* @__PURE__ */ React10.createElement("option", { value: "bgm1" }, "\u{1F3B5} \u80CC\u666F\u97F3\u4E50 1"),
|
|
6833
|
+
/* @__PURE__ */ React10.createElement("option", { value: "bgm2" }, "\u{1F3B6} \u80CC\u666F\u97F3\u4E50 2")
|
|
6834
|
+
)))), /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement("h4", { className: "font-semibold mb-2 text-red-300" }, "\u{1F3AE} \u63A7\u5236\u64CD\u4F5C"), /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React10.createElement(
|
|
6835
|
+
"button",
|
|
6836
|
+
{
|
|
6837
|
+
onClick: () => {
|
|
6838
|
+
if (modelRootRef.current) {
|
|
6839
|
+
modelRootRef.current.visible = false;
|
|
6840
|
+
}
|
|
6841
|
+
if (markerRootRef.current) {
|
|
6842
|
+
markerRootRef.current.visible = true;
|
|
6843
|
+
}
|
|
6844
|
+
setState((prev) => ({ ...prev, modelPlaced: false, markerDetected: false }));
|
|
6792
6845
|
},
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6846
|
+
className: "w-full px-3 py-2 bg-blue-600 hover:bg-blue-700 rounded transition-colors text-sm"
|
|
6847
|
+
},
|
|
6848
|
+
"\u{1F504} \u91CD\u65B0\u8BBE\u7F6E\u6807\u8BB0\u70B9"
|
|
6849
|
+
), /* @__PURE__ */ React10.createElement(
|
|
6850
|
+
"button",
|
|
6851
|
+
{
|
|
6852
|
+
onClick: () => {
|
|
6853
|
+
if (modelRootRef.current) {
|
|
6854
|
+
modelRootRef.current.clear();
|
|
6855
|
+
modelRootRef.current.visible = false;
|
|
6856
|
+
}
|
|
6857
|
+
if (markerRootRef.current) {
|
|
6858
|
+
markerRootRef.current.visible = true;
|
|
6859
|
+
}
|
|
6860
|
+
setState((prev) => ({
|
|
6861
|
+
...prev,
|
|
6862
|
+
modelPlaced: false,
|
|
6863
|
+
markerDetected: false,
|
|
6864
|
+
selectedModel: "sphere",
|
|
6865
|
+
selectedMotion: "idle",
|
|
6866
|
+
selectedAudio: "none"
|
|
6867
|
+
}));
|
|
6800
6868
|
},
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
)
|
|
6869
|
+
className: "w-full px-3 py-2 bg-red-600 hover:bg-red-700 rounded transition-colors text-sm"
|
|
6870
|
+
},
|
|
6871
|
+
"\u{1F5D1}\uFE0F \u6E05\u9664\u6240\u6709"
|
|
6872
|
+
))))), /* @__PURE__ */ React10.createElement("div", { className: "absolute bottom-4 left-4 right-4 flex justify-center space-x-4" }, /* @__PURE__ */ React10.createElement(
|
|
6873
|
+
"button",
|
|
6874
|
+
{
|
|
6875
|
+
onClick: () => setState((prev) => ({ ...prev, showSettings: !prev.showSettings })),
|
|
6876
|
+
className: "px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors"
|
|
6877
|
+
},
|
|
6878
|
+
"\u2699\uFE0F \u8BBE\u7F6E"
|
|
6879
|
+
), !state.modelPlaced && /* @__PURE__ */ React10.createElement(
|
|
6880
|
+
"button",
|
|
6881
|
+
{
|
|
6882
|
+
onClick: placeModel,
|
|
6883
|
+
disabled: !state.markerDetected,
|
|
6884
|
+
className: `px-6 py-2 rounded-lg transition-colors ${state.markerDetected ? "bg-green-600 hover:bg-green-700 text-white" : "bg-gray-500 text-gray-300 cursor-not-allowed"}`
|
|
6885
|
+
},
|
|
6886
|
+
"\u{1F4CD} \u653E\u7F6E\u6A21\u578B"
|
|
6887
|
+
), state.modelPlaced && /* @__PURE__ */ React10.createElement(
|
|
6888
|
+
"button",
|
|
6889
|
+
{
|
|
6890
|
+
onClick: takePhoto,
|
|
6891
|
+
className: "px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
|
6892
|
+
},
|
|
6893
|
+
"\u{1F4F8} \u62CD\u7167"
|
|
6894
|
+
)), /* @__PURE__ */ React10.createElement("div", { className: "absolute top-4 right-4 flex space-x-2" }, /* @__PURE__ */ React10.createElement("div", { className: `w-3 h-3 rounded-full ${state.cameraReady ? "bg-green-400" : "bg-red-400"}`, title: "\u6444\u50CF\u5934" }), /* @__PURE__ */ React10.createElement("div", { className: `w-3 h-3 rounded-full ${state.arReady ? "bg-green-400" : "bg-red-400"}`, title: "AR" }), /* @__PURE__ */ React10.createElement("div", { className: `w-3 h-3 rounded-full ${window.DeviceOrientationEvent ? "bg-purple-400" : "bg-gray-400"}`, title: "\u9640\u87BA\u4EEA" }), /* @__PURE__ */ React10.createElement("div", { className: `w-3 h-3 rounded-full ${state.markerDetected ? "bg-blue-400" : "bg-gray-400"}`, title: "\u6807\u8BB0\u68C0\u6D4B" }), /* @__PURE__ */ React10.createElement("div", { className: `w-3 h-3 rounded-full ${state.modelPlaced && modelRootRef.current?.visible ? "bg-green-400" : "bg-yellow-400"}`, title: "\u6A21\u578B" }))));
|
|
6805
6895
|
});
|
|
6806
6896
|
MMDARPlayer.displayName = "MMDARPlayer";
|
|
6807
6897
|
|
|
6898
|
+
// src/mmd/ar/types.ts
|
|
6899
|
+
var ARMode = /* @__PURE__ */ ((ARMode2) => {
|
|
6900
|
+
ARMode2["Overlay"] = "overlay";
|
|
6901
|
+
ARMode2["WorldFixed"] = "world-fixed";
|
|
6902
|
+
return ARMode2;
|
|
6903
|
+
})(ARMode || {});
|
|
6904
|
+
|
|
6808
6905
|
// src/mmd/fx/HLSLToGLSLConverter.ts
|
|
6809
6906
|
var HLSLToGLSLConverter = class {
|
|
6810
6907
|
constructor() {
|