@liveroom-tech/react-immersive 1.1.7 → 1.2.0
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/index.js +301 -185
- package/dist/index.mjs +324 -204
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ var import_react_dom = require("react-dom");
|
|
|
55
55
|
var import_fiber11 = require("@react-three/fiber");
|
|
56
56
|
var import_xr4 = require("@react-three/xr");
|
|
57
57
|
var import_qrcode = require("qrcode.react");
|
|
58
|
-
var
|
|
58
|
+
var import_three19 = require("three");
|
|
59
59
|
|
|
60
60
|
// #style-inject:#style-inject
|
|
61
61
|
function styleInject(css, { insertAt } = {}) {
|
|
@@ -1536,6 +1536,7 @@ function Viewer({
|
|
|
1536
1536
|
dpr: [1, effectiveMaxDpr],
|
|
1537
1537
|
shadows: effectiveShadows ? "soft" : false,
|
|
1538
1538
|
resize: { debounce: 0, scroll: false },
|
|
1539
|
+
raycaster: { firstHitOnly: true },
|
|
1539
1540
|
style: backgroundStyle,
|
|
1540
1541
|
camera: camera ?? {
|
|
1541
1542
|
fov: 50
|
|
@@ -1593,6 +1594,24 @@ function rotatePositionY(pos, deg) {
|
|
|
1593
1594
|
const s = Math.sin(rad);
|
|
1594
1595
|
return [pos[0] * c + pos[2] * s, pos[1], -pos[0] * s + pos[2] * c];
|
|
1595
1596
|
}
|
|
1597
|
+
function fitShadowCamera(bounds, position) {
|
|
1598
|
+
const [cx, cy, cz] = bounds.center;
|
|
1599
|
+
const radius = bounds.radius;
|
|
1600
|
+
const length = Math.hypot(position[0], position[1], position[2]);
|
|
1601
|
+
const dir = length > 1e-6 ? [position[0] / length, position[1] / length, position[2] / length] : [0, 1, 0];
|
|
1602
|
+
const distance = radius * 2;
|
|
1603
|
+
return {
|
|
1604
|
+
position: [
|
|
1605
|
+
cx + dir[0] * distance,
|
|
1606
|
+
cy + dir[1] * distance,
|
|
1607
|
+
cz + dir[2] * distance
|
|
1608
|
+
],
|
|
1609
|
+
target: bounds.center,
|
|
1610
|
+
extent: radius,
|
|
1611
|
+
near: Math.max(radius / 100, 1e-3),
|
|
1612
|
+
far: distance + radius * 2
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1596
1615
|
var DEFAULT_LIGHTING = {
|
|
1597
1616
|
ambient: { intensity: 0.25, color: "#ffffff" },
|
|
1598
1617
|
lights: [
|
|
@@ -1642,7 +1661,8 @@ function LightInstance({
|
|
|
1642
1661
|
light,
|
|
1643
1662
|
shadows,
|
|
1644
1663
|
shadowsEnabled,
|
|
1645
|
-
environmentRotation
|
|
1664
|
+
environmentRotation,
|
|
1665
|
+
modelBounds
|
|
1646
1666
|
}) {
|
|
1647
1667
|
const dirRef = (0, import_react9.useRef)(null);
|
|
1648
1668
|
const pointRef = (0, import_react9.useRef)(null);
|
|
@@ -1658,8 +1678,19 @@ function LightInstance({
|
|
|
1658
1678
|
if (pointRef.current) pointRef.current.position.copy(camera.position);
|
|
1659
1679
|
if (spotRef.current) spotRef.current.position.copy(camera.position);
|
|
1660
1680
|
});
|
|
1661
|
-
if (!light.visible || light.type === "none") return null;
|
|
1662
1681
|
const castShadow = shadowsEnabled && shadows.enabled && light.castShadow;
|
|
1682
|
+
const fit = (0, import_react9.useMemo)(
|
|
1683
|
+
() => light.type === "directional" && castShadow && modelBounds && !light.attachedToCamera ? fitShadowCamera(modelBounds, position) : null,
|
|
1684
|
+
[light.type, light.attachedToCamera, castShadow, modelBounds, position]
|
|
1685
|
+
);
|
|
1686
|
+
(0, import_react9.useEffect)(() => {
|
|
1687
|
+
const dirLight = dirRef.current;
|
|
1688
|
+
if (!dirLight) return;
|
|
1689
|
+
const [tx, ty, tz] = fit?.target ?? [0, 0, 0];
|
|
1690
|
+
dirLight.target.position.set(tx, ty, tz);
|
|
1691
|
+
dirLight.target.updateMatrixWorld();
|
|
1692
|
+
}, [fit]);
|
|
1693
|
+
if (!light.visible || light.type === "none") return null;
|
|
1663
1694
|
if (light.type === "directional") {
|
|
1664
1695
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1665
1696
|
"directionalLight",
|
|
@@ -1667,19 +1698,19 @@ function LightInstance({
|
|
|
1667
1698
|
ref: dirRef,
|
|
1668
1699
|
color: light.color,
|
|
1669
1700
|
intensity: light.intensity,
|
|
1670
|
-
position,
|
|
1701
|
+
position: fit?.position ?? position,
|
|
1671
1702
|
castShadow,
|
|
1672
1703
|
"shadow-mapSize-width": shadows.mapSize,
|
|
1673
1704
|
"shadow-mapSize-height": shadows.mapSize,
|
|
1674
1705
|
"shadow-bias": light.shadowBias,
|
|
1675
1706
|
"shadow-normalBias": shadows.normalBias,
|
|
1676
1707
|
"shadow-radius": shadows.radius,
|
|
1677
|
-
"shadow-camera-near": 0.5,
|
|
1678
|
-
"shadow-camera-far": 40,
|
|
1679
|
-
"shadow-camera-left": -8,
|
|
1680
|
-
"shadow-camera-right": 8,
|
|
1681
|
-
"shadow-camera-top": 8,
|
|
1682
|
-
"shadow-camera-bottom": -8
|
|
1708
|
+
"shadow-camera-near": fit?.near ?? 0.5,
|
|
1709
|
+
"shadow-camera-far": fit?.far ?? 40,
|
|
1710
|
+
"shadow-camera-left": -(fit?.extent ?? 8),
|
|
1711
|
+
"shadow-camera-right": fit?.extent ?? 8,
|
|
1712
|
+
"shadow-camera-top": fit?.extent ?? 8,
|
|
1713
|
+
"shadow-camera-bottom": -(fit?.extent ?? 8)
|
|
1683
1714
|
}
|
|
1684
1715
|
);
|
|
1685
1716
|
}
|
|
@@ -1715,7 +1746,8 @@ function Lights({
|
|
|
1715
1746
|
shadowConfig,
|
|
1716
1747
|
shadowsEnabled = true,
|
|
1717
1748
|
environmentRotation = 0,
|
|
1718
|
-
environmentConfig
|
|
1749
|
+
environmentConfig,
|
|
1750
|
+
modelBounds
|
|
1719
1751
|
}) {
|
|
1720
1752
|
const lighting = lightingConfig ?? DEFAULT_LIGHTING;
|
|
1721
1753
|
const shadows = shadowConfig ?? DEFAULT_SHADOWS;
|
|
@@ -1735,28 +1767,29 @@ function Lights({
|
|
|
1735
1767
|
light,
|
|
1736
1768
|
shadows,
|
|
1737
1769
|
shadowsEnabled,
|
|
1738
|
-
environmentRotation
|
|
1770
|
+
environmentRotation,
|
|
1771
|
+
modelBounds
|
|
1739
1772
|
},
|
|
1740
1773
|
light.id
|
|
1741
1774
|
)),
|
|
1742
1775
|
sl?.enabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1743
|
-
|
|
1776
|
+
LightInstance,
|
|
1744
1777
|
{
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1778
|
+
light: {
|
|
1779
|
+
id: "light-env-shadow",
|
|
1780
|
+
type: "directional",
|
|
1781
|
+
color: "#ffffff",
|
|
1782
|
+
intensity: sl.intensity,
|
|
1783
|
+
position: envShadowPosition,
|
|
1784
|
+
attachedToCamera: false,
|
|
1785
|
+
castShadow: true,
|
|
1786
|
+
shadowBias: sl.bias,
|
|
1787
|
+
visible: true
|
|
1788
|
+
},
|
|
1789
|
+
shadows,
|
|
1790
|
+
shadowsEnabled,
|
|
1791
|
+
environmentRotation: 0,
|
|
1792
|
+
modelBounds
|
|
1760
1793
|
}
|
|
1761
1794
|
)
|
|
1762
1795
|
] });
|
|
@@ -1854,7 +1887,7 @@ function useConfiguredGLTF(url, config = {}) {
|
|
|
1854
1887
|
}
|
|
1855
1888
|
|
|
1856
1889
|
// src/components/Model.tsx
|
|
1857
|
-
var
|
|
1890
|
+
var import_three14 = require("three");
|
|
1858
1891
|
var import_three_stdlib3 = require("three-stdlib");
|
|
1859
1892
|
|
|
1860
1893
|
// src/utils/animationClips.ts
|
|
@@ -2451,6 +2484,68 @@ function refreshSkinnedBounds(root) {
|
|
|
2451
2484
|
});
|
|
2452
2485
|
}
|
|
2453
2486
|
|
|
2487
|
+
// src/utils/bvh.ts
|
|
2488
|
+
var import_three13 = require("three");
|
|
2489
|
+
var import_three_mesh_bvh = require("three-mesh-bvh");
|
|
2490
|
+
var raycastPatched = false;
|
|
2491
|
+
function enableAcceleratedRaycast() {
|
|
2492
|
+
if (raycastPatched) return;
|
|
2493
|
+
import_three13.Mesh.prototype.raycast = import_three_mesh_bvh.acceleratedRaycast;
|
|
2494
|
+
raycastPatched = true;
|
|
2495
|
+
}
|
|
2496
|
+
function collectGeometries(root) {
|
|
2497
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2498
|
+
root.traverse((node) => {
|
|
2499
|
+
const geometry = node.geometry;
|
|
2500
|
+
if (!geometry || !geometry.attributes?.position) return;
|
|
2501
|
+
if (geometry.boundsTree) return;
|
|
2502
|
+
seen.add(geometry);
|
|
2503
|
+
});
|
|
2504
|
+
return [...seen];
|
|
2505
|
+
}
|
|
2506
|
+
async function buildSceneBVH(root, signal) {
|
|
2507
|
+
const geometries = collectGeometries(root);
|
|
2508
|
+
if (geometries.length === 0) return;
|
|
2509
|
+
enableAcceleratedRaycast();
|
|
2510
|
+
let worker = null;
|
|
2511
|
+
if (typeof window !== "undefined" && typeof Worker !== "undefined") {
|
|
2512
|
+
try {
|
|
2513
|
+
const mod = await import("three-mesh-bvh/src/workers/ParallelMeshBVHWorker.js");
|
|
2514
|
+
worker = new mod.ParallelMeshBVHWorker();
|
|
2515
|
+
} catch {
|
|
2516
|
+
worker = null;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
try {
|
|
2520
|
+
for (const geometry of geometries) {
|
|
2521
|
+
if (signal?.aborted) return;
|
|
2522
|
+
let tree;
|
|
2523
|
+
if (worker) {
|
|
2524
|
+
try {
|
|
2525
|
+
tree = await worker.generate(geometry);
|
|
2526
|
+
} catch {
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
if (tree === void 0) {
|
|
2530
|
+
tree = import_three_mesh_bvh.computeBoundsTree.call(geometry);
|
|
2531
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2532
|
+
}
|
|
2533
|
+
if (signal?.aborted) return;
|
|
2534
|
+
geometry.boundsTree = tree;
|
|
2535
|
+
}
|
|
2536
|
+
} finally {
|
|
2537
|
+
worker?.dispose?.();
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
function disposeSceneBVH(root) {
|
|
2541
|
+
root.traverse((node) => {
|
|
2542
|
+
const geometry = node.geometry;
|
|
2543
|
+
if (!geometry?.boundsTree) return;
|
|
2544
|
+
import_three_mesh_bvh.disposeBoundsTree.call(geometry);
|
|
2545
|
+
geometry.boundsTree = void 0;
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2454
2549
|
// src/components/Model.tsx
|
|
2455
2550
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2456
2551
|
function firstMaterial(node) {
|
|
@@ -2508,17 +2603,17 @@ function Model({
|
|
|
2508
2603
|
}, [scene]);
|
|
2509
2604
|
const shellNodeNames = (0, import_react11.useMemo)(() => {
|
|
2510
2605
|
const shell = /* @__PURE__ */ new Set();
|
|
2511
|
-
const sceneBox = new
|
|
2606
|
+
const sceneBox = new import_three14.Box3();
|
|
2512
2607
|
const meshNodes = [];
|
|
2513
2608
|
for (const [name, node] of Object.entries(nodeMap)) {
|
|
2514
2609
|
if (!isMeshNode(node)) continue;
|
|
2515
|
-
const box = new
|
|
2610
|
+
const box = new import_three14.Box3().setFromObject(node);
|
|
2516
2611
|
if (box.isEmpty()) continue;
|
|
2517
2612
|
meshNodes.push({ name, box });
|
|
2518
2613
|
sceneBox.union(box);
|
|
2519
2614
|
}
|
|
2520
|
-
const sceneSize = sceneBox.getSize(new
|
|
2521
|
-
const size = new
|
|
2615
|
+
const sceneSize = sceneBox.getSize(new import_three14.Vector3());
|
|
2616
|
+
const size = new import_three14.Vector3();
|
|
2522
2617
|
for (const { name, box } of meshNodes) {
|
|
2523
2618
|
box.getSize(size);
|
|
2524
2619
|
const isShell = size.x > sceneSize.x * 0.7 && size.z > sceneSize.z * 0.7 && size.y > sceneSize.y * 0.4;
|
|
@@ -2537,6 +2632,14 @@ function Model({
|
|
|
2537
2632
|
(0, import_react11.useEffect)(() => {
|
|
2538
2633
|
refreshSkinnedBounds(scene);
|
|
2539
2634
|
}, [scene]);
|
|
2635
|
+
(0, import_react11.useEffect)(() => {
|
|
2636
|
+
const controller = new AbortController();
|
|
2637
|
+
void buildSceneBVH(scene, controller.signal);
|
|
2638
|
+
return () => {
|
|
2639
|
+
controller.abort();
|
|
2640
|
+
disposeSceneBVH(scene);
|
|
2641
|
+
};
|
|
2642
|
+
}, [scene]);
|
|
2540
2643
|
const bindingKeyByNodeName = (0, import_react11.useMemo)(() => {
|
|
2541
2644
|
const map = {};
|
|
2542
2645
|
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
@@ -2584,7 +2687,7 @@ function Model({
|
|
|
2584
2687
|
const map = {};
|
|
2585
2688
|
if (shadingMode !== "unlit") return map;
|
|
2586
2689
|
for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
|
|
2587
|
-
const basic = new
|
|
2690
|
+
const basic = new import_three14.MeshBasicMaterial();
|
|
2588
2691
|
basic.color.copy(material.color);
|
|
2589
2692
|
basic.map = material.map;
|
|
2590
2693
|
basic.alphaMap = material.alphaMap;
|
|
@@ -2600,7 +2703,7 @@ function Model({
|
|
|
2600
2703
|
const map = {};
|
|
2601
2704
|
if (shadingMode !== "normals") return map;
|
|
2602
2705
|
for (const nodeName of Object.keys(perNodeMaterials)) {
|
|
2603
|
-
map[nodeName] = new
|
|
2706
|
+
map[nodeName] = new import_three14.MeshNormalMaterial();
|
|
2604
2707
|
}
|
|
2605
2708
|
return map;
|
|
2606
2709
|
}, [perNodeMaterials, shadingMode]);
|
|
@@ -2609,7 +2712,7 @@ function Model({
|
|
|
2609
2712
|
if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
|
|
2610
2713
|
const matcapTexture = getMatcapTexture();
|
|
2611
2714
|
for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
|
|
2612
|
-
const matcap = new
|
|
2715
|
+
const matcap = new import_three14.MeshMatcapMaterial();
|
|
2613
2716
|
matcap.matcap = matcapTexture;
|
|
2614
2717
|
matcap.color.copy(material.color);
|
|
2615
2718
|
matcap.alphaMap = material.alphaMap;
|
|
@@ -2776,16 +2879,16 @@ function Model({
|
|
|
2776
2879
|
const glbNodeName = binding.modelObjectId ?? nodeName;
|
|
2777
2880
|
const node = nodeMap[glbNodeName];
|
|
2778
2881
|
if (!isMeshNode(node)) continue;
|
|
2779
|
-
const geometry = new
|
|
2882
|
+
const geometry = new import_three14.WireframeGeometry(
|
|
2780
2883
|
node.geometry
|
|
2781
2884
|
);
|
|
2782
|
-
const lineMaterial = new
|
|
2885
|
+
const lineMaterial = new import_three14.LineBasicMaterial({
|
|
2783
2886
|
color: wireframeColor,
|
|
2784
2887
|
transparent: true,
|
|
2785
2888
|
opacity: wireframeOpacity,
|
|
2786
2889
|
depthTest: false
|
|
2787
2890
|
});
|
|
2788
|
-
const line = new
|
|
2891
|
+
const line = new import_three14.LineSegments(geometry, lineMaterial);
|
|
2789
2892
|
line.renderOrder = 1;
|
|
2790
2893
|
node.add(line);
|
|
2791
2894
|
added.push({ node, line });
|
|
@@ -2837,7 +2940,7 @@ function Model({
|
|
|
2837
2940
|
const applyClipConfigToAction = (action, clipConfig) => {
|
|
2838
2941
|
const loopMode = getClipLoopMode(clipConfig);
|
|
2839
2942
|
action.setLoop(
|
|
2840
|
-
loopMode === "once" ?
|
|
2943
|
+
loopMode === "once" ? import_three14.LoopOnce : import_three14.LoopRepeat,
|
|
2841
2944
|
loopMode === "once" ? 1 : Infinity
|
|
2842
2945
|
);
|
|
2843
2946
|
action.clampWhenFinished = loopMode === "once";
|
|
@@ -2904,7 +3007,7 @@ function Model({
|
|
|
2904
3007
|
invalidate();
|
|
2905
3008
|
};
|
|
2906
3009
|
(0, import_react11.useEffect)(() => {
|
|
2907
|
-
const mixer = new
|
|
3010
|
+
const mixer = new import_three14.AnimationMixer(scene);
|
|
2908
3011
|
mixerRef.current = mixer;
|
|
2909
3012
|
const actions = {};
|
|
2910
3013
|
for (const { clip, sourceName } of resolvedAnimationClips) {
|
|
@@ -2995,17 +3098,17 @@ function Model({
|
|
|
2995
3098
|
(0, import_fiber7.useFrame)((_, delta) => {
|
|
2996
3099
|
if (!groupRef.current) return;
|
|
2997
3100
|
const rotation = groupRef.current.rotation;
|
|
2998
|
-
rotation.x =
|
|
3101
|
+
rotation.x = import_three14.MathUtils.lerp(
|
|
2999
3102
|
rotation.x,
|
|
3000
3103
|
targetRotation[0],
|
|
3001
3104
|
1 - Math.exp(-5 * delta)
|
|
3002
3105
|
);
|
|
3003
|
-
rotation.y =
|
|
3106
|
+
rotation.y = import_three14.MathUtils.lerp(
|
|
3004
3107
|
rotation.y,
|
|
3005
3108
|
targetRotation[1],
|
|
3006
3109
|
1 - Math.exp(-5 * delta)
|
|
3007
3110
|
);
|
|
3008
|
-
rotation.z =
|
|
3111
|
+
rotation.z = import_three14.MathUtils.lerp(
|
|
3009
3112
|
rotation.z,
|
|
3010
3113
|
targetRotation[2],
|
|
3011
3114
|
1 - Math.exp(-5 * delta)
|
|
@@ -3016,16 +3119,16 @@ function Model({
|
|
|
3016
3119
|
const explodePartsRef = (0, import_react11.useRef)([]);
|
|
3017
3120
|
const explodeStrengthRef = (0, import_react11.useRef)(0);
|
|
3018
3121
|
const currentExplodeRef = (0, import_react11.useRef)(0);
|
|
3019
|
-
const explodeTmp = (0, import_react11.useMemo)(() => new
|
|
3122
|
+
const explodeTmp = (0, import_react11.useMemo)(() => new import_three14.Vector3(), []);
|
|
3020
3123
|
(0, import_react11.useEffect)(() => {
|
|
3021
3124
|
for (const part of explodePartsRef.current) {
|
|
3022
3125
|
part.node.position.copy(part.restLocalPos);
|
|
3023
3126
|
}
|
|
3024
3127
|
currentExplodeRef.current = 0;
|
|
3025
3128
|
scene.updateWorldMatrix(true, true);
|
|
3026
|
-
const sceneBox = new
|
|
3027
|
-
const modelCenter = sceneBox.getCenter(new
|
|
3028
|
-
const radius = sceneBox.getSize(new
|
|
3129
|
+
const sceneBox = new import_three14.Box3().setFromObject(scene);
|
|
3130
|
+
const modelCenter = sceneBox.getCenter(new import_three14.Vector3());
|
|
3131
|
+
const radius = sceneBox.getSize(new import_three14.Vector3()).length() * 0.5;
|
|
3029
3132
|
explodeStrengthRef.current = Number.isFinite(radius) ? radius : 0;
|
|
3030
3133
|
const targets = [];
|
|
3031
3134
|
const targetSet = /* @__PURE__ */ new Set();
|
|
@@ -3036,7 +3139,7 @@ function Model({
|
|
|
3036
3139
|
targets.push(node);
|
|
3037
3140
|
}
|
|
3038
3141
|
const parts = [];
|
|
3039
|
-
const partCenter = new
|
|
3142
|
+
const partCenter = new import_three14.Vector3();
|
|
3040
3143
|
for (const node of targets) {
|
|
3041
3144
|
if (!node.parent) continue;
|
|
3042
3145
|
let ancestor = node.parent;
|
|
@@ -3049,7 +3152,7 @@ function Model({
|
|
|
3049
3152
|
ancestor = ancestor.parent;
|
|
3050
3153
|
}
|
|
3051
3154
|
if (nested) continue;
|
|
3052
|
-
const box = new
|
|
3155
|
+
const box = new import_three14.Box3().setFromObject(node);
|
|
3053
3156
|
if (box.isEmpty()) continue;
|
|
3054
3157
|
box.getCenter(partCenter);
|
|
3055
3158
|
const worldDir = partCenter.clone().sub(modelCenter);
|
|
@@ -3058,9 +3161,9 @@ function Model({
|
|
|
3058
3161
|
parts.push({
|
|
3059
3162
|
node,
|
|
3060
3163
|
restLocalPos: node.position.clone(),
|
|
3061
|
-
restWorldPos: node.getWorldPosition(new
|
|
3164
|
+
restWorldPos: node.getWorldPosition(new import_three14.Vector3()),
|
|
3062
3165
|
worldDir,
|
|
3063
|
-
parentInverse: new
|
|
3166
|
+
parentInverse: new import_three14.Matrix4().copy(node.parent.matrixWorld).invert()
|
|
3064
3167
|
});
|
|
3065
3168
|
}
|
|
3066
3169
|
explodePartsRef.current = parts;
|
|
@@ -3078,7 +3181,7 @@ function Model({
|
|
|
3078
3181
|
if (current === target) return;
|
|
3079
3182
|
currentExplodeRef.current = target;
|
|
3080
3183
|
} else {
|
|
3081
|
-
currentExplodeRef.current =
|
|
3184
|
+
currentExplodeRef.current = import_three14.MathUtils.lerp(
|
|
3082
3185
|
current,
|
|
3083
3186
|
target,
|
|
3084
3187
|
1 - Math.exp(-6 * delta)
|
|
@@ -3143,7 +3246,7 @@ function Model({
|
|
|
3143
3246
|
var import_react12 = require("react");
|
|
3144
3247
|
var import_drei4 = require("@react-three/drei");
|
|
3145
3248
|
var import_fiber8 = require("@react-three/fiber");
|
|
3146
|
-
var
|
|
3249
|
+
var import_three15 = require("three");
|
|
3147
3250
|
|
|
3148
3251
|
// src/styles/AnnotationsLayer.css
|
|
3149
3252
|
styleInject(".ri-annotationMarker--hovered {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n");
|
|
@@ -3156,7 +3259,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3156
3259
|
const scene = (0, import_fiber8.useThree)((state) => state.scene);
|
|
3157
3260
|
const anchorNodeRef = (0, import_react12.useRef)(null);
|
|
3158
3261
|
const baseLocalPosition = (0, import_react12.useMemo)(
|
|
3159
|
-
() => new
|
|
3262
|
+
() => new import_three15.Vector3(
|
|
3160
3263
|
...annotation.anchorLocalPosition ?? annotation.localPosition ?? annotation.worldPosition
|
|
3161
3264
|
),
|
|
3162
3265
|
[
|
|
@@ -3165,9 +3268,9 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3165
3268
|
annotation.worldPosition
|
|
3166
3269
|
]
|
|
3167
3270
|
);
|
|
3168
|
-
const worldPosition = (0, import_react12.useMemo)(() => new
|
|
3169
|
-
const biasedWorldPosition = (0, import_react12.useMemo)(() => new
|
|
3170
|
-
const toCamera = (0, import_react12.useMemo)(() => new
|
|
3271
|
+
const worldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3272
|
+
const biasedWorldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3273
|
+
const toCamera = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3171
3274
|
(0, import_react12.useEffect)(() => {
|
|
3172
3275
|
anchorNodeRef.current = null;
|
|
3173
3276
|
}, [annotation.anchorNodeName]);
|
|
@@ -3190,7 +3293,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3190
3293
|
if (worldPositionsRef) {
|
|
3191
3294
|
let stored = worldPositionsRef.current.get(annotation.id);
|
|
3192
3295
|
if (!stored) {
|
|
3193
|
-
stored = new
|
|
3296
|
+
stored = new import_three15.Vector3();
|
|
3194
3297
|
worldPositionsRef.current.set(annotation.id, stored);
|
|
3195
3298
|
}
|
|
3196
3299
|
stored.copy(worldPosition);
|
|
@@ -3488,17 +3591,17 @@ function AnnotationsLayer({
|
|
|
3488
3591
|
);
|
|
3489
3592
|
(0, import_fiber8.useFrame)((_, delta) => {
|
|
3490
3593
|
if (!groupRef.current) return;
|
|
3491
|
-
groupRef.current.rotation.x =
|
|
3594
|
+
groupRef.current.rotation.x = import_three15.MathUtils.lerp(
|
|
3492
3595
|
groupRef.current.rotation.x,
|
|
3493
3596
|
targetRotation[0],
|
|
3494
3597
|
1 - Math.exp(-5 * delta)
|
|
3495
3598
|
);
|
|
3496
|
-
groupRef.current.rotation.y =
|
|
3599
|
+
groupRef.current.rotation.y = import_three15.MathUtils.lerp(
|
|
3497
3600
|
groupRef.current.rotation.y,
|
|
3498
3601
|
targetRotation[1],
|
|
3499
3602
|
1 - Math.exp(-5 * delta)
|
|
3500
3603
|
);
|
|
3501
|
-
groupRef.current.rotation.z =
|
|
3604
|
+
groupRef.current.rotation.z = import_three15.MathUtils.lerp(
|
|
3502
3605
|
groupRef.current.rotation.z,
|
|
3503
3606
|
targetRotation[2],
|
|
3504
3607
|
1 - Math.exp(-5 * delta)
|
|
@@ -3536,7 +3639,7 @@ function AnnotationsLayer({
|
|
|
3536
3639
|
// src/components/ModelViewer/MeasurementLayer.tsx
|
|
3537
3640
|
var import_react13 = require("react");
|
|
3538
3641
|
var import_drei5 = require("@react-three/drei");
|
|
3539
|
-
var
|
|
3642
|
+
var import_three16 = require("three");
|
|
3540
3643
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3541
3644
|
var LINE_COLOR = "#22d3ee";
|
|
3542
3645
|
var BOX_COLOR = "#facc15";
|
|
@@ -3564,8 +3667,8 @@ function DimensionsBox({ box, unit }) {
|
|
|
3564
3667
|
);
|
|
3565
3668
|
const edges = (0, import_react13.useMemo)(() => {
|
|
3566
3669
|
const [w2, h2, d2] = box.size;
|
|
3567
|
-
const geometry = new
|
|
3568
|
-
new
|
|
3670
|
+
const geometry = new import_three16.EdgesGeometry(
|
|
3671
|
+
new import_three16.BoxGeometry(w2 || 1e-4, h2 || 1e-4, d2 || 1e-4)
|
|
3569
3672
|
);
|
|
3570
3673
|
return geometry;
|
|
3571
3674
|
}, [box.size]);
|
|
@@ -3611,7 +3714,7 @@ function MeasurementLayer({
|
|
|
3611
3714
|
points.slice(1).map((point, index) => {
|
|
3612
3715
|
const a = points[index].position;
|
|
3613
3716
|
const b = point.position;
|
|
3614
|
-
const distance = new
|
|
3717
|
+
const distance = new import_three16.Vector3(...a).distanceTo(new import_three16.Vector3(...b));
|
|
3615
3718
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("group", { children: [
|
|
3616
3719
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_drei5.Line, { points: [a, b], color: LINE_COLOR, lineWidth: 2 }),
|
|
3617
3720
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MeasureLabel, { position: midpoint(a, b), children: formatMeasurement(distance, unit) })
|
|
@@ -3625,7 +3728,7 @@ function MeasurementLayer({
|
|
|
3625
3728
|
var import_react14 = require("react");
|
|
3626
3729
|
var import_xr3 = require("@react-three/xr");
|
|
3627
3730
|
var import_fiber9 = require("@react-three/fiber");
|
|
3628
|
-
var
|
|
3731
|
+
var import_three17 = require("three");
|
|
3629
3732
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
3630
3733
|
var TARGET_SIZE = 1;
|
|
3631
3734
|
var FORWARD_DISTANCE = 1;
|
|
@@ -3662,13 +3765,13 @@ var XRModelPresenter = (0, import_react14.forwardRef)(
|
|
|
3662
3765
|
() => box ? computeNormalization(box, scaleMode) : null,
|
|
3663
3766
|
[box, scaleMode]
|
|
3664
3767
|
);
|
|
3665
|
-
const placedPosition = (0, import_react14.useRef)(new
|
|
3768
|
+
const placedPosition = (0, import_react14.useRef)(new import_three17.Vector3(0, 0, -FORWARD_DISTANCE));
|
|
3666
3769
|
const userScale = (0, import_react14.useRef)(1);
|
|
3667
3770
|
const rotationY = (0, import_react14.useRef)(0);
|
|
3668
3771
|
const placementMode = (0, import_react14.useRef)(true);
|
|
3669
3772
|
const modelPlaced = (0, import_react14.useRef)(false);
|
|
3670
|
-
const hitMatrix = (0, import_react14.useMemo)(() => new
|
|
3671
|
-
const hitPosition = (0, import_react14.useRef)(new
|
|
3773
|
+
const hitMatrix = (0, import_react14.useMemo)(() => new import_three17.Matrix4(), []);
|
|
3774
|
+
const hitPosition = (0, import_react14.useRef)(new import_three17.Vector3());
|
|
3672
3775
|
const hasHit = (0, import_react14.useRef)(false);
|
|
3673
3776
|
const lastARState = (0, import_react14.useRef)(null);
|
|
3674
3777
|
const gesturing = (0, import_react14.useRef)(false);
|
|
@@ -3861,7 +3964,7 @@ var XRModelPresenter_default = XRModelPresenter;
|
|
|
3861
3964
|
// src/components/ModelViewer/CinematicCamera.tsx
|
|
3862
3965
|
var import_react15 = require("react");
|
|
3863
3966
|
var import_fiber10 = require("@react-three/fiber");
|
|
3864
|
-
var
|
|
3967
|
+
var import_three18 = require("three");
|
|
3865
3968
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
3866
3969
|
function CinematicCamera({
|
|
3867
3970
|
controlsRef,
|
|
@@ -3883,14 +3986,14 @@ function CinematicCamera({
|
|
|
3883
3986
|
const curves = (0, import_react15.useMemo)(() => {
|
|
3884
3987
|
if (!hasPath) return null;
|
|
3885
3988
|
return {
|
|
3886
|
-
position: new
|
|
3887
|
-
waypoints.map((w) => new
|
|
3989
|
+
position: new import_three18.CatmullRomCurve3(
|
|
3990
|
+
waypoints.map((w) => new import_three18.Vector3(...w.position)),
|
|
3888
3991
|
loop,
|
|
3889
3992
|
"catmullrom",
|
|
3890
3993
|
0.5
|
|
3891
3994
|
),
|
|
3892
|
-
target: new
|
|
3893
|
-
waypoints.map((w) => new
|
|
3995
|
+
target: new import_three18.CatmullRomCurve3(
|
|
3996
|
+
waypoints.map((w) => new import_three18.Vector3(...w.target)),
|
|
3894
3997
|
loop,
|
|
3895
3998
|
"catmullrom",
|
|
3896
3999
|
0.5
|
|
@@ -3899,12 +4002,12 @@ function CinematicCamera({
|
|
|
3899
4002
|
}, [signature, hasPath, loop]);
|
|
3900
4003
|
const elapsedRef = (0, import_react15.useRef)(0);
|
|
3901
4004
|
const enterElapsedRef = (0, import_react15.useRef)(0);
|
|
3902
|
-
const enterPos = (0, import_react15.useRef)(new
|
|
3903
|
-
const enterTarget = (0, import_react15.useRef)(new
|
|
4005
|
+
const enterPos = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
4006
|
+
const enterTarget = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
3904
4007
|
const startedRef = (0, import_react15.useRef)(false);
|
|
3905
4008
|
const orbitRef = (0, import_react15.useRef)(null);
|
|
3906
|
-
const tmpPos = (0, import_react15.useMemo)(() => new
|
|
3907
|
-
const tmpTarget = (0, import_react15.useMemo)(() => new
|
|
4009
|
+
const tmpPos = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
4010
|
+
const tmpTarget = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
3908
4011
|
(0, import_react15.useEffect)(() => {
|
|
3909
4012
|
if (!playing) {
|
|
3910
4013
|
startedRef.current = false;
|
|
@@ -3920,7 +4023,7 @@ function CinematicCamera({
|
|
|
3920
4023
|
if (!hasPath) {
|
|
3921
4024
|
const scene = sceneRef.current;
|
|
3922
4025
|
if (scene) {
|
|
3923
|
-
const sphere = new
|
|
4026
|
+
const sphere = new import_three18.Box3().setFromObject(scene).getBoundingSphere(new import_three18.Sphere());
|
|
3924
4027
|
orbitRef.current = {
|
|
3925
4028
|
center: sphere.center.clone(),
|
|
3926
4029
|
radius: sphere.radius > 0 ? sphere.radius : 1
|
|
@@ -4217,7 +4320,7 @@ var useLicense = (licenseKey) => {
|
|
|
4217
4320
|
var import_react17 = require("react");
|
|
4218
4321
|
|
|
4219
4322
|
// src/styles/MouseController.css
|
|
4220
|
-
styleInject(".mc__wrapper {\n position:
|
|
4323
|
+
styleInject(".mc__wrapper {\n position: absolute;\n bottom: -10px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n z-index: 100;\n}\n.mc__dpad {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n margin-bottom: 20px;\n}\n.mc__middle-row {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n.mc__joystick {\n width: 60px;\n height: 60px;\n flex-shrink: 0;\n background:\n radial-gradient(\n circle at 40% 35%,\n rgba(255, 255, 255, 0.07),\n rgba(0, 0, 0, 0.55));\n border-radius: 50%;\n border: 1.5px solid rgba(255, 255, 255, 0.15);\n position: relative;\n cursor: grab;\n touch-action: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n margin: 4px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 0 4px rgba(255, 255, 255, 0.03);\n}\n.mc__joystick:hover {\n border-color: rgba(255, 255, 255, 0.28);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.55), 0 0 0 4px rgba(66, 133, 244, 0.08);\n}\n.mc__joystick:active {\n cursor: grabbing;\n border-color: rgba(66, 133, 244, 0.6);\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5), 0 0 4px rgba(66, 133, 244, 0.15);\n}\n.mc__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 34px;\n height: 34px;\n margin-left: -17px;\n margin-top: -17px;\n background:\n linear-gradient(\n 145deg,\n #5a9bf5,\n #1a66d9);\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 3px 10px rgba(26, 102, 217, 0.5), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n transition: transform 0.04s linear, box-shadow 0.1s ease;\n border: 1.5px solid rgba(255, 255, 255, 0.45);\n}\n.mc__joystick:active .mc__knob {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.7), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n animation: pulse 0.6s ease infinite;\n}\n.mc__arrow {\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n box-shadow: none;\n outline: none;\n -webkit-appearance: none;\n appearance: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n transition: transform 0.1s ease, filter 0.15s ease;\n overflow: visible;\n}\n.mc__arrow svg {\n display: block !important;\n pointer-events: none !important;\n user-select: none !important;\n -webkit-user-select: none !important;\n overflow: visible !important;\n}\n.mc__arrow--up {\n margin-bottom: 2px;\n}\n.mc__arrow--up:hover {\n transform: scale(1.15) translateY(-2px);\n filter: drop-shadow(0 4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--up:active {\n transform: scale(0.92) translateY(2px);\n}\n.mc__arrow--down {\n margin-top: 2px;\n}\n.mc__arrow--down:hover {\n transform: scale(1.15) translateY(2px);\n filter: drop-shadow(0 -4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--down:active {\n transform: scale(0.92) translateY(-2px);\n}\n.mc__arrow--left {\n margin-right: 2px;\n}\n.mc__arrow--left:hover {\n transform: scale(1.15) translateX(-2px);\n filter: drop-shadow(4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--left:active {\n transform: scale(0.92) translateX(2px);\n}\n.mc__arrow--right {\n margin-left: 2px;\n}\n.mc__arrow--right:hover {\n transform: scale(1.15) translateX(2px);\n filter: drop-shadow(-4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--right:active {\n transform: scale(0.92) translateX(-2px);\n}\n@keyframes pulse {\n 0%, 100% {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.6), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 0.85;\n }\n 50% {\n box-shadow: 0 4px 24px rgba(66, 133, 244, 0.95), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 1;\n }\n}\n@media (max-width: 768px) {\n .mc__wrapper {\n bottom: -5px;\n }\n .mc__joystick {\n width: 68px;\n height: 68px;\n }\n .mc__knob {\n width: 30px;\n height: 30px;\n margin-left: -15px;\n margin-top: -15px;\n }\n .mc__arrow--up:hover {\n transform: scale(1.1) translateY(-1px);\n }\n .mc__arrow--down:hover {\n transform: scale(1.1) translateY(1px);\n }\n .mc__arrow--left:hover {\n transform: scale(1.1) translateX(-1px);\n }\n .mc__arrow--right:hover {\n transform: scale(1.1) translateX(1px);\n }\n}\n@media (max-width: 480px) {\n .mc__joystick {\n width: 56px;\n height: 56px;\n }\n .mc__knob {\n width: 26px;\n height: 26px;\n margin-left: -13px;\n margin-top: -13px;\n }\n .mc__arrow--up {\n margin-bottom: 1px;\n }\n .mc__arrow--down {\n margin-top: 1px;\n }\n .mc__arrow--left {\n margin-right: 1px;\n }\n .mc__arrow--right {\n margin-left: 1px;\n }\n}\n@media (hover: none) {\n .mc__arrow:hover {\n transform: none !important;\n filter: none !important;\n }\n .mc__arrow:active {\n transform: scale(0.92) !important;\n }\n}\n");
|
|
4221
4324
|
|
|
4222
4325
|
// src/components/MouseController.tsx
|
|
4223
4326
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
@@ -5049,7 +5152,7 @@ function findInfluencingBone(hit) {
|
|
|
5049
5152
|
if (!skinIndex || !skinWeight || !position) return null;
|
|
5050
5153
|
const localHit = mesh.worldToLocal(hit.point.clone());
|
|
5051
5154
|
const vertices = [hit.face.a, hit.face.b, hit.face.c];
|
|
5052
|
-
const vertex = new
|
|
5155
|
+
const vertex = new import_three19.Vector3();
|
|
5053
5156
|
let nearest = vertices[0];
|
|
5054
5157
|
let nearestDist = Infinity;
|
|
5055
5158
|
for (const vi of vertices) {
|
|
@@ -5099,9 +5202,9 @@ function GuidedTourCameraFollow({
|
|
|
5099
5202
|
const scene = (0, import_fiber11.useThree)((state) => state.scene);
|
|
5100
5203
|
const nodeRef = (0, import_react22.useRef)(null);
|
|
5101
5204
|
const hasPrev = (0, import_react22.useRef)(false);
|
|
5102
|
-
const live = (0, import_react22.useMemo)(() => new
|
|
5103
|
-
const prev = (0, import_react22.useMemo)(() => new
|
|
5104
|
-
const goal = (0, import_react22.useMemo)(() => new
|
|
5205
|
+
const live = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5206
|
+
const prev = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5207
|
+
const goal = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5105
5208
|
const nodeName = annotation?.anchorNodeName;
|
|
5106
5209
|
const localPosition = annotation?.anchorLocalPosition;
|
|
5107
5210
|
(0, import_react22.useEffect)(() => {
|
|
@@ -5140,8 +5243,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
|
|
|
5140
5243
|
isModelPlaced: false
|
|
5141
5244
|
};
|
|
5142
5245
|
function computeMeasureBox(scene) {
|
|
5143
|
-
const box = new
|
|
5144
|
-
const size = new
|
|
5246
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5247
|
+
const size = new import_three19.Vector3();
|
|
5145
5248
|
box.getSize(size);
|
|
5146
5249
|
return {
|
|
5147
5250
|
min: [box.min.x, box.min.y, box.min.z],
|
|
@@ -5149,11 +5252,19 @@ function computeMeasureBox(scene) {
|
|
|
5149
5252
|
size: [size.x, size.y, size.z]
|
|
5150
5253
|
};
|
|
5151
5254
|
}
|
|
5255
|
+
function computeModelBounds(scene) {
|
|
5256
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5257
|
+
if (box.isEmpty()) return null;
|
|
5258
|
+
const center = box.getCenter(new import_three19.Vector3());
|
|
5259
|
+
const radius = box.getSize(new import_three19.Vector3()).length() / 2;
|
|
5260
|
+
if (!Number.isFinite(radius) || radius <= 0) return null;
|
|
5261
|
+
return { center: [center.x, center.y, center.z], radius };
|
|
5262
|
+
}
|
|
5152
5263
|
function fitClippingPlanesToScene(scene, controls) {
|
|
5153
5264
|
const camera = controls?.camera;
|
|
5154
|
-
if (!(camera instanceof
|
|
5155
|
-
const box = new
|
|
5156
|
-
const size = box.getSize(new
|
|
5265
|
+
if (!(camera instanceof import_three19.PerspectiveCamera)) return;
|
|
5266
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5267
|
+
const size = box.getSize(new import_three19.Vector3()).length();
|
|
5157
5268
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
5158
5269
|
camera.near = size / 100;
|
|
5159
5270
|
camera.far = size * 100;
|
|
@@ -5306,8 +5417,8 @@ function ModelViewer({
|
|
|
5306
5417
|
if (!controls || initialCameraStateRef.current) {
|
|
5307
5418
|
return;
|
|
5308
5419
|
}
|
|
5309
|
-
const position = controls.getPosition(new
|
|
5310
|
-
const target = controls.getTarget(new
|
|
5420
|
+
const position = controls.getPosition(new import_three19.Vector3());
|
|
5421
|
+
const target = controls.getTarget(new import_three19.Vector3());
|
|
5311
5422
|
initialCameraStateRef.current = {
|
|
5312
5423
|
position: [position.x, position.y, position.z],
|
|
5313
5424
|
target: [target.x, target.y, target.z]
|
|
@@ -5318,6 +5429,7 @@ function ModelViewer({
|
|
|
5318
5429
|
const [measurePoints, setMeasurePoints] = (0, import_react22.useState)([]);
|
|
5319
5430
|
const [showDimensions, setShowDimensions] = (0, import_react22.useState)(false);
|
|
5320
5431
|
const [measureBox, setMeasureBox] = (0, import_react22.useState)(null);
|
|
5432
|
+
const [modelBounds, setModelBounds] = (0, import_react22.useState)(null);
|
|
5321
5433
|
const measureModeRef = (0, import_react22.useRef)(false);
|
|
5322
5434
|
measureModeRef.current = measureMode;
|
|
5323
5435
|
const measurePointCounter = (0, import_react22.useRef)(0);
|
|
@@ -5561,7 +5673,7 @@ function ModelViewer({
|
|
|
5561
5673
|
const controls = controlsRef.current;
|
|
5562
5674
|
const scene = sceneRef.current;
|
|
5563
5675
|
if (!controls) return;
|
|
5564
|
-
const target = new
|
|
5676
|
+
const target = new import_three19.Vector3();
|
|
5565
5677
|
const livePosition = annotationWorldPositionsRef.current.get(
|
|
5566
5678
|
annotation.id
|
|
5567
5679
|
);
|
|
@@ -5579,15 +5691,15 @@ function ModelViewer({
|
|
|
5579
5691
|
controls.smoothTime = 0.6;
|
|
5580
5692
|
const storedNormal = annotation.worldNormal;
|
|
5581
5693
|
if (scene) {
|
|
5582
|
-
const box = new
|
|
5583
|
-
const modelCenter = new
|
|
5694
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5695
|
+
const modelCenter = new import_three19.Vector3();
|
|
5584
5696
|
box.getCenter(modelCenter);
|
|
5585
|
-
const modelSize = new
|
|
5697
|
+
const modelSize = new import_three19.Vector3();
|
|
5586
5698
|
box.getSize(modelSize);
|
|
5587
5699
|
const modelRadius = modelSize.length() * 0.2;
|
|
5588
5700
|
let outwardDir;
|
|
5589
5701
|
if (storedNormal) {
|
|
5590
|
-
outwardDir = new
|
|
5702
|
+
outwardDir = new import_three19.Vector3(
|
|
5591
5703
|
storedNormal[0],
|
|
5592
5704
|
storedNormal[1],
|
|
5593
5705
|
storedNormal[2]
|
|
@@ -5885,8 +5997,8 @@ function ModelViewer({
|
|
|
5885
5997
|
};
|
|
5886
5998
|
if (canvasContainer && cameraControls && object) {
|
|
5887
5999
|
const canvasRect = canvasContainer.getBoundingClientRect();
|
|
5888
|
-
const objectCenter = new
|
|
5889
|
-
new
|
|
6000
|
+
const objectCenter = new import_three19.Vector3();
|
|
6001
|
+
new import_three19.Box3().setFromObject(object).getCenter(objectCenter);
|
|
5890
6002
|
objectCenter.project(cameraControls);
|
|
5891
6003
|
const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
|
|
5892
6004
|
const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
|
|
@@ -6110,14 +6222,14 @@ function ModelViewer({
|
|
|
6110
6222
|
}
|
|
6111
6223
|
try {
|
|
6112
6224
|
if (flyIn) {
|
|
6113
|
-
const target = controls.getTarget(new
|
|
6114
|
-
const position = controls.getPosition(new
|
|
6225
|
+
const target = controls.getTarget(new import_three19.Vector3());
|
|
6226
|
+
const position = controls.getPosition(new import_three19.Vector3());
|
|
6115
6227
|
const direction = position.clone().sub(target);
|
|
6116
6228
|
if (direction.lengthSq() < 1e-6) {
|
|
6117
6229
|
direction.set(0, 0, 1);
|
|
6118
6230
|
}
|
|
6119
6231
|
direction.normalize();
|
|
6120
|
-
const sphere = new
|
|
6232
|
+
const sphere = new import_three19.Box3().setFromObject(scene).getBoundingSphere(new import_three19.Sphere());
|
|
6121
6233
|
const farDistance = Math.max(sphere.radius * 10, 20);
|
|
6122
6234
|
const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
|
|
6123
6235
|
await controls.setLookAt(
|
|
@@ -6346,6 +6458,7 @@ function ModelViewer({
|
|
|
6346
6458
|
(scene) => {
|
|
6347
6459
|
sceneRef.current = scene;
|
|
6348
6460
|
setMeasureBox(computeMeasureBox(scene));
|
|
6461
|
+
setModelBounds(computeModelBounds(scene));
|
|
6349
6462
|
setModelLoaded(true);
|
|
6350
6463
|
setModelLoadFailed(false);
|
|
6351
6464
|
setMismatchDismissed(false);
|
|
@@ -6408,8 +6521,8 @@ function ModelViewer({
|
|
|
6408
6521
|
const rect = canvas.getBoundingClientRect();
|
|
6409
6522
|
const ndcX = (event.clientX - rect.left) / rect.width * 2 - 1;
|
|
6410
6523
|
const ndcY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
6411
|
-
const raycaster = new
|
|
6412
|
-
raycaster.setFromCamera(new
|
|
6524
|
+
const raycaster = new import_three19.Raycaster();
|
|
6525
|
+
raycaster.setFromCamera(new import_three19.Vector2(ndcX, ndcY), cam);
|
|
6413
6526
|
const hits = raycaster.intersectObject(scene, true);
|
|
6414
6527
|
if (hits.length === 0) return;
|
|
6415
6528
|
const { point } = hits[0];
|
|
@@ -6505,8 +6618,8 @@ function ModelViewer({
|
|
|
6505
6618
|
const rect = canvas.getBoundingClientRect();
|
|
6506
6619
|
const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
6507
6620
|
const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
6508
|
-
const raycaster = new
|
|
6509
|
-
raycaster.setFromCamera(new
|
|
6621
|
+
const raycaster = new import_three19.Raycaster();
|
|
6622
|
+
raycaster.setFromCamera(new import_three19.Vector2(ndcX, ndcY), cam);
|
|
6510
6623
|
const hits = raycaster.intersectObject(scene, true);
|
|
6511
6624
|
if (hits.length === 0) return;
|
|
6512
6625
|
const hit = hits[0];
|
|
@@ -6701,7 +6814,8 @@ function ModelViewer({
|
|
|
6701
6814
|
shadowConfig: resolvedSceneConfig.shadows,
|
|
6702
6815
|
shadowsEnabled: shadows,
|
|
6703
6816
|
environmentRotation: resolvedSceneConfig.environment.rotation,
|
|
6704
|
-
environmentConfig: resolvedSceneConfig.environment
|
|
6817
|
+
environmentConfig: resolvedSceneConfig.environment,
|
|
6818
|
+
modelBounds
|
|
6705
6819
|
}
|
|
6706
6820
|
),
|
|
6707
6821
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
@@ -7269,7 +7383,7 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
|
|
|
7269
7383
|
|
|
7270
7384
|
// src/hooks/useViewerCamera.ts
|
|
7271
7385
|
var import_react23 = require("react");
|
|
7272
|
-
var
|
|
7386
|
+
var import_three20 = require("three");
|
|
7273
7387
|
|
|
7274
7388
|
// src/utils/resolveBindingKey.ts
|
|
7275
7389
|
function resolveBindingKey(objectBindings, objectId) {
|
|
@@ -7293,8 +7407,8 @@ function toTuple(vector) {
|
|
|
7293
7407
|
return [vector.x, vector.y, vector.z];
|
|
7294
7408
|
}
|
|
7295
7409
|
function buildCameraState(camera, controls) {
|
|
7296
|
-
const position = new
|
|
7297
|
-
const target = new
|
|
7410
|
+
const position = new import_three20.Vector3();
|
|
7411
|
+
const target = new import_three20.Vector3();
|
|
7298
7412
|
controls.getPosition(position);
|
|
7299
7413
|
controls.getTarget(target);
|
|
7300
7414
|
return {
|
|
@@ -13518,8 +13632,6 @@ function CameraPositionPanel({
|
|
|
13518
13632
|
var import_react36 = require("react");
|
|
13519
13633
|
|
|
13520
13634
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13521
|
-
var import_three_stdlib4 = require("three-stdlib");
|
|
13522
|
-
var import_three20 = require("three");
|
|
13523
13635
|
function slugify(value) {
|
|
13524
13636
|
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
13525
13637
|
}
|
|
@@ -13585,51 +13697,15 @@ function buildBindingsFromNodes(nodeNames) {
|
|
|
13585
13697
|
nodeNames.map((nodeName) => [nodeName, createDefaultBinding(nodeName)])
|
|
13586
13698
|
);
|
|
13587
13699
|
}
|
|
13588
|
-
function
|
|
13589
|
-
const
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
canvas: document.createElement("canvas"),
|
|
13595
|
-
antialias: false
|
|
13596
|
-
});
|
|
13597
|
-
configureGLTFLoader(loader, {}, renderer);
|
|
13598
|
-
} catch {
|
|
13599
|
-
renderer = null;
|
|
13600
|
-
configureGLTFLoader(loader, {});
|
|
13700
|
+
function collectNodeNames(scene) {
|
|
13701
|
+
const names = /* @__PURE__ */ new Set();
|
|
13702
|
+
scene.traverse((child) => {
|
|
13703
|
+
const mesh = child;
|
|
13704
|
+
if (mesh.isMesh && mesh.geometry && child.name) {
|
|
13705
|
+
names.add(child.name);
|
|
13601
13706
|
}
|
|
13602
|
-
} else {
|
|
13603
|
-
configureGLTFLoader(loader, {});
|
|
13604
|
-
}
|
|
13605
|
-
return new Promise((resolve, reject) => {
|
|
13606
|
-
loader.load(
|
|
13607
|
-
modelUrl,
|
|
13608
|
-
(gltf) => {
|
|
13609
|
-
const names = /* @__PURE__ */ new Set();
|
|
13610
|
-
gltf.scene.traverse((child) => {
|
|
13611
|
-
if (child?.isMesh && child.geometry && child.name) {
|
|
13612
|
-
names.add(child.name);
|
|
13613
|
-
}
|
|
13614
|
-
});
|
|
13615
|
-
resolve({
|
|
13616
|
-
nodeNames: Array.from(names).sort((a, b) => a.localeCompare(b)),
|
|
13617
|
-
animations: resolveAnimationClips(gltf.animations).map(
|
|
13618
|
-
({ sourceName, duration }) => ({
|
|
13619
|
-
sourceName,
|
|
13620
|
-
duration
|
|
13621
|
-
})
|
|
13622
|
-
)
|
|
13623
|
-
});
|
|
13624
|
-
renderer?.dispose();
|
|
13625
|
-
},
|
|
13626
|
-
void 0,
|
|
13627
|
-
(error) => {
|
|
13628
|
-
renderer?.dispose();
|
|
13629
|
-
reject(error);
|
|
13630
|
-
}
|
|
13631
|
-
);
|
|
13632
13707
|
});
|
|
13708
|
+
return Array.from(names).sort((a, b) => a.localeCompare(b));
|
|
13633
13709
|
}
|
|
13634
13710
|
|
|
13635
13711
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
@@ -14175,6 +14251,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14175
14251
|
const uploadedModelRef = (0, import_react38.useRef)(null);
|
|
14176
14252
|
const previewDragDepthRef = (0, import_react38.useRef)(0);
|
|
14177
14253
|
const loadRequestRef = (0, import_react38.useRef)(0);
|
|
14254
|
+
const pendingLoadRef = (0, import_react38.useRef)(null);
|
|
14255
|
+
const loadedSceneRef = (0, import_react38.useRef)(null);
|
|
14178
14256
|
const {
|
|
14179
14257
|
cameraState,
|
|
14180
14258
|
resetView,
|
|
@@ -14343,42 +14421,76 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14343
14421
|
});
|
|
14344
14422
|
}
|
|
14345
14423
|
};
|
|
14346
|
-
const
|
|
14424
|
+
const applyBindingsFromScene = (0, import_react38.useCallback)(
|
|
14425
|
+
(scene, nextModelName) => {
|
|
14426
|
+
const nodeNames = collectNodeNames(scene);
|
|
14427
|
+
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
14428
|
+
setSelectedItem(nodeNames[0] ?? null);
|
|
14429
|
+
setSceneConfig((prev) => ({ ...prev, annotations: [] }));
|
|
14430
|
+
setStatusMessage(
|
|
14431
|
+
`Generated ${nodeNames.length} bindings from ${nextModelName}.`
|
|
14432
|
+
);
|
|
14433
|
+
setStatusTone("success");
|
|
14434
|
+
setLoading(false);
|
|
14435
|
+
},
|
|
14436
|
+
[bindingsHistory, setSceneConfig]
|
|
14437
|
+
);
|
|
14438
|
+
const generateBindingsForModel = (nextModelUrl, nextModelName) => {
|
|
14347
14439
|
const requestId = ++loadRequestRef.current;
|
|
14348
14440
|
setLoading(true);
|
|
14349
14441
|
setStatusMessage(
|
|
14350
14442
|
`Reading ${nextModelName} and generating starter bindings...`
|
|
14351
14443
|
);
|
|
14352
14444
|
setStatusTone("default");
|
|
14353
|
-
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14360
|
-
|
|
14361
|
-
|
|
14445
|
+
setModelName(nextModelName);
|
|
14446
|
+
setActiveAnnotation(null);
|
|
14447
|
+
const loaded = loadedSceneRef.current;
|
|
14448
|
+
if (nextModelUrl === modelUrl && loaded?.url === nextModelUrl) {
|
|
14449
|
+
pendingLoadRef.current = null;
|
|
14450
|
+
applyBindingsFromScene(loaded.scene, nextModelName);
|
|
14451
|
+
return;
|
|
14452
|
+
}
|
|
14453
|
+
pendingLoadRef.current = {
|
|
14454
|
+
requestId,
|
|
14455
|
+
modelName: nextModelName,
|
|
14456
|
+
modelUrl: nextModelUrl
|
|
14457
|
+
};
|
|
14458
|
+
setModelUrl(nextModelUrl);
|
|
14459
|
+
bindingsHistory.reset({});
|
|
14460
|
+
setSelectedItem(null);
|
|
14461
|
+
};
|
|
14462
|
+
const handleViewerModelLoaded = (0, import_react38.useCallback)(
|
|
14463
|
+
(scene) => {
|
|
14464
|
+
const pending = pendingLoadRef.current;
|
|
14465
|
+
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14466
|
+
pendingLoadRef.current = null;
|
|
14467
|
+
loadedSceneRef.current = { url: pending.modelUrl, scene };
|
|
14468
|
+
applyBindingsFromScene(scene, pending.modelName);
|
|
14469
|
+
},
|
|
14470
|
+
[applyBindingsFromScene]
|
|
14471
|
+
);
|
|
14472
|
+
const handleViewerLoadError = (0, import_react38.useCallback)((error) => {
|
|
14473
|
+
const pending = pendingLoadRef.current;
|
|
14474
|
+
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14475
|
+
pendingLoadRef.current = null;
|
|
14476
|
+
console.error(error);
|
|
14477
|
+
setStatusMessage(
|
|
14478
|
+
`Could not read ${pending.modelName}. Make sure the file is a valid GLB/GLTF asset.`
|
|
14479
|
+
);
|
|
14480
|
+
setStatusTone("error");
|
|
14481
|
+
setLoading(false);
|
|
14482
|
+
}, []);
|
|
14483
|
+
const handleAnimationsReady = (0, import_react38.useCallback)(
|
|
14484
|
+
(controls) => {
|
|
14485
|
+
viewerAnimations.handleAnimationsReady(controls);
|
|
14486
|
+
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
14362
14487
|
setSceneConfig((prev) => ({
|
|
14363
14488
|
...prev,
|
|
14364
|
-
animations: mergeAnimationConfig(prev,
|
|
14365
|
-
annotations: []
|
|
14489
|
+
animations: mergeAnimationConfig(prev, details)
|
|
14366
14490
|
}));
|
|
14367
|
-
|
|
14368
|
-
|
|
14369
|
-
|
|
14370
|
-
setStatusTone("success");
|
|
14371
|
-
} catch (error) {
|
|
14372
|
-
if (requestId !== loadRequestRef.current) return;
|
|
14373
|
-
console.error(error);
|
|
14374
|
-
setStatusMessage(
|
|
14375
|
-
`Could not read ${nextModelName}. Make sure the file is a valid GLB/GLTF asset.`
|
|
14376
|
-
);
|
|
14377
|
-
setStatusTone("error");
|
|
14378
|
-
} finally {
|
|
14379
|
-
if (requestId === loadRequestRef.current) setLoading(false);
|
|
14380
|
-
}
|
|
14381
|
-
};
|
|
14491
|
+
},
|
|
14492
|
+
[viewerAnimations, setSceneConfig]
|
|
14493
|
+
);
|
|
14382
14494
|
const handleLoadDemo = async () => {
|
|
14383
14495
|
uploadedModelRef.current?.cleanup();
|
|
14384
14496
|
uploadedModelRef.current = null;
|
|
@@ -14408,6 +14520,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14408
14520
|
const clearModel = () => {
|
|
14409
14521
|
uploadedModelRef.current?.cleanup();
|
|
14410
14522
|
uploadedModelRef.current = null;
|
|
14523
|
+
loadedSceneRef.current = null;
|
|
14524
|
+
pendingLoadRef.current = null;
|
|
14411
14525
|
previewDragDepthRef.current = 0;
|
|
14412
14526
|
setIsPreviewDragActive(false);
|
|
14413
14527
|
setModelUrl(null);
|
|
@@ -15206,7 +15320,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15206
15320
|
onAnnotationsChange: handleAnnotationsChange,
|
|
15207
15321
|
activeAnnotation: activeAnnotation?.annotation ?? null,
|
|
15208
15322
|
onActiveAnnotationChange: handleActiveAnnotationChange,
|
|
15209
|
-
onAnimationsReady:
|
|
15323
|
+
onAnimationsReady: handleAnimationsReady,
|
|
15324
|
+
onModelLoaded: handleViewerModelLoaded,
|
|
15325
|
+
onLoadError: handleViewerLoadError,
|
|
15210
15326
|
licenseKey,
|
|
15211
15327
|
sceneConfig,
|
|
15212
15328
|
disableZoom: false,
|
|
@@ -15290,7 +15406,7 @@ var import_drei6 = require("@react-three/drei");
|
|
|
15290
15406
|
var import_fiber12 = require("@react-three/fiber");
|
|
15291
15407
|
var import_fiber13 = require("@react-three/fiber");
|
|
15292
15408
|
var import_three21 = require("three");
|
|
15293
|
-
var
|
|
15409
|
+
var import_three_stdlib4 = require("three-stdlib");
|
|
15294
15410
|
|
|
15295
15411
|
// src/styles/SimpleModelViewer.css
|
|
15296
15412
|
styleInject('.smv-root {\n --smv-panel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --smv-panel-bg: #141824;\n --smv-panel-bg-strong: #1a1f2e;\n --smv-panel-bg-strong-hover: #252b3b;\n --smv-panel-border: rgba(255, 255, 255, 0.06);\n --smv-panel-border-strong: rgba(255, 255, 255, 0.08);\n --smv-panel-border-active: rgba(255, 255, 255, 0.18);\n --smv-panel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --smv-panel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --smv-panel-text: #f1f5f9;\n --smv-panel-text-emphasis: #ffffff;\n --smv-panel-text-muted: #94a3b8;\n --smv-panel-text-subtle: #64748b;\n --smv-panel-surface: rgba(255, 255, 255, 0.05);\n --smv-panel-surface-hover: rgba(255, 255, 255, 0.1);\n --smv-panel-surface-soft: rgba(255, 255, 255, 0.04);\n --smv-panel-accent: #3b82f6;\n --smv-panel-accent-contrast: #ffffff;\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n display: flex;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.smv-canvasArea {\n flex: 1;\n position: relative;\n min-width: 0;\n}\n.smv-upload-input {\n display: none;\n}\n.smv-upload-toolbar {\n position: absolute;\n top: 12px;\n left: 50%;\n z-index: 120;\n display: flex;\n align-items: center;\n gap: 10px;\n max-width: calc(100% - 32px);\n padding: 5px 6px;\n border: 1px solid var(--smv-panel-border-strong);\n border-radius: 10px;\n background: rgba(20, 24, 36, 0.88);\n color: var(--smv-panel-text);\n box-shadow: 0 16px 30px rgba(0, 0, 0, 0.28);\n backdrop-filter: blur(14px);\n transform: translateX(-50%);\n}\n.smv-upload-toolbar__name {\n max-width: 260px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--smv-panel-text-muted);\n font-size: 12px;\n font-weight: 500;\n}\n.smv-upload-toolbar__button {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n min-height: 34px;\n padding: 0 14px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 10px;\n background: rgba(59, 130, 246, 0.18);\n color: var(--smv-panel-text-emphasis);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n.smv-upload-toolbar__button--ghost {\n background: var(--smv-panel-surface);\n color: var(--smv-panel-text-muted);\n}\n.smv-upload-empty-state {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n padding: 24px;\n}\n.smv-upload-empty-state__card {\n width: min(440px, 100%);\n padding: 28px;\n border: 1px dashed rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n color: var(--smv-panel-text);\n text-align: center;\n box-shadow: 0 24px 40px rgba(0, 0, 0, 0.28);\n backdrop-filter: blur(14px);\n}\n.smv-upload-empty-state__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n margin-bottom: 18px;\n border-radius: 10px;\n background: rgba(59, 130, 246, 0.18);\n color: #bfdbfe;\n}\n.smv-upload-empty-state__title {\n margin: 0 0 10px;\n color: var(--smv-panel-text-emphasis);\n font-size: 24px;\n line-height: 1.15;\n}\n.smv-upload-empty-state__body {\n margin: 0 0 18px;\n color: var(--smv-panel-text-muted);\n font-size: 14px;\n font-weight: 700;\n line-height: 1.6;\n}\n.smv-upload-empty-state__body code {\n color: var(--smv-panel-text-emphasis);\n font-family: var(--smv-panel-font-family);\n}\n.smv-upload-empty-state__button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n min-height: 40px;\n padding: 0 18px;\n border: none;\n border-radius: 8px;\n background: #3b82f6;\n color: #eff6ff;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n}\n.smv-upload-drop-overlay {\n position: absolute;\n inset: 0;\n z-index: 130;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(8, 12, 20, 0.48);\n backdrop-filter: blur(4px);\n}\n.smv-upload-drop-overlay__card {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 16px 20px;\n border: 1px dashed rgba(191, 219, 254, 0.75);\n border-radius: 10px;\n background: rgba(30, 41, 59, 0.92);\n color: #dbeafe;\n font-size: 14px;\n font-weight: 700;\n box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28);\n}\n.smv-canvasArea .bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 110;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(15, 23, 42, 0.46);\n backdrop-filter: blur(1px);\n}\n.smv-canvasArea .bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(226, 232, 240, 0.24);\n border-top-color: #93c5fd;\n animation: bb-spin 0.8s linear infinite;\n}\n.smv-canvasArea .bb-loadingMessage {\n max-width: 320px;\n color: #f7f8fa;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.right-panel-wrapper {\n height: 100%;\n transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n}\n.right-panel-wrapper--open {\n width: 300px;\n flex-shrink: 0;\n}\n.right-panel-wrapper--closed {\n width: 0;\n flex-shrink: 0;\n}\n.left-panel-wrapper {\n height: 100%;\n transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n order: -1;\n}\n.left-panel-wrapper--open {\n width: 280px;\n flex-shrink: 0;\n}\n.left-panel-wrapper--closed {\n width: 0;\n flex-shrink: 0;\n}\n.side-panel__open-btn.side-panel__open-btn--left {\n left: 12px;\n right: auto;\n}\n.side-panel.side-panel--left {\n left: 0;\n right: auto;\n width: 280px;\n transform: translateX(-100%);\n border-left: none;\n border-right: 1px solid var(--smv-panel-border);\n box-shadow: var(--smv-panel-shadow-left);\n}\n.side-panel.side-panel--open-left {\n transform: translateX(0);\n}\n.side-panel.side-panel--closed-left {\n transform: translateX(-100%);\n}\n.env-controls {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n flex: 1;\n}\n.env-section {\n border-bottom: 1px solid var(--smv-panel-border-strong);\n}\n.env-section__header {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 9px 14px;\n border: none;\n background: var(--smv-panel-surface-soft);\n color: var(--smv-panel-text-muted);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.env-section__chevron {\n transition: transform 0.15s;\n}\n.env-section__chevron--collapsed {\n transform: rotate(-90deg);\n}\n.env-section__body {\n display: flex;\n flex-direction: column;\n}\n.env-row {\n display: flex;\n gap: 6px;\n align-items: center;\n min-height: 32px;\n border-bottom: 1px solid var(--smv-panel-surface);\n}\n.env-row__label {\n flex: 0 0 42%;\n padding: 0 0 0 14px;\n font-size: 11.5px;\n font-weight: 500;\n color: var(--smv-panel-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.env-row__value {\n flex: 1;\n display: flex;\n align-items: center;\n padding: 5px 10px 5px 8px;\n min-width: 0;\n}\n.env-row__value--slider {\n gap: 6px;\n}\n.env-checkbox-btn {\n display: flex;\n align-items: center;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n}\n.env-checkbox {\n width: 15px;\n height: 15px;\n border-radius: 3px;\n border: 1.5px solid var(--smv-panel-border-active);\n background: var(--smv-panel-surface-soft);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s, border-color 0.15s;\n}\n.env-checkbox--checked {\n background: var(--smv-panel-accent);\n border-color: var(--smv-panel-accent);\n}\n.env-select {\n width: 100%;\n padding: 5px 8px;\n border-radius: 4px;\n border: none;\n background: var(--smv-panel-surface-hover);\n color: var(--smv-panel-text);\n font-size: 11.5px;\n outline: none;\n box-sizing: border-box;\n text-transform: capitalize;\n}\n.env-color-swatch {\n position: relative;\n width: 100%;\n height: 22px;\n border-radius: 4px;\n border: 1px solid rgba(255, 255, 255, 0.15);\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n cursor: pointer;\n}\n.env-color-swatch__hex {\n font-size: 10.5px;\n font-weight: 700;\n color: #0f172a;\n text-shadow: 0 1px 1px rgba(255, 255, 255, 0.35);\n pointer-events: none;\n text-transform: uppercase;\n}\n.env-color-swatch__input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: none;\n padding: 0;\n opacity: 0;\n cursor: pointer;\n}\n.env-slider {\n flex: 1;\n accent-color: var(--smv-panel-accent);\n cursor: pointer;\n min-width: 0;\n}\n.env-slider__value {\n flex-shrink: 0;\n min-width: 32px;\n padding: 2px 5px;\n border-radius: 4px;\n background: var(--smv-panel-surface-hover);\n color: var(--smv-panel-accent);\n font-size: 10.5px;\n font-weight: 600;\n text-align: center;\n}\n.side-panel__open-btn {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 7px 13px;\n border-radius: 7px;\n border: 1px solid var(--smv-panel-border-strong);\n background: var(--smv-panel-bg-strong);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--smv-panel-text);\n z-index: 190;\n transition: background 0.15s;\n}\n.side-panel__open-btn:hover {\n background: var(--smv-panel-bg-strong-hover);\n}\n.side-panel {\n position: absolute;\n top: 0;\n right: 0;\n width: 300px;\n height: 100%;\n background: var(--smv-panel-bg);\n display: flex;\n flex-direction: column;\n font-family: var(--smv-panel-font-family);\n transform: translateX(100%);\n transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n z-index: 200;\n border-left: 1px solid var(--smv-panel-border);\n box-shadow: var(--smv-panel-shadow-right);\n}\n.side-panel--open {\n transform: translateX(0);\n}\n.side-panel--closed {\n transform: translateX(100%);\n}\n.side-panel__header {\n padding: 24px 20px 16px 20px;\n border-bottom: 1px solid var(--smv-panel-border-strong);\n}\n.side-panel__header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 4px;\n}\n.side-panel__eyebrow {\n font-size: 15px;\n font-weight: 700;\n letter-spacing: 0.12em;\n color: var(--smv-panel-text-subtle);\n text-transform: uppercase;\n}\n.side-panel__title {\n font-size: 22px;\n font-weight: 700;\n color: var(--smv-panel-text);\n margin: 0;\n line-height: 1.2;\n}\n.side-panel__close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border-radius: 8px;\n border: 1px solid var(--smv-panel-border-strong);\n background: var(--smv-panel-surface);\n cursor: pointer;\n color: var(--smv-panel-text-muted);\n transition: background 0.15s, color 0.15s;\n flex-shrink: 0;\n}\n.side-panel__close-btn:hover {\n background: var(--smv-panel-surface-hover);\n color: var(--smv-panel-text);\n}\n.side-panel__search-wrap {\n padding: 14px 16px;\n border-bottom: 1px solid var(--smv-panel-border-strong);\n}\n.side-panel__search {\n width: 100%;\n padding: 10px 14px;\n border-radius: 10px;\n border: 1px solid var(--smv-panel-border-strong);\n background: var(--smv-panel-surface);\n color: var(--smv-panel-text);\n font-size: 13px;\n outline: none;\n box-sizing: border-box;\n transition: border-color 0.15s, background 0.15s;\n}\n.side-panel__search::placeholder {\n color: var(--smv-panel-text-subtle);\n}\n.side-panel__search:focus {\n border-color: var(--smv-panel-border-active);\n background: var(--smv-panel-surface-hover);\n}\n.side-panel__list {\n display: flex;\n flex-direction: column;\n padding: 10px 0;\n overflow-y: auto;\n flex: 1;\n}\n.side-panel__list::-webkit-scrollbar {\n width: 4px;\n}\n.side-panel__list::-webkit-scrollbar-track {\n background: transparent;\n}\n.side-panel__list::-webkit-scrollbar-thumb {\n background: var(--smv-panel-surface-hover);\n border-radius: 4px;\n}\n.side-panel__row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 20px;\n cursor: pointer;\n transition: background 0.12s;\n border-bottom: 1px solid var(--smv-panel-surface-soft);\n}\n.side-panel__row:hover,\n.side-panel__row--hovered {\n background: var(--smv-panel-surface);\n}\n.side-panel__label {\n font-size: 14px;\n font-weight: 600;\n color: var(--smv-panel-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n flex: 1;\n}\n.side-panel__label--hovered {\n color: var(--smv-panel-text-emphasis);\n}\n.side-panel__vis-btn {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n margin-left: 10px;\n}\n.side-panel__checkbox {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1.5px solid var(--smv-panel-border-active);\n background: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s, border-color 0.15s;\n}\n.side-panel__checkbox--checked {\n background: var(--smv-panel-accent);\n border-color: var(--smv-panel-accent);\n}\n@media (max-width: 900px) {\n .smv-upload-toolbar {\n top: 56px;\n gap: 8px;\n max-width: calc(100% - 24px);\n padding: 8px 10px;\n }\n .smv-upload-toolbar__name {\n max-width: 140px;\n }\n .right-panel-wrapper--open,\n .left-panel-wrapper--open {\n width: 0;\n }\n .side-panel,\n .side-panel.side-panel--left {\n width: min(300px, 85vw);\n }\n}\n');
|
|
@@ -15359,7 +15475,7 @@ function InnerModel({
|
|
|
15359
15475
|
const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
|
|
15360
15476
|
const [hoveredKey, setHoveredKey] = (0, import_react39.useState)(null);
|
|
15361
15477
|
const scene = (0, import_react39.useMemo)(
|
|
15362
|
-
() =>
|
|
15478
|
+
() => import_three_stdlib4.SkeletonUtils.clone(rawScene),
|
|
15363
15479
|
[rawScene]
|
|
15364
15480
|
);
|
|
15365
15481
|
const meshMap = (0, import_react39.useMemo)(() => {
|