@liveroom-tech/react-immersive 1.1.8 → 1.2.1
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 +288 -184
- package/dist/index.mjs +311 -203
- 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,56 @@ 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
|
+
function countTriangles(geometries) {
|
|
2507
|
+
let total = 0;
|
|
2508
|
+
for (const geometry of geometries) {
|
|
2509
|
+
const count = geometry.index ? geometry.index.count : geometry.attributes.position.count;
|
|
2510
|
+
total += count / 3;
|
|
2511
|
+
}
|
|
2512
|
+
return total;
|
|
2513
|
+
}
|
|
2514
|
+
var MAX_BVH_TRIANGLES = 12e6;
|
|
2515
|
+
async function buildSceneBVH(root, signal) {
|
|
2516
|
+
const geometries = collectGeometries(root);
|
|
2517
|
+
if (geometries.length === 0) return;
|
|
2518
|
+
if (countTriangles(geometries) > MAX_BVH_TRIANGLES) return;
|
|
2519
|
+
enableAcceleratedRaycast();
|
|
2520
|
+
for (const geometry of geometries) {
|
|
2521
|
+
if (signal?.aborted) return;
|
|
2522
|
+
const tree = import_three_mesh_bvh.computeBoundsTree.call(geometry);
|
|
2523
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2524
|
+
if (signal?.aborted) return;
|
|
2525
|
+
geometry.boundsTree = tree;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
function disposeSceneBVH(root) {
|
|
2529
|
+
root.traverse((node) => {
|
|
2530
|
+
const geometry = node.geometry;
|
|
2531
|
+
if (!geometry?.boundsTree) return;
|
|
2532
|
+
import_three_mesh_bvh.disposeBoundsTree.call(geometry);
|
|
2533
|
+
geometry.boundsTree = void 0;
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2454
2537
|
// src/components/Model.tsx
|
|
2455
2538
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2456
2539
|
function firstMaterial(node) {
|
|
@@ -2508,17 +2591,17 @@ function Model({
|
|
|
2508
2591
|
}, [scene]);
|
|
2509
2592
|
const shellNodeNames = (0, import_react11.useMemo)(() => {
|
|
2510
2593
|
const shell = /* @__PURE__ */ new Set();
|
|
2511
|
-
const sceneBox = new
|
|
2594
|
+
const sceneBox = new import_three14.Box3();
|
|
2512
2595
|
const meshNodes = [];
|
|
2513
2596
|
for (const [name, node] of Object.entries(nodeMap)) {
|
|
2514
2597
|
if (!isMeshNode(node)) continue;
|
|
2515
|
-
const box = new
|
|
2598
|
+
const box = new import_three14.Box3().setFromObject(node);
|
|
2516
2599
|
if (box.isEmpty()) continue;
|
|
2517
2600
|
meshNodes.push({ name, box });
|
|
2518
2601
|
sceneBox.union(box);
|
|
2519
2602
|
}
|
|
2520
|
-
const sceneSize = sceneBox.getSize(new
|
|
2521
|
-
const size = new
|
|
2603
|
+
const sceneSize = sceneBox.getSize(new import_three14.Vector3());
|
|
2604
|
+
const size = new import_three14.Vector3();
|
|
2522
2605
|
for (const { name, box } of meshNodes) {
|
|
2523
2606
|
box.getSize(size);
|
|
2524
2607
|
const isShell = size.x > sceneSize.x * 0.7 && size.z > sceneSize.z * 0.7 && size.y > sceneSize.y * 0.4;
|
|
@@ -2537,6 +2620,14 @@ function Model({
|
|
|
2537
2620
|
(0, import_react11.useEffect)(() => {
|
|
2538
2621
|
refreshSkinnedBounds(scene);
|
|
2539
2622
|
}, [scene]);
|
|
2623
|
+
(0, import_react11.useEffect)(() => {
|
|
2624
|
+
const controller = new AbortController();
|
|
2625
|
+
void buildSceneBVH(scene, controller.signal);
|
|
2626
|
+
return () => {
|
|
2627
|
+
controller.abort();
|
|
2628
|
+
disposeSceneBVH(scene);
|
|
2629
|
+
};
|
|
2630
|
+
}, [scene]);
|
|
2540
2631
|
const bindingKeyByNodeName = (0, import_react11.useMemo)(() => {
|
|
2541
2632
|
const map = {};
|
|
2542
2633
|
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
@@ -2584,7 +2675,7 @@ function Model({
|
|
|
2584
2675
|
const map = {};
|
|
2585
2676
|
if (shadingMode !== "unlit") return map;
|
|
2586
2677
|
for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
|
|
2587
|
-
const basic = new
|
|
2678
|
+
const basic = new import_three14.MeshBasicMaterial();
|
|
2588
2679
|
basic.color.copy(material.color);
|
|
2589
2680
|
basic.map = material.map;
|
|
2590
2681
|
basic.alphaMap = material.alphaMap;
|
|
@@ -2600,7 +2691,7 @@ function Model({
|
|
|
2600
2691
|
const map = {};
|
|
2601
2692
|
if (shadingMode !== "normals") return map;
|
|
2602
2693
|
for (const nodeName of Object.keys(perNodeMaterials)) {
|
|
2603
|
-
map[nodeName] = new
|
|
2694
|
+
map[nodeName] = new import_three14.MeshNormalMaterial();
|
|
2604
2695
|
}
|
|
2605
2696
|
return map;
|
|
2606
2697
|
}, [perNodeMaterials, shadingMode]);
|
|
@@ -2609,7 +2700,7 @@ function Model({
|
|
|
2609
2700
|
if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
|
|
2610
2701
|
const matcapTexture = getMatcapTexture();
|
|
2611
2702
|
for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
|
|
2612
|
-
const matcap = new
|
|
2703
|
+
const matcap = new import_three14.MeshMatcapMaterial();
|
|
2613
2704
|
matcap.matcap = matcapTexture;
|
|
2614
2705
|
matcap.color.copy(material.color);
|
|
2615
2706
|
matcap.alphaMap = material.alphaMap;
|
|
@@ -2776,16 +2867,16 @@ function Model({
|
|
|
2776
2867
|
const glbNodeName = binding.modelObjectId ?? nodeName;
|
|
2777
2868
|
const node = nodeMap[glbNodeName];
|
|
2778
2869
|
if (!isMeshNode(node)) continue;
|
|
2779
|
-
const geometry = new
|
|
2870
|
+
const geometry = new import_three14.WireframeGeometry(
|
|
2780
2871
|
node.geometry
|
|
2781
2872
|
);
|
|
2782
|
-
const lineMaterial = new
|
|
2873
|
+
const lineMaterial = new import_three14.LineBasicMaterial({
|
|
2783
2874
|
color: wireframeColor,
|
|
2784
2875
|
transparent: true,
|
|
2785
2876
|
opacity: wireframeOpacity,
|
|
2786
2877
|
depthTest: false
|
|
2787
2878
|
});
|
|
2788
|
-
const line = new
|
|
2879
|
+
const line = new import_three14.LineSegments(geometry, lineMaterial);
|
|
2789
2880
|
line.renderOrder = 1;
|
|
2790
2881
|
node.add(line);
|
|
2791
2882
|
added.push({ node, line });
|
|
@@ -2837,7 +2928,7 @@ function Model({
|
|
|
2837
2928
|
const applyClipConfigToAction = (action, clipConfig) => {
|
|
2838
2929
|
const loopMode = getClipLoopMode(clipConfig);
|
|
2839
2930
|
action.setLoop(
|
|
2840
|
-
loopMode === "once" ?
|
|
2931
|
+
loopMode === "once" ? import_three14.LoopOnce : import_three14.LoopRepeat,
|
|
2841
2932
|
loopMode === "once" ? 1 : Infinity
|
|
2842
2933
|
);
|
|
2843
2934
|
action.clampWhenFinished = loopMode === "once";
|
|
@@ -2904,7 +2995,7 @@ function Model({
|
|
|
2904
2995
|
invalidate();
|
|
2905
2996
|
};
|
|
2906
2997
|
(0, import_react11.useEffect)(() => {
|
|
2907
|
-
const mixer = new
|
|
2998
|
+
const mixer = new import_three14.AnimationMixer(scene);
|
|
2908
2999
|
mixerRef.current = mixer;
|
|
2909
3000
|
const actions = {};
|
|
2910
3001
|
for (const { clip, sourceName } of resolvedAnimationClips) {
|
|
@@ -2995,17 +3086,17 @@ function Model({
|
|
|
2995
3086
|
(0, import_fiber7.useFrame)((_, delta) => {
|
|
2996
3087
|
if (!groupRef.current) return;
|
|
2997
3088
|
const rotation = groupRef.current.rotation;
|
|
2998
|
-
rotation.x =
|
|
3089
|
+
rotation.x = import_three14.MathUtils.lerp(
|
|
2999
3090
|
rotation.x,
|
|
3000
3091
|
targetRotation[0],
|
|
3001
3092
|
1 - Math.exp(-5 * delta)
|
|
3002
3093
|
);
|
|
3003
|
-
rotation.y =
|
|
3094
|
+
rotation.y = import_three14.MathUtils.lerp(
|
|
3004
3095
|
rotation.y,
|
|
3005
3096
|
targetRotation[1],
|
|
3006
3097
|
1 - Math.exp(-5 * delta)
|
|
3007
3098
|
);
|
|
3008
|
-
rotation.z =
|
|
3099
|
+
rotation.z = import_three14.MathUtils.lerp(
|
|
3009
3100
|
rotation.z,
|
|
3010
3101
|
targetRotation[2],
|
|
3011
3102
|
1 - Math.exp(-5 * delta)
|
|
@@ -3016,16 +3107,16 @@ function Model({
|
|
|
3016
3107
|
const explodePartsRef = (0, import_react11.useRef)([]);
|
|
3017
3108
|
const explodeStrengthRef = (0, import_react11.useRef)(0);
|
|
3018
3109
|
const currentExplodeRef = (0, import_react11.useRef)(0);
|
|
3019
|
-
const explodeTmp = (0, import_react11.useMemo)(() => new
|
|
3110
|
+
const explodeTmp = (0, import_react11.useMemo)(() => new import_three14.Vector3(), []);
|
|
3020
3111
|
(0, import_react11.useEffect)(() => {
|
|
3021
3112
|
for (const part of explodePartsRef.current) {
|
|
3022
3113
|
part.node.position.copy(part.restLocalPos);
|
|
3023
3114
|
}
|
|
3024
3115
|
currentExplodeRef.current = 0;
|
|
3025
3116
|
scene.updateWorldMatrix(true, true);
|
|
3026
|
-
const sceneBox = new
|
|
3027
|
-
const modelCenter = sceneBox.getCenter(new
|
|
3028
|
-
const radius = sceneBox.getSize(new
|
|
3117
|
+
const sceneBox = new import_three14.Box3().setFromObject(scene);
|
|
3118
|
+
const modelCenter = sceneBox.getCenter(new import_three14.Vector3());
|
|
3119
|
+
const radius = sceneBox.getSize(new import_three14.Vector3()).length() * 0.5;
|
|
3029
3120
|
explodeStrengthRef.current = Number.isFinite(radius) ? radius : 0;
|
|
3030
3121
|
const targets = [];
|
|
3031
3122
|
const targetSet = /* @__PURE__ */ new Set();
|
|
@@ -3036,7 +3127,7 @@ function Model({
|
|
|
3036
3127
|
targets.push(node);
|
|
3037
3128
|
}
|
|
3038
3129
|
const parts = [];
|
|
3039
|
-
const partCenter = new
|
|
3130
|
+
const partCenter = new import_three14.Vector3();
|
|
3040
3131
|
for (const node of targets) {
|
|
3041
3132
|
if (!node.parent) continue;
|
|
3042
3133
|
let ancestor = node.parent;
|
|
@@ -3049,7 +3140,7 @@ function Model({
|
|
|
3049
3140
|
ancestor = ancestor.parent;
|
|
3050
3141
|
}
|
|
3051
3142
|
if (nested) continue;
|
|
3052
|
-
const box = new
|
|
3143
|
+
const box = new import_three14.Box3().setFromObject(node);
|
|
3053
3144
|
if (box.isEmpty()) continue;
|
|
3054
3145
|
box.getCenter(partCenter);
|
|
3055
3146
|
const worldDir = partCenter.clone().sub(modelCenter);
|
|
@@ -3058,9 +3149,9 @@ function Model({
|
|
|
3058
3149
|
parts.push({
|
|
3059
3150
|
node,
|
|
3060
3151
|
restLocalPos: node.position.clone(),
|
|
3061
|
-
restWorldPos: node.getWorldPosition(new
|
|
3152
|
+
restWorldPos: node.getWorldPosition(new import_three14.Vector3()),
|
|
3062
3153
|
worldDir,
|
|
3063
|
-
parentInverse: new
|
|
3154
|
+
parentInverse: new import_three14.Matrix4().copy(node.parent.matrixWorld).invert()
|
|
3064
3155
|
});
|
|
3065
3156
|
}
|
|
3066
3157
|
explodePartsRef.current = parts;
|
|
@@ -3078,7 +3169,7 @@ function Model({
|
|
|
3078
3169
|
if (current === target) return;
|
|
3079
3170
|
currentExplodeRef.current = target;
|
|
3080
3171
|
} else {
|
|
3081
|
-
currentExplodeRef.current =
|
|
3172
|
+
currentExplodeRef.current = import_three14.MathUtils.lerp(
|
|
3082
3173
|
current,
|
|
3083
3174
|
target,
|
|
3084
3175
|
1 - Math.exp(-6 * delta)
|
|
@@ -3143,7 +3234,7 @@ function Model({
|
|
|
3143
3234
|
var import_react12 = require("react");
|
|
3144
3235
|
var import_drei4 = require("@react-three/drei");
|
|
3145
3236
|
var import_fiber8 = require("@react-three/fiber");
|
|
3146
|
-
var
|
|
3237
|
+
var import_three15 = require("three");
|
|
3147
3238
|
|
|
3148
3239
|
// src/styles/AnnotationsLayer.css
|
|
3149
3240
|
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 +3247,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3156
3247
|
const scene = (0, import_fiber8.useThree)((state) => state.scene);
|
|
3157
3248
|
const anchorNodeRef = (0, import_react12.useRef)(null);
|
|
3158
3249
|
const baseLocalPosition = (0, import_react12.useMemo)(
|
|
3159
|
-
() => new
|
|
3250
|
+
() => new import_three15.Vector3(
|
|
3160
3251
|
...annotation.anchorLocalPosition ?? annotation.localPosition ?? annotation.worldPosition
|
|
3161
3252
|
),
|
|
3162
3253
|
[
|
|
@@ -3165,9 +3256,9 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3165
3256
|
annotation.worldPosition
|
|
3166
3257
|
]
|
|
3167
3258
|
);
|
|
3168
|
-
const worldPosition = (0, import_react12.useMemo)(() => new
|
|
3169
|
-
const biasedWorldPosition = (0, import_react12.useMemo)(() => new
|
|
3170
|
-
const toCamera = (0, import_react12.useMemo)(() => new
|
|
3259
|
+
const worldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3260
|
+
const biasedWorldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3261
|
+
const toCamera = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
|
|
3171
3262
|
(0, import_react12.useEffect)(() => {
|
|
3172
3263
|
anchorNodeRef.current = null;
|
|
3173
3264
|
}, [annotation.anchorNodeName]);
|
|
@@ -3190,7 +3281,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3190
3281
|
if (worldPositionsRef) {
|
|
3191
3282
|
let stored = worldPositionsRef.current.get(annotation.id);
|
|
3192
3283
|
if (!stored) {
|
|
3193
|
-
stored = new
|
|
3284
|
+
stored = new import_three15.Vector3();
|
|
3194
3285
|
worldPositionsRef.current.set(annotation.id, stored);
|
|
3195
3286
|
}
|
|
3196
3287
|
stored.copy(worldPosition);
|
|
@@ -3488,17 +3579,17 @@ function AnnotationsLayer({
|
|
|
3488
3579
|
);
|
|
3489
3580
|
(0, import_fiber8.useFrame)((_, delta) => {
|
|
3490
3581
|
if (!groupRef.current) return;
|
|
3491
|
-
groupRef.current.rotation.x =
|
|
3582
|
+
groupRef.current.rotation.x = import_three15.MathUtils.lerp(
|
|
3492
3583
|
groupRef.current.rotation.x,
|
|
3493
3584
|
targetRotation[0],
|
|
3494
3585
|
1 - Math.exp(-5 * delta)
|
|
3495
3586
|
);
|
|
3496
|
-
groupRef.current.rotation.y =
|
|
3587
|
+
groupRef.current.rotation.y = import_three15.MathUtils.lerp(
|
|
3497
3588
|
groupRef.current.rotation.y,
|
|
3498
3589
|
targetRotation[1],
|
|
3499
3590
|
1 - Math.exp(-5 * delta)
|
|
3500
3591
|
);
|
|
3501
|
-
groupRef.current.rotation.z =
|
|
3592
|
+
groupRef.current.rotation.z = import_three15.MathUtils.lerp(
|
|
3502
3593
|
groupRef.current.rotation.z,
|
|
3503
3594
|
targetRotation[2],
|
|
3504
3595
|
1 - Math.exp(-5 * delta)
|
|
@@ -3536,7 +3627,7 @@ function AnnotationsLayer({
|
|
|
3536
3627
|
// src/components/ModelViewer/MeasurementLayer.tsx
|
|
3537
3628
|
var import_react13 = require("react");
|
|
3538
3629
|
var import_drei5 = require("@react-three/drei");
|
|
3539
|
-
var
|
|
3630
|
+
var import_three16 = require("three");
|
|
3540
3631
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3541
3632
|
var LINE_COLOR = "#22d3ee";
|
|
3542
3633
|
var BOX_COLOR = "#facc15";
|
|
@@ -3564,8 +3655,8 @@ function DimensionsBox({ box, unit }) {
|
|
|
3564
3655
|
);
|
|
3565
3656
|
const edges = (0, import_react13.useMemo)(() => {
|
|
3566
3657
|
const [w2, h2, d2] = box.size;
|
|
3567
|
-
const geometry = new
|
|
3568
|
-
new
|
|
3658
|
+
const geometry = new import_three16.EdgesGeometry(
|
|
3659
|
+
new import_three16.BoxGeometry(w2 || 1e-4, h2 || 1e-4, d2 || 1e-4)
|
|
3569
3660
|
);
|
|
3570
3661
|
return geometry;
|
|
3571
3662
|
}, [box.size]);
|
|
@@ -3611,7 +3702,7 @@ function MeasurementLayer({
|
|
|
3611
3702
|
points.slice(1).map((point, index) => {
|
|
3612
3703
|
const a = points[index].position;
|
|
3613
3704
|
const b = point.position;
|
|
3614
|
-
const distance = new
|
|
3705
|
+
const distance = new import_three16.Vector3(...a).distanceTo(new import_three16.Vector3(...b));
|
|
3615
3706
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("group", { children: [
|
|
3616
3707
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_drei5.Line, { points: [a, b], color: LINE_COLOR, lineWidth: 2 }),
|
|
3617
3708
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MeasureLabel, { position: midpoint(a, b), children: formatMeasurement(distance, unit) })
|
|
@@ -3625,7 +3716,7 @@ function MeasurementLayer({
|
|
|
3625
3716
|
var import_react14 = require("react");
|
|
3626
3717
|
var import_xr3 = require("@react-three/xr");
|
|
3627
3718
|
var import_fiber9 = require("@react-three/fiber");
|
|
3628
|
-
var
|
|
3719
|
+
var import_three17 = require("three");
|
|
3629
3720
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
3630
3721
|
var TARGET_SIZE = 1;
|
|
3631
3722
|
var FORWARD_DISTANCE = 1;
|
|
@@ -3662,13 +3753,13 @@ var XRModelPresenter = (0, import_react14.forwardRef)(
|
|
|
3662
3753
|
() => box ? computeNormalization(box, scaleMode) : null,
|
|
3663
3754
|
[box, scaleMode]
|
|
3664
3755
|
);
|
|
3665
|
-
const placedPosition = (0, import_react14.useRef)(new
|
|
3756
|
+
const placedPosition = (0, import_react14.useRef)(new import_three17.Vector3(0, 0, -FORWARD_DISTANCE));
|
|
3666
3757
|
const userScale = (0, import_react14.useRef)(1);
|
|
3667
3758
|
const rotationY = (0, import_react14.useRef)(0);
|
|
3668
3759
|
const placementMode = (0, import_react14.useRef)(true);
|
|
3669
3760
|
const modelPlaced = (0, import_react14.useRef)(false);
|
|
3670
|
-
const hitMatrix = (0, import_react14.useMemo)(() => new
|
|
3671
|
-
const hitPosition = (0, import_react14.useRef)(new
|
|
3761
|
+
const hitMatrix = (0, import_react14.useMemo)(() => new import_three17.Matrix4(), []);
|
|
3762
|
+
const hitPosition = (0, import_react14.useRef)(new import_three17.Vector3());
|
|
3672
3763
|
const hasHit = (0, import_react14.useRef)(false);
|
|
3673
3764
|
const lastARState = (0, import_react14.useRef)(null);
|
|
3674
3765
|
const gesturing = (0, import_react14.useRef)(false);
|
|
@@ -3861,7 +3952,7 @@ var XRModelPresenter_default = XRModelPresenter;
|
|
|
3861
3952
|
// src/components/ModelViewer/CinematicCamera.tsx
|
|
3862
3953
|
var import_react15 = require("react");
|
|
3863
3954
|
var import_fiber10 = require("@react-three/fiber");
|
|
3864
|
-
var
|
|
3955
|
+
var import_three18 = require("three");
|
|
3865
3956
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
3866
3957
|
function CinematicCamera({
|
|
3867
3958
|
controlsRef,
|
|
@@ -3883,14 +3974,14 @@ function CinematicCamera({
|
|
|
3883
3974
|
const curves = (0, import_react15.useMemo)(() => {
|
|
3884
3975
|
if (!hasPath) return null;
|
|
3885
3976
|
return {
|
|
3886
|
-
position: new
|
|
3887
|
-
waypoints.map((w) => new
|
|
3977
|
+
position: new import_three18.CatmullRomCurve3(
|
|
3978
|
+
waypoints.map((w) => new import_three18.Vector3(...w.position)),
|
|
3888
3979
|
loop,
|
|
3889
3980
|
"catmullrom",
|
|
3890
3981
|
0.5
|
|
3891
3982
|
),
|
|
3892
|
-
target: new
|
|
3893
|
-
waypoints.map((w) => new
|
|
3983
|
+
target: new import_three18.CatmullRomCurve3(
|
|
3984
|
+
waypoints.map((w) => new import_three18.Vector3(...w.target)),
|
|
3894
3985
|
loop,
|
|
3895
3986
|
"catmullrom",
|
|
3896
3987
|
0.5
|
|
@@ -3899,12 +3990,12 @@ function CinematicCamera({
|
|
|
3899
3990
|
}, [signature, hasPath, loop]);
|
|
3900
3991
|
const elapsedRef = (0, import_react15.useRef)(0);
|
|
3901
3992
|
const enterElapsedRef = (0, import_react15.useRef)(0);
|
|
3902
|
-
const enterPos = (0, import_react15.useRef)(new
|
|
3903
|
-
const enterTarget = (0, import_react15.useRef)(new
|
|
3993
|
+
const enterPos = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
3994
|
+
const enterTarget = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
3904
3995
|
const startedRef = (0, import_react15.useRef)(false);
|
|
3905
3996
|
const orbitRef = (0, import_react15.useRef)(null);
|
|
3906
|
-
const tmpPos = (0, import_react15.useMemo)(() => new
|
|
3907
|
-
const tmpTarget = (0, import_react15.useMemo)(() => new
|
|
3997
|
+
const tmpPos = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
3998
|
+
const tmpTarget = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
3908
3999
|
(0, import_react15.useEffect)(() => {
|
|
3909
4000
|
if (!playing) {
|
|
3910
4001
|
startedRef.current = false;
|
|
@@ -3920,7 +4011,7 @@ function CinematicCamera({
|
|
|
3920
4011
|
if (!hasPath) {
|
|
3921
4012
|
const scene = sceneRef.current;
|
|
3922
4013
|
if (scene) {
|
|
3923
|
-
const sphere = new
|
|
4014
|
+
const sphere = new import_three18.Box3().setFromObject(scene).getBoundingSphere(new import_three18.Sphere());
|
|
3924
4015
|
orbitRef.current = {
|
|
3925
4016
|
center: sphere.center.clone(),
|
|
3926
4017
|
radius: sphere.radius > 0 ? sphere.radius : 1
|
|
@@ -5049,7 +5140,7 @@ function findInfluencingBone(hit) {
|
|
|
5049
5140
|
if (!skinIndex || !skinWeight || !position) return null;
|
|
5050
5141
|
const localHit = mesh.worldToLocal(hit.point.clone());
|
|
5051
5142
|
const vertices = [hit.face.a, hit.face.b, hit.face.c];
|
|
5052
|
-
const vertex = new
|
|
5143
|
+
const vertex = new import_three19.Vector3();
|
|
5053
5144
|
let nearest = vertices[0];
|
|
5054
5145
|
let nearestDist = Infinity;
|
|
5055
5146
|
for (const vi of vertices) {
|
|
@@ -5099,9 +5190,9 @@ function GuidedTourCameraFollow({
|
|
|
5099
5190
|
const scene = (0, import_fiber11.useThree)((state) => state.scene);
|
|
5100
5191
|
const nodeRef = (0, import_react22.useRef)(null);
|
|
5101
5192
|
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
|
|
5193
|
+
const live = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5194
|
+
const prev = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5195
|
+
const goal = (0, import_react22.useMemo)(() => new import_three19.Vector3(), []);
|
|
5105
5196
|
const nodeName = annotation?.anchorNodeName;
|
|
5106
5197
|
const localPosition = annotation?.anchorLocalPosition;
|
|
5107
5198
|
(0, import_react22.useEffect)(() => {
|
|
@@ -5140,8 +5231,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
|
|
|
5140
5231
|
isModelPlaced: false
|
|
5141
5232
|
};
|
|
5142
5233
|
function computeMeasureBox(scene) {
|
|
5143
|
-
const box = new
|
|
5144
|
-
const size = new
|
|
5234
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5235
|
+
const size = new import_three19.Vector3();
|
|
5145
5236
|
box.getSize(size);
|
|
5146
5237
|
return {
|
|
5147
5238
|
min: [box.min.x, box.min.y, box.min.z],
|
|
@@ -5149,11 +5240,19 @@ function computeMeasureBox(scene) {
|
|
|
5149
5240
|
size: [size.x, size.y, size.z]
|
|
5150
5241
|
};
|
|
5151
5242
|
}
|
|
5243
|
+
function computeModelBounds(scene) {
|
|
5244
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5245
|
+
if (box.isEmpty()) return null;
|
|
5246
|
+
const center = box.getCenter(new import_three19.Vector3());
|
|
5247
|
+
const radius = box.getSize(new import_three19.Vector3()).length() / 2;
|
|
5248
|
+
if (!Number.isFinite(radius) || radius <= 0) return null;
|
|
5249
|
+
return { center: [center.x, center.y, center.z], radius };
|
|
5250
|
+
}
|
|
5152
5251
|
function fitClippingPlanesToScene(scene, controls) {
|
|
5153
5252
|
const camera = controls?.camera;
|
|
5154
|
-
if (!(camera instanceof
|
|
5155
|
-
const box = new
|
|
5156
|
-
const size = box.getSize(new
|
|
5253
|
+
if (!(camera instanceof import_three19.PerspectiveCamera)) return;
|
|
5254
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5255
|
+
const size = box.getSize(new import_three19.Vector3()).length();
|
|
5157
5256
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
5158
5257
|
camera.near = size / 100;
|
|
5159
5258
|
camera.far = size * 100;
|
|
@@ -5306,8 +5405,8 @@ function ModelViewer({
|
|
|
5306
5405
|
if (!controls || initialCameraStateRef.current) {
|
|
5307
5406
|
return;
|
|
5308
5407
|
}
|
|
5309
|
-
const position = controls.getPosition(new
|
|
5310
|
-
const target = controls.getTarget(new
|
|
5408
|
+
const position = controls.getPosition(new import_three19.Vector3());
|
|
5409
|
+
const target = controls.getTarget(new import_three19.Vector3());
|
|
5311
5410
|
initialCameraStateRef.current = {
|
|
5312
5411
|
position: [position.x, position.y, position.z],
|
|
5313
5412
|
target: [target.x, target.y, target.z]
|
|
@@ -5318,6 +5417,7 @@ function ModelViewer({
|
|
|
5318
5417
|
const [measurePoints, setMeasurePoints] = (0, import_react22.useState)([]);
|
|
5319
5418
|
const [showDimensions, setShowDimensions] = (0, import_react22.useState)(false);
|
|
5320
5419
|
const [measureBox, setMeasureBox] = (0, import_react22.useState)(null);
|
|
5420
|
+
const [modelBounds, setModelBounds] = (0, import_react22.useState)(null);
|
|
5321
5421
|
const measureModeRef = (0, import_react22.useRef)(false);
|
|
5322
5422
|
measureModeRef.current = measureMode;
|
|
5323
5423
|
const measurePointCounter = (0, import_react22.useRef)(0);
|
|
@@ -5561,7 +5661,7 @@ function ModelViewer({
|
|
|
5561
5661
|
const controls = controlsRef.current;
|
|
5562
5662
|
const scene = sceneRef.current;
|
|
5563
5663
|
if (!controls) return;
|
|
5564
|
-
const target = new
|
|
5664
|
+
const target = new import_three19.Vector3();
|
|
5565
5665
|
const livePosition = annotationWorldPositionsRef.current.get(
|
|
5566
5666
|
annotation.id
|
|
5567
5667
|
);
|
|
@@ -5579,15 +5679,15 @@ function ModelViewer({
|
|
|
5579
5679
|
controls.smoothTime = 0.6;
|
|
5580
5680
|
const storedNormal = annotation.worldNormal;
|
|
5581
5681
|
if (scene) {
|
|
5582
|
-
const box = new
|
|
5583
|
-
const modelCenter = new
|
|
5682
|
+
const box = new import_three19.Box3().setFromObject(scene);
|
|
5683
|
+
const modelCenter = new import_three19.Vector3();
|
|
5584
5684
|
box.getCenter(modelCenter);
|
|
5585
|
-
const modelSize = new
|
|
5685
|
+
const modelSize = new import_three19.Vector3();
|
|
5586
5686
|
box.getSize(modelSize);
|
|
5587
5687
|
const modelRadius = modelSize.length() * 0.2;
|
|
5588
5688
|
let outwardDir;
|
|
5589
5689
|
if (storedNormal) {
|
|
5590
|
-
outwardDir = new
|
|
5690
|
+
outwardDir = new import_three19.Vector3(
|
|
5591
5691
|
storedNormal[0],
|
|
5592
5692
|
storedNormal[1],
|
|
5593
5693
|
storedNormal[2]
|
|
@@ -5885,8 +5985,8 @@ function ModelViewer({
|
|
|
5885
5985
|
};
|
|
5886
5986
|
if (canvasContainer && cameraControls && object) {
|
|
5887
5987
|
const canvasRect = canvasContainer.getBoundingClientRect();
|
|
5888
|
-
const objectCenter = new
|
|
5889
|
-
new
|
|
5988
|
+
const objectCenter = new import_three19.Vector3();
|
|
5989
|
+
new import_three19.Box3().setFromObject(object).getCenter(objectCenter);
|
|
5890
5990
|
objectCenter.project(cameraControls);
|
|
5891
5991
|
const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
|
|
5892
5992
|
const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
|
|
@@ -6110,14 +6210,14 @@ function ModelViewer({
|
|
|
6110
6210
|
}
|
|
6111
6211
|
try {
|
|
6112
6212
|
if (flyIn) {
|
|
6113
|
-
const target = controls.getTarget(new
|
|
6114
|
-
const position = controls.getPosition(new
|
|
6213
|
+
const target = controls.getTarget(new import_three19.Vector3());
|
|
6214
|
+
const position = controls.getPosition(new import_three19.Vector3());
|
|
6115
6215
|
const direction = position.clone().sub(target);
|
|
6116
6216
|
if (direction.lengthSq() < 1e-6) {
|
|
6117
6217
|
direction.set(0, 0, 1);
|
|
6118
6218
|
}
|
|
6119
6219
|
direction.normalize();
|
|
6120
|
-
const sphere = new
|
|
6220
|
+
const sphere = new import_three19.Box3().setFromObject(scene).getBoundingSphere(new import_three19.Sphere());
|
|
6121
6221
|
const farDistance = Math.max(sphere.radius * 10, 20);
|
|
6122
6222
|
const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
|
|
6123
6223
|
await controls.setLookAt(
|
|
@@ -6346,6 +6446,7 @@ function ModelViewer({
|
|
|
6346
6446
|
(scene) => {
|
|
6347
6447
|
sceneRef.current = scene;
|
|
6348
6448
|
setMeasureBox(computeMeasureBox(scene));
|
|
6449
|
+
setModelBounds(computeModelBounds(scene));
|
|
6349
6450
|
setModelLoaded(true);
|
|
6350
6451
|
setModelLoadFailed(false);
|
|
6351
6452
|
setMismatchDismissed(false);
|
|
@@ -6408,8 +6509,8 @@ function ModelViewer({
|
|
|
6408
6509
|
const rect = canvas.getBoundingClientRect();
|
|
6409
6510
|
const ndcX = (event.clientX - rect.left) / rect.width * 2 - 1;
|
|
6410
6511
|
const ndcY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
6411
|
-
const raycaster = new
|
|
6412
|
-
raycaster.setFromCamera(new
|
|
6512
|
+
const raycaster = new import_three19.Raycaster();
|
|
6513
|
+
raycaster.setFromCamera(new import_three19.Vector2(ndcX, ndcY), cam);
|
|
6413
6514
|
const hits = raycaster.intersectObject(scene, true);
|
|
6414
6515
|
if (hits.length === 0) return;
|
|
6415
6516
|
const { point } = hits[0];
|
|
@@ -6505,8 +6606,8 @@ function ModelViewer({
|
|
|
6505
6606
|
const rect = canvas.getBoundingClientRect();
|
|
6506
6607
|
const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
6507
6608
|
const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
6508
|
-
const raycaster = new
|
|
6509
|
-
raycaster.setFromCamera(new
|
|
6609
|
+
const raycaster = new import_three19.Raycaster();
|
|
6610
|
+
raycaster.setFromCamera(new import_three19.Vector2(ndcX, ndcY), cam);
|
|
6510
6611
|
const hits = raycaster.intersectObject(scene, true);
|
|
6511
6612
|
if (hits.length === 0) return;
|
|
6512
6613
|
const hit = hits[0];
|
|
@@ -6701,7 +6802,8 @@ function ModelViewer({
|
|
|
6701
6802
|
shadowConfig: resolvedSceneConfig.shadows,
|
|
6702
6803
|
shadowsEnabled: shadows,
|
|
6703
6804
|
environmentRotation: resolvedSceneConfig.environment.rotation,
|
|
6704
|
-
environmentConfig: resolvedSceneConfig.environment
|
|
6805
|
+
environmentConfig: resolvedSceneConfig.environment,
|
|
6806
|
+
modelBounds
|
|
6705
6807
|
}
|
|
6706
6808
|
),
|
|
6707
6809
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
@@ -7269,7 +7371,7 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
|
|
|
7269
7371
|
|
|
7270
7372
|
// src/hooks/useViewerCamera.ts
|
|
7271
7373
|
var import_react23 = require("react");
|
|
7272
|
-
var
|
|
7374
|
+
var import_three20 = require("three");
|
|
7273
7375
|
|
|
7274
7376
|
// src/utils/resolveBindingKey.ts
|
|
7275
7377
|
function resolveBindingKey(objectBindings, objectId) {
|
|
@@ -7293,8 +7395,8 @@ function toTuple(vector) {
|
|
|
7293
7395
|
return [vector.x, vector.y, vector.z];
|
|
7294
7396
|
}
|
|
7295
7397
|
function buildCameraState(camera, controls) {
|
|
7296
|
-
const position = new
|
|
7297
|
-
const target = new
|
|
7398
|
+
const position = new import_three20.Vector3();
|
|
7399
|
+
const target = new import_three20.Vector3();
|
|
7298
7400
|
controls.getPosition(position);
|
|
7299
7401
|
controls.getTarget(target);
|
|
7300
7402
|
return {
|
|
@@ -13518,8 +13620,6 @@ function CameraPositionPanel({
|
|
|
13518
13620
|
var import_react36 = require("react");
|
|
13519
13621
|
|
|
13520
13622
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13521
|
-
var import_three_stdlib4 = require("three-stdlib");
|
|
13522
|
-
var import_three20 = require("three");
|
|
13523
13623
|
function slugify(value) {
|
|
13524
13624
|
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
13625
|
}
|
|
@@ -13585,51 +13685,15 @@ function buildBindingsFromNodes(nodeNames) {
|
|
|
13585
13685
|
nodeNames.map((nodeName) => [nodeName, createDefaultBinding(nodeName)])
|
|
13586
13686
|
);
|
|
13587
13687
|
}
|
|
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, {});
|
|
13688
|
+
function collectNodeNames(scene) {
|
|
13689
|
+
const names = /* @__PURE__ */ new Set();
|
|
13690
|
+
scene.traverse((child) => {
|
|
13691
|
+
const mesh = child;
|
|
13692
|
+
if (mesh.isMesh && mesh.geometry && child.name) {
|
|
13693
|
+
names.add(child.name);
|
|
13601
13694
|
}
|
|
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
13695
|
});
|
|
13696
|
+
return Array.from(names).sort((a, b) => a.localeCompare(b));
|
|
13633
13697
|
}
|
|
13634
13698
|
|
|
13635
13699
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
@@ -14175,6 +14239,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14175
14239
|
const uploadedModelRef = (0, import_react38.useRef)(null);
|
|
14176
14240
|
const previewDragDepthRef = (0, import_react38.useRef)(0);
|
|
14177
14241
|
const loadRequestRef = (0, import_react38.useRef)(0);
|
|
14242
|
+
const pendingLoadRef = (0, import_react38.useRef)(null);
|
|
14243
|
+
const loadedSceneRef = (0, import_react38.useRef)(null);
|
|
14178
14244
|
const {
|
|
14179
14245
|
cameraState,
|
|
14180
14246
|
resetView,
|
|
@@ -14343,42 +14409,76 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14343
14409
|
});
|
|
14344
14410
|
}
|
|
14345
14411
|
};
|
|
14346
|
-
const
|
|
14412
|
+
const applyBindingsFromScene = (0, import_react38.useCallback)(
|
|
14413
|
+
(scene, nextModelName) => {
|
|
14414
|
+
const nodeNames = collectNodeNames(scene);
|
|
14415
|
+
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
14416
|
+
setSelectedItem(nodeNames[0] ?? null);
|
|
14417
|
+
setSceneConfig((prev) => ({ ...prev, annotations: [] }));
|
|
14418
|
+
setStatusMessage(
|
|
14419
|
+
`Generated ${nodeNames.length} bindings from ${nextModelName}.`
|
|
14420
|
+
);
|
|
14421
|
+
setStatusTone("success");
|
|
14422
|
+
setLoading(false);
|
|
14423
|
+
},
|
|
14424
|
+
[bindingsHistory, setSceneConfig]
|
|
14425
|
+
);
|
|
14426
|
+
const generateBindingsForModel = (nextModelUrl, nextModelName) => {
|
|
14347
14427
|
const requestId = ++loadRequestRef.current;
|
|
14348
14428
|
setLoading(true);
|
|
14349
14429
|
setStatusMessage(
|
|
14350
14430
|
`Reading ${nextModelName} and generating starter bindings...`
|
|
14351
14431
|
);
|
|
14352
14432
|
setStatusTone("default");
|
|
14353
|
-
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14360
|
-
|
|
14361
|
-
|
|
14433
|
+
setModelName(nextModelName);
|
|
14434
|
+
setActiveAnnotation(null);
|
|
14435
|
+
const loaded = loadedSceneRef.current;
|
|
14436
|
+
if (nextModelUrl === modelUrl && loaded?.url === nextModelUrl) {
|
|
14437
|
+
pendingLoadRef.current = null;
|
|
14438
|
+
applyBindingsFromScene(loaded.scene, nextModelName);
|
|
14439
|
+
return;
|
|
14440
|
+
}
|
|
14441
|
+
pendingLoadRef.current = {
|
|
14442
|
+
requestId,
|
|
14443
|
+
modelName: nextModelName,
|
|
14444
|
+
modelUrl: nextModelUrl
|
|
14445
|
+
};
|
|
14446
|
+
setModelUrl(nextModelUrl);
|
|
14447
|
+
bindingsHistory.reset({});
|
|
14448
|
+
setSelectedItem(null);
|
|
14449
|
+
};
|
|
14450
|
+
const handleViewerModelLoaded = (0, import_react38.useCallback)(
|
|
14451
|
+
(scene) => {
|
|
14452
|
+
const pending = pendingLoadRef.current;
|
|
14453
|
+
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14454
|
+
pendingLoadRef.current = null;
|
|
14455
|
+
loadedSceneRef.current = { url: pending.modelUrl, scene };
|
|
14456
|
+
applyBindingsFromScene(scene, pending.modelName);
|
|
14457
|
+
},
|
|
14458
|
+
[applyBindingsFromScene]
|
|
14459
|
+
);
|
|
14460
|
+
const handleViewerLoadError = (0, import_react38.useCallback)((error) => {
|
|
14461
|
+
const pending = pendingLoadRef.current;
|
|
14462
|
+
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14463
|
+
pendingLoadRef.current = null;
|
|
14464
|
+
console.error(error);
|
|
14465
|
+
setStatusMessage(
|
|
14466
|
+
`Could not read ${pending.modelName}. Make sure the file is a valid GLB/GLTF asset.`
|
|
14467
|
+
);
|
|
14468
|
+
setStatusTone("error");
|
|
14469
|
+
setLoading(false);
|
|
14470
|
+
}, []);
|
|
14471
|
+
const handleAnimationsReady = (0, import_react38.useCallback)(
|
|
14472
|
+
(controls) => {
|
|
14473
|
+
viewerAnimations.handleAnimationsReady(controls);
|
|
14474
|
+
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
14362
14475
|
setSceneConfig((prev) => ({
|
|
14363
14476
|
...prev,
|
|
14364
|
-
animations: mergeAnimationConfig(prev,
|
|
14365
|
-
annotations: []
|
|
14477
|
+
animations: mergeAnimationConfig(prev, details)
|
|
14366
14478
|
}));
|
|
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
|
-
};
|
|
14479
|
+
},
|
|
14480
|
+
[viewerAnimations, setSceneConfig]
|
|
14481
|
+
);
|
|
14382
14482
|
const handleLoadDemo = async () => {
|
|
14383
14483
|
uploadedModelRef.current?.cleanup();
|
|
14384
14484
|
uploadedModelRef.current = null;
|
|
@@ -14408,6 +14508,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14408
14508
|
const clearModel = () => {
|
|
14409
14509
|
uploadedModelRef.current?.cleanup();
|
|
14410
14510
|
uploadedModelRef.current = null;
|
|
14511
|
+
loadedSceneRef.current = null;
|
|
14512
|
+
pendingLoadRef.current = null;
|
|
14411
14513
|
previewDragDepthRef.current = 0;
|
|
14412
14514
|
setIsPreviewDragActive(false);
|
|
14413
14515
|
setModelUrl(null);
|
|
@@ -15206,7 +15308,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15206
15308
|
onAnnotationsChange: handleAnnotationsChange,
|
|
15207
15309
|
activeAnnotation: activeAnnotation?.annotation ?? null,
|
|
15208
15310
|
onActiveAnnotationChange: handleActiveAnnotationChange,
|
|
15209
|
-
onAnimationsReady:
|
|
15311
|
+
onAnimationsReady: handleAnimationsReady,
|
|
15312
|
+
onModelLoaded: handleViewerModelLoaded,
|
|
15313
|
+
onLoadError: handleViewerLoadError,
|
|
15210
15314
|
licenseKey,
|
|
15211
15315
|
sceneConfig,
|
|
15212
15316
|
disableZoom: false,
|
|
@@ -15290,7 +15394,7 @@ var import_drei6 = require("@react-three/drei");
|
|
|
15290
15394
|
var import_fiber12 = require("@react-three/fiber");
|
|
15291
15395
|
var import_fiber13 = require("@react-three/fiber");
|
|
15292
15396
|
var import_three21 = require("three");
|
|
15293
|
-
var
|
|
15397
|
+
var import_three_stdlib4 = require("three-stdlib");
|
|
15294
15398
|
|
|
15295
15399
|
// src/styles/SimpleModelViewer.css
|
|
15296
15400
|
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 +15463,7 @@ function InnerModel({
|
|
|
15359
15463
|
const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
|
|
15360
15464
|
const [hoveredKey, setHoveredKey] = (0, import_react39.useState)(null);
|
|
15361
15465
|
const scene = (0, import_react39.useMemo)(
|
|
15362
|
-
() =>
|
|
15466
|
+
() => import_three_stdlib4.SkeletonUtils.clone(rawScene),
|
|
15363
15467
|
[rawScene]
|
|
15364
15468
|
);
|
|
15365
15469
|
const meshMap = (0, import_react39.useMemo)(() => {
|