@liveroom-tech/react-immersive 2.2.1 → 3.0.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/README.md +193 -28
- package/dist/index.d.mts +386 -76
- package/dist/index.d.ts +386 -76
- package/dist/index.js +1769 -468
- package/dist/index.mjs +1589 -288
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -174,7 +174,7 @@ import {
|
|
|
174
174
|
Raycaster,
|
|
175
175
|
Sphere as Sphere3,
|
|
176
176
|
Vector2 as Vector26,
|
|
177
|
-
Vector3 as
|
|
177
|
+
Vector3 as Vector39
|
|
178
178
|
} from "three";
|
|
179
179
|
|
|
180
180
|
// #style-inject:#style-inject
|
|
@@ -2053,6 +2053,28 @@ function Controls({ controlsRef, onCameraChange }) {
|
|
|
2053
2053
|
// src/components/Lights.tsx
|
|
2054
2054
|
import { useRef as useRef4, useMemo as useMemo5, useEffect as useEffect6 } from "react";
|
|
2055
2055
|
import { useFrame as useFrame3, useThree as useThree5 } from "@react-three/fiber";
|
|
2056
|
+
import {
|
|
2057
|
+
Vector3 as Vector32
|
|
2058
|
+
} from "three";
|
|
2059
|
+
|
|
2060
|
+
// src/utils/resolveBindingKey.ts
|
|
2061
|
+
function resolveBindingKey(objectBindings, objectId) {
|
|
2062
|
+
if (objectBindings[objectId]) {
|
|
2063
|
+
return objectId;
|
|
2064
|
+
}
|
|
2065
|
+
const idMatch = Object.entries(objectBindings).find(
|
|
2066
|
+
([, binding]) => binding.id === objectId
|
|
2067
|
+
)?.[0];
|
|
2068
|
+
if (idMatch) {
|
|
2069
|
+
return idMatch;
|
|
2070
|
+
}
|
|
2071
|
+
const modelObjectMatch = Object.entries(objectBindings).find(
|
|
2072
|
+
([, binding]) => binding.modelObjectId === objectId
|
|
2073
|
+
)?.[0];
|
|
2074
|
+
return modelObjectMatch ?? null;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// src/components/Lights.tsx
|
|
2056
2078
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2057
2079
|
function rotatePositionY(pos, deg) {
|
|
2058
2080
|
if (!deg) return pos;
|
|
@@ -2132,21 +2154,42 @@ function LightInstance({
|
|
|
2132
2154
|
shadows,
|
|
2133
2155
|
shadowsEnabled,
|
|
2134
2156
|
environmentRotation,
|
|
2135
|
-
modelBounds
|
|
2157
|
+
modelBounds,
|
|
2158
|
+
objectBindings,
|
|
2159
|
+
nodeRefs
|
|
2136
2160
|
}) {
|
|
2137
2161
|
const dirRef = useRef4(null);
|
|
2138
2162
|
const pointRef = useRef4(null);
|
|
2139
2163
|
const spotRef = useRef4(null);
|
|
2140
2164
|
const camera = useThree5((s) => s.camera);
|
|
2165
|
+
const rootScene = useThree5((s) => s.scene);
|
|
2166
|
+
const attachedPosition = useMemo5(() => new Vector32(), []);
|
|
2141
2167
|
const position = useMemo5(
|
|
2142
|
-
() => light.attachedToCamera ? light.position : rotatePositionY(light.position, environmentRotation),
|
|
2143
|
-
[
|
|
2168
|
+
() => light.attachedToCamera || light.attachment ? light.position : rotatePositionY(light.position, environmentRotation),
|
|
2169
|
+
[
|
|
2170
|
+
light.position,
|
|
2171
|
+
light.attachedToCamera,
|
|
2172
|
+
light.attachment,
|
|
2173
|
+
environmentRotation
|
|
2174
|
+
]
|
|
2144
2175
|
);
|
|
2145
2176
|
useFrame3(() => {
|
|
2146
|
-
|
|
2147
|
-
if (
|
|
2148
|
-
if (
|
|
2149
|
-
|
|
2177
|
+
const instance = dirRef.current ?? pointRef.current ?? spotRef.current;
|
|
2178
|
+
if (!instance) return;
|
|
2179
|
+
if (light.attachedToCamera) {
|
|
2180
|
+
instance.position.copy(camera.position);
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
const attachment = light.attachment;
|
|
2184
|
+
if (!attachment) return;
|
|
2185
|
+
const bindingKey = objectBindings ? resolveBindingKey(objectBindings, attachment.objectId) : null;
|
|
2186
|
+
const binding = bindingKey ? objectBindings?.[bindingKey] : void 0;
|
|
2187
|
+
const modelObjectId = binding?.modelObjectId ?? attachment.objectId;
|
|
2188
|
+
const target = (bindingKey ? nodeRefs?.current[bindingKey] : void 0) ?? rootScene.getObjectByName(modelObjectId);
|
|
2189
|
+
if (!target) return;
|
|
2190
|
+
attachedPosition.fromArray(attachment.offset);
|
|
2191
|
+
target.localToWorld(attachedPosition);
|
|
2192
|
+
instance.position.copy(attachedPosition);
|
|
2150
2193
|
});
|
|
2151
2194
|
const castShadow = shadowsEnabled && shadows.enabled && light.castShadow;
|
|
2152
2195
|
const fit = useMemo5(
|
|
@@ -2192,6 +2235,8 @@ function LightInstance({
|
|
|
2192
2235
|
color: light.color,
|
|
2193
2236
|
intensity: light.intensity,
|
|
2194
2237
|
position,
|
|
2238
|
+
distance: light.distance,
|
|
2239
|
+
decay: light.decay,
|
|
2195
2240
|
castShadow,
|
|
2196
2241
|
"shadow-mapSize-width": shadows.mapSize,
|
|
2197
2242
|
"shadow-mapSize-height": shadows.mapSize,
|
|
@@ -2217,7 +2262,9 @@ function Lights({
|
|
|
2217
2262
|
shadowsEnabled = true,
|
|
2218
2263
|
environmentRotation = 0,
|
|
2219
2264
|
environmentConfig,
|
|
2220
|
-
modelBounds
|
|
2265
|
+
modelBounds,
|
|
2266
|
+
objectBindings,
|
|
2267
|
+
nodeRefs
|
|
2221
2268
|
}) {
|
|
2222
2269
|
const lighting = lightingConfig ?? DEFAULT_LIGHTING;
|
|
2223
2270
|
const shadows = shadowConfig ?? DEFAULT_SHADOWS;
|
|
@@ -2238,7 +2285,9 @@ function Lights({
|
|
|
2238
2285
|
shadows,
|
|
2239
2286
|
shadowsEnabled,
|
|
2240
2287
|
environmentRotation,
|
|
2241
|
-
modelBounds
|
|
2288
|
+
modelBounds,
|
|
2289
|
+
objectBindings,
|
|
2290
|
+
nodeRefs
|
|
2242
2291
|
},
|
|
2243
2292
|
light.id
|
|
2244
2293
|
)),
|
|
@@ -2259,7 +2308,9 @@ function Lights({
|
|
|
2259
2308
|
shadows,
|
|
2260
2309
|
shadowsEnabled,
|
|
2261
2310
|
environmentRotation: 0,
|
|
2262
|
-
modelBounds
|
|
2311
|
+
modelBounds,
|
|
2312
|
+
objectBindings,
|
|
2313
|
+
nodeRefs
|
|
2263
2314
|
}
|
|
2264
2315
|
)
|
|
2265
2316
|
] });
|
|
@@ -2368,7 +2419,7 @@ import {
|
|
|
2368
2419
|
LoopOnce,
|
|
2369
2420
|
LoopRepeat,
|
|
2370
2421
|
Box3 as Box32,
|
|
2371
|
-
Vector3 as
|
|
2422
|
+
Vector3 as Vector33,
|
|
2372
2423
|
Matrix4,
|
|
2373
2424
|
MathUtils as MathUtils2
|
|
2374
2425
|
} from "three";
|
|
@@ -2984,6 +3035,24 @@ function refreshSkinnedBounds(root) {
|
|
|
2984
3035
|
|
|
2985
3036
|
// src/components/Model.tsx
|
|
2986
3037
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
3038
|
+
function applyBindingTransform(node, transform, original) {
|
|
3039
|
+
if (!original) return;
|
|
3040
|
+
if (transform?.position) node.position.fromArray(transform.position);
|
|
3041
|
+
else node.position.copy(original.position);
|
|
3042
|
+
if (transform?.rotation) {
|
|
3043
|
+
node.rotation.set(
|
|
3044
|
+
transform.rotation[0],
|
|
3045
|
+
transform.rotation[1],
|
|
3046
|
+
transform.rotation[2],
|
|
3047
|
+
original.rotation.order
|
|
3048
|
+
);
|
|
3049
|
+
} else {
|
|
3050
|
+
node.rotation.copy(original.rotation);
|
|
3051
|
+
}
|
|
3052
|
+
if (transform?.scale) node.scale.fromArray(transform.scale);
|
|
3053
|
+
else node.scale.copy(original.scale);
|
|
3054
|
+
node.updateMatrix();
|
|
3055
|
+
}
|
|
2987
3056
|
function firstMaterial(node) {
|
|
2988
3057
|
const mat = node.material;
|
|
2989
3058
|
if (!mat) return void 0;
|
|
@@ -3037,6 +3106,17 @@ function Model({
|
|
|
3037
3106
|
});
|
|
3038
3107
|
return map;
|
|
3039
3108
|
}, [scene]);
|
|
3109
|
+
const originalNodeTransforms = useMemo7(() => {
|
|
3110
|
+
const transforms = /* @__PURE__ */ new WeakMap();
|
|
3111
|
+
scene.traverse((node) => {
|
|
3112
|
+
transforms.set(node, {
|
|
3113
|
+
position: node.position.clone(),
|
|
3114
|
+
rotation: node.rotation.clone(),
|
|
3115
|
+
scale: node.scale.clone()
|
|
3116
|
+
});
|
|
3117
|
+
});
|
|
3118
|
+
return transforms;
|
|
3119
|
+
}, [scene]);
|
|
3040
3120
|
const shellNodeNames = useMemo7(() => {
|
|
3041
3121
|
const shell = /* @__PURE__ */ new Set();
|
|
3042
3122
|
const sceneBox = new Box32();
|
|
@@ -3048,8 +3128,8 @@ function Model({
|
|
|
3048
3128
|
meshNodes.push({ name, box });
|
|
3049
3129
|
sceneBox.union(box);
|
|
3050
3130
|
}
|
|
3051
|
-
const sceneSize = sceneBox.getSize(new
|
|
3052
|
-
const size = new
|
|
3131
|
+
const sceneSize = sceneBox.getSize(new Vector33());
|
|
3132
|
+
const size = new Vector33();
|
|
3053
3133
|
for (const { name, box } of meshNodes) {
|
|
3054
3134
|
box.getSize(size);
|
|
3055
3135
|
const isShell = size.x > sceneSize.x * 0.7 && size.z > sceneSize.z * 0.7 && size.y > sceneSize.y * 0.4;
|
|
@@ -3089,6 +3169,43 @@ function Model({
|
|
|
3089
3169
|
).sort().join(""),
|
|
3090
3170
|
[objectBindings]
|
|
3091
3171
|
);
|
|
3172
|
+
const bindingTransformSignature = useMemo7(
|
|
3173
|
+
() => Object.entries(objectBindings).map(
|
|
3174
|
+
([nodeName, binding]) => JSON.stringify([
|
|
3175
|
+
nodeName,
|
|
3176
|
+
binding.modelObjectId ?? nodeName,
|
|
3177
|
+
binding.transform ?? null
|
|
3178
|
+
])
|
|
3179
|
+
).sort().join(""),
|
|
3180
|
+
[objectBindings]
|
|
3181
|
+
);
|
|
3182
|
+
const transformedNodesRef = useRef5(/* @__PURE__ */ new Set());
|
|
3183
|
+
useEffect7(() => {
|
|
3184
|
+
for (const node of transformedNodesRef.current) {
|
|
3185
|
+
applyBindingTransform(node, void 0, originalNodeTransforms.get(node));
|
|
3186
|
+
}
|
|
3187
|
+
const nextTransformedNodes = /* @__PURE__ */ new Set();
|
|
3188
|
+
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3189
|
+
if (!binding.transform) continue;
|
|
3190
|
+
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3191
|
+
if (!node) continue;
|
|
3192
|
+
applyBindingTransform(
|
|
3193
|
+
node,
|
|
3194
|
+
binding.transform,
|
|
3195
|
+
originalNodeTransforms.get(node)
|
|
3196
|
+
);
|
|
3197
|
+
nextTransformedNodes.add(node);
|
|
3198
|
+
}
|
|
3199
|
+
transformedNodesRef.current = nextTransformedNodes;
|
|
3200
|
+
scene.updateMatrixWorld(true);
|
|
3201
|
+
invalidate();
|
|
3202
|
+
}, [
|
|
3203
|
+
scene,
|
|
3204
|
+
nodeMap,
|
|
3205
|
+
originalNodeTransforms,
|
|
3206
|
+
bindingTransformSignature,
|
|
3207
|
+
invalidate
|
|
3208
|
+
]);
|
|
3092
3209
|
const perNodeMaterials = useMemo7(() => {
|
|
3093
3210
|
const map = {};
|
|
3094
3211
|
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
@@ -3592,27 +3709,30 @@ function Model({
|
|
|
3592
3709
|
const explodePartsRef = useRef5([]);
|
|
3593
3710
|
const explodeStrengthRef = useRef5(0);
|
|
3594
3711
|
const currentExplodeRef = useRef5(0);
|
|
3595
|
-
const explodeTmp = useMemo7(() => new
|
|
3712
|
+
const explodeTmp = useMemo7(() => new Vector33(), []);
|
|
3596
3713
|
useEffect7(() => {
|
|
3714
|
+
const bindingByNode = /* @__PURE__ */ new Map();
|
|
3715
|
+
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3716
|
+
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3717
|
+
if (node) bindingByNode.set(node, binding);
|
|
3718
|
+
}
|
|
3597
3719
|
for (const part of explodePartsRef.current) {
|
|
3598
|
-
|
|
3720
|
+
applyBindingTransform(
|
|
3721
|
+
part.node,
|
|
3722
|
+
bindingByNode.get(part.node)?.transform,
|
|
3723
|
+
originalNodeTransforms.get(part.node)
|
|
3724
|
+
);
|
|
3599
3725
|
}
|
|
3600
3726
|
currentExplodeRef.current = 0;
|
|
3601
3727
|
scene.updateWorldMatrix(true, true);
|
|
3602
3728
|
const sceneBox = new Box32().setFromObject(scene);
|
|
3603
|
-
const modelCenter = sceneBox.getCenter(new
|
|
3604
|
-
const radius = sceneBox.getSize(new
|
|
3729
|
+
const modelCenter = sceneBox.getCenter(new Vector33());
|
|
3730
|
+
const radius = sceneBox.getSize(new Vector33()).length() * 0.5;
|
|
3605
3731
|
explodeStrengthRef.current = Number.isFinite(radius) ? radius : 0;
|
|
3606
|
-
const targets = [];
|
|
3607
|
-
const targetSet =
|
|
3608
|
-
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3609
|
-
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3610
|
-
if (!node || targetSet.has(node)) continue;
|
|
3611
|
-
targetSet.add(node);
|
|
3612
|
-
targets.push(node);
|
|
3613
|
-
}
|
|
3732
|
+
const targets = [...bindingByNode.keys()];
|
|
3733
|
+
const targetSet = new Set(targets);
|
|
3614
3734
|
const parts = [];
|
|
3615
|
-
const partCenter = new
|
|
3735
|
+
const partCenter = new Vector33();
|
|
3616
3736
|
for (const node of targets) {
|
|
3617
3737
|
if (!node.parent) continue;
|
|
3618
3738
|
let ancestor = node.parent;
|
|
@@ -3634,14 +3754,21 @@ function Model({
|
|
|
3634
3754
|
parts.push({
|
|
3635
3755
|
node,
|
|
3636
3756
|
restLocalPos: node.position.clone(),
|
|
3637
|
-
restWorldPos: node.getWorldPosition(new
|
|
3757
|
+
restWorldPos: node.getWorldPosition(new Vector33()),
|
|
3638
3758
|
worldDir,
|
|
3639
3759
|
parentInverse: new Matrix4().copy(node.parent.matrixWorld).invert()
|
|
3640
3760
|
});
|
|
3641
3761
|
}
|
|
3642
3762
|
explodePartsRef.current = parts;
|
|
3643
3763
|
invalidate();
|
|
3644
|
-
}, [
|
|
3764
|
+
}, [
|
|
3765
|
+
scene,
|
|
3766
|
+
nodeMap,
|
|
3767
|
+
nodeBindingSignature,
|
|
3768
|
+
bindingTransformSignature,
|
|
3769
|
+
originalNodeTransforms,
|
|
3770
|
+
invalidate
|
|
3771
|
+
]);
|
|
3645
3772
|
useEffect7(() => {
|
|
3646
3773
|
invalidate();
|
|
3647
3774
|
}, [explodeFactor, invalidate]);
|
|
@@ -3733,7 +3860,7 @@ import {
|
|
|
3733
3860
|
} from "react";
|
|
3734
3861
|
import { Html } from "@react-three/drei";
|
|
3735
3862
|
import { useFrame as useFrame5, useThree as useThree8 } from "@react-three/fiber";
|
|
3736
|
-
import { MathUtils as MathUtils3, Vector3 as
|
|
3863
|
+
import { MathUtils as MathUtils3, Vector3 as Vector34 } from "three";
|
|
3737
3864
|
|
|
3738
3865
|
// src/styles/AnnotationsLayer.css
|
|
3739
3866
|
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");
|
|
@@ -3746,7 +3873,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3746
3873
|
const scene = useThree8((state) => state.scene);
|
|
3747
3874
|
const anchorNodeRef = useRef6(null);
|
|
3748
3875
|
const baseLocalPosition = useMemo8(
|
|
3749
|
-
() => new
|
|
3876
|
+
() => new Vector34(
|
|
3750
3877
|
...annotation.anchorLocalPosition ?? annotation.localPosition ?? annotation.worldPosition
|
|
3751
3878
|
),
|
|
3752
3879
|
[
|
|
@@ -3755,9 +3882,9 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3755
3882
|
annotation.worldPosition
|
|
3756
3883
|
]
|
|
3757
3884
|
);
|
|
3758
|
-
const worldPosition = useMemo8(() => new
|
|
3759
|
-
const biasedWorldPosition = useMemo8(() => new
|
|
3760
|
-
const toCamera = useMemo8(() => new
|
|
3885
|
+
const worldPosition = useMemo8(() => new Vector34(), []);
|
|
3886
|
+
const biasedWorldPosition = useMemo8(() => new Vector34(), []);
|
|
3887
|
+
const toCamera = useMemo8(() => new Vector34(), []);
|
|
3761
3888
|
useEffect8(() => {
|
|
3762
3889
|
anchorNodeRef.current = null;
|
|
3763
3890
|
}, [annotation.anchorNodeName]);
|
|
@@ -3780,7 +3907,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3780
3907
|
if (worldPositionsRef) {
|
|
3781
3908
|
let stored = worldPositionsRef.current.get(annotation.id);
|
|
3782
3909
|
if (!stored) {
|
|
3783
|
-
stored = new
|
|
3910
|
+
stored = new Vector34();
|
|
3784
3911
|
worldPositionsRef.current.set(annotation.id, stored);
|
|
3785
3912
|
}
|
|
3786
3913
|
stored.copy(worldPosition);
|
|
@@ -4178,7 +4305,7 @@ function AnnotationsLayer({
|
|
|
4178
4305
|
// src/components/ModelViewer/MeasurementLayer.tsx
|
|
4179
4306
|
import { useEffect as useEffect9, useMemo as useMemo9 } from "react";
|
|
4180
4307
|
import { Html as Html2, Line } from "@react-three/drei";
|
|
4181
|
-
import { BoxGeometry, EdgesGeometry, Vector3 as
|
|
4308
|
+
import { BoxGeometry, EdgesGeometry, Vector3 as Vector35 } from "three";
|
|
4182
4309
|
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4183
4310
|
var LINE_COLOR = "#22d3ee";
|
|
4184
4311
|
var BOX_COLOR = "#facc15";
|
|
@@ -4253,7 +4380,7 @@ function MeasurementLayer({
|
|
|
4253
4380
|
points.slice(1).map((point, index) => {
|
|
4254
4381
|
const a = points[index].position;
|
|
4255
4382
|
const b = point.position;
|
|
4256
|
-
const distance = new
|
|
4383
|
+
const distance = new Vector35(...a).distanceTo(new Vector35(...b));
|
|
4257
4384
|
return /* @__PURE__ */ jsxs6("group", { children: [
|
|
4258
4385
|
/* @__PURE__ */ jsx10(Line, { points: [a, b], color: LINE_COLOR, lineWidth: 2 }),
|
|
4259
4386
|
/* @__PURE__ */ jsx10(MeasureLabel, { position: midpoint(a, b), children: formatMeasurement(distance, unit) })
|
|
@@ -4274,7 +4401,7 @@ import {
|
|
|
4274
4401
|
} from "react";
|
|
4275
4402
|
import { useXR as useXR2, useXRHitTest, useXRInputSourceEvent } from "@react-three/xr";
|
|
4276
4403
|
import { useFrame as useFrame6 } from "@react-three/fiber";
|
|
4277
|
-
import { Matrix4 as Matrix42, Vector3 as
|
|
4404
|
+
import { Matrix4 as Matrix42, Vector3 as Vector36 } from "three";
|
|
4278
4405
|
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4279
4406
|
var TARGET_SIZE = 1;
|
|
4280
4407
|
var FORWARD_DISTANCE = 1;
|
|
@@ -4311,13 +4438,13 @@ var XRModelPresenter = forwardRef2(
|
|
|
4311
4438
|
() => box ? computeNormalization(box, scaleMode) : null,
|
|
4312
4439
|
[box, scaleMode]
|
|
4313
4440
|
);
|
|
4314
|
-
const placedPosition = useRef7(new
|
|
4441
|
+
const placedPosition = useRef7(new Vector36(0, 0, -FORWARD_DISTANCE));
|
|
4315
4442
|
const userScale = useRef7(1);
|
|
4316
4443
|
const rotationY = useRef7(0);
|
|
4317
4444
|
const placementMode = useRef7(true);
|
|
4318
4445
|
const modelPlaced = useRef7(false);
|
|
4319
4446
|
const hitMatrix = useMemo10(() => new Matrix42(), []);
|
|
4320
|
-
const hitPosition = useRef7(new
|
|
4447
|
+
const hitPosition = useRef7(new Vector36());
|
|
4321
4448
|
const hasHit = useRef7(false);
|
|
4322
4449
|
const lastARState = useRef7(null);
|
|
4323
4450
|
const gesturing = useRef7(false);
|
|
@@ -4515,7 +4642,7 @@ import {
|
|
|
4515
4642
|
CatmullRomCurve3,
|
|
4516
4643
|
Sphere,
|
|
4517
4644
|
Spherical,
|
|
4518
|
-
Vector3 as
|
|
4645
|
+
Vector3 as Vector37
|
|
4519
4646
|
} from "three";
|
|
4520
4647
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
4521
4648
|
function CinematicCamera({
|
|
@@ -4596,13 +4723,13 @@ function CinematicCamera({
|
|
|
4596
4723
|
if (!hasPath) return null;
|
|
4597
4724
|
return {
|
|
4598
4725
|
position: new CatmullRomCurve3(
|
|
4599
|
-
waypoints.map((w) => new
|
|
4726
|
+
waypoints.map((w) => new Vector37(...w.position)),
|
|
4600
4727
|
loop,
|
|
4601
4728
|
"catmullrom",
|
|
4602
4729
|
0.5
|
|
4603
4730
|
),
|
|
4604
4731
|
target: new CatmullRomCurve3(
|
|
4605
|
-
waypoints.map((w) => new
|
|
4732
|
+
waypoints.map((w) => new Vector37(...w.target)),
|
|
4606
4733
|
loop,
|
|
4607
4734
|
"catmullrom",
|
|
4608
4735
|
0.5
|
|
@@ -4618,8 +4745,8 @@ function CinematicCamera({
|
|
|
4618
4745
|
const points = waypoints.map((waypoint) => ({
|
|
4619
4746
|
azimuth: waypoint.azimuthAngle,
|
|
4620
4747
|
polar: waypoint.polarAngle,
|
|
4621
|
-
distance: new
|
|
4622
|
-
new
|
|
4748
|
+
distance: new Vector37(...waypoint.position).distanceTo(
|
|
4749
|
+
new Vector37(...waypoint.target)
|
|
4623
4750
|
)
|
|
4624
4751
|
}));
|
|
4625
4752
|
for (let index = 1; index < points.length; index += 1) {
|
|
@@ -4631,14 +4758,14 @@ function CinematicCamera({
|
|
|
4631
4758
|
}, [signature, hasPath]);
|
|
4632
4759
|
const elapsedRef = useRef8(0);
|
|
4633
4760
|
const enterElapsedRef = useRef8(0);
|
|
4634
|
-
const enterPos = useRef8(new
|
|
4635
|
-
const enterTarget = useRef8(new
|
|
4761
|
+
const enterPos = useRef8(new Vector37());
|
|
4762
|
+
const enterTarget = useRef8(new Vector37());
|
|
4636
4763
|
const startedRef = useRef8(false);
|
|
4637
4764
|
const pausedElapsedRef = useRef8(0);
|
|
4638
4765
|
const hasFinishedRef = useRef8(false);
|
|
4639
4766
|
const orbitRef = useRef8(null);
|
|
4640
|
-
const tmpPos = useMemo11(() => new
|
|
4641
|
-
const tmpTarget = useMemo11(() => new
|
|
4767
|
+
const tmpPos = useMemo11(() => new Vector37(), []);
|
|
4768
|
+
const tmpTarget = useMemo11(() => new Vector37(), []);
|
|
4642
4769
|
const tmpSpherical = useMemo11(() => new Spherical(), []);
|
|
4643
4770
|
useEffect11(() => {
|
|
4644
4771
|
elapsedRef.current = 0;
|
|
@@ -5877,7 +6004,7 @@ import { useRef as useRef11, useEffect as useEffect15, useMemo as useMemo13, use
|
|
|
5877
6004
|
import { useThree as useThree10, useFrame as useFrame8 } from "@react-three/fiber";
|
|
5878
6005
|
import { Sphere as Sphere2, Line as Line2 } from "@react-three/drei";
|
|
5879
6006
|
import {
|
|
5880
|
-
Vector3 as
|
|
6007
|
+
Vector3 as Vector38,
|
|
5881
6008
|
Vector2 as Vector25,
|
|
5882
6009
|
AdditiveBlending as AdditiveBlending2,
|
|
5883
6010
|
Plane,
|
|
@@ -5905,7 +6032,7 @@ var RING_SCREEN_SIZE = SUN_SCREEN_SIZE * 3;
|
|
|
5905
6032
|
var RING_COLOR = "#4d7dff";
|
|
5906
6033
|
var LINE_COLOR2 = "#ffcc00";
|
|
5907
6034
|
var LINE_GLOW_RADIUS = 0.02;
|
|
5908
|
-
var UP_AXIS = new
|
|
6035
|
+
var UP_AXIS = new Vector38(0, 1, 0);
|
|
5909
6036
|
var AXIS_ARROW_LENGTH = 1.4;
|
|
5910
6037
|
var AXIS_CONE_LENGTH = 0.35;
|
|
5911
6038
|
var AXIS_CONE_RADIUS = 0.13;
|
|
@@ -5917,17 +6044,17 @@ var AXIS_COLORS = {
|
|
|
5917
6044
|
z: "#4444ff"
|
|
5918
6045
|
};
|
|
5919
6046
|
function axisVector(axis) {
|
|
5920
|
-
return new
|
|
6047
|
+
return new Vector38(axis === "x" ? 1 : 0, axis === "y" ? 1 : 0, axis === "z" ? 1 : 0);
|
|
5921
6048
|
}
|
|
5922
6049
|
function computeAxisDragPlaneNormal(axis, eye) {
|
|
5923
6050
|
const axisVec = axisVector(axis);
|
|
5924
6051
|
if (eye.lengthSq() < 1e-8) {
|
|
5925
6052
|
eye = axisVec.clone();
|
|
5926
6053
|
}
|
|
5927
|
-
let normal = new
|
|
6054
|
+
let normal = new Vector38().crossVectors(axisVec, new Vector38().crossVectors(eye, axisVec));
|
|
5928
6055
|
if (normal.lengthSq() < 1e-8) {
|
|
5929
|
-
const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new
|
|
5930
|
-
normal = new
|
|
6056
|
+
const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new Vector38(1, 0, 0);
|
|
6057
|
+
normal = new Vector38().crossVectors(axisVec, helper);
|
|
5931
6058
|
}
|
|
5932
6059
|
return normal.normalize();
|
|
5933
6060
|
}
|
|
@@ -6000,17 +6127,17 @@ function LightHelpers({
|
|
|
6000
6127
|
const shaftZRef = useRef11(null);
|
|
6001
6128
|
const axisDragRef = useRef11({ dragging: false, axis: null });
|
|
6002
6129
|
const axisDragPlaneRef = useRef11(new Plane());
|
|
6003
|
-
const dragStartPosRef = useRef11(new
|
|
6130
|
+
const dragStartPosRef = useRef11(new Vector38());
|
|
6004
6131
|
const dragLineRef = useRef11(null);
|
|
6005
6132
|
const dragLineGeomRef = useRef11(null);
|
|
6006
|
-
const currentPosRef = useRef11(new
|
|
6133
|
+
const currentPosRef = useRef11(new Vector38());
|
|
6007
6134
|
const isDraggingRef = useRef11(false);
|
|
6008
6135
|
const dragPlaneRef = useRef11(new Plane());
|
|
6009
6136
|
const onDraggingChangeRef = useRef11(onDraggingChange);
|
|
6010
6137
|
useEffect15(() => {
|
|
6011
6138
|
onDraggingChangeRef.current = onDraggingChange;
|
|
6012
6139
|
}, [onDraggingChange]);
|
|
6013
|
-
const lightPos = useMemo13(() => new
|
|
6140
|
+
const lightPos = useMemo13(() => new Vector38(position[0], position[1], position[2]), [position]);
|
|
6014
6141
|
const linePositions = useMemo13(() => new Float32Array(6), []);
|
|
6015
6142
|
const dragLinePositions = useMemo13(() => new Float32Array(6), []);
|
|
6016
6143
|
const ringXPositions = useMemo13(() => makeRingPositions("x"), []);
|
|
@@ -6169,12 +6296,12 @@ function LightHelpers({
|
|
|
6169
6296
|
const axis = axisDragRef.current.axis;
|
|
6170
6297
|
const currentPos = currentPosRef.current;
|
|
6171
6298
|
const axisVec = axisVector(axis);
|
|
6172
|
-
const eye = new
|
|
6299
|
+
const eye = new Vector38().subVectors(camera.position, currentPos);
|
|
6173
6300
|
const planeNormal = computeAxisDragPlaneNormal(axis, eye);
|
|
6174
6301
|
axisDragPlaneRef.current.setFromNormalAndCoplanarPoint(planeNormal, currentPos);
|
|
6175
6302
|
const mouse2 = getMouseNdc(event);
|
|
6176
6303
|
raycaster.setFromCamera(mouse2, camera);
|
|
6177
|
-
const intersection2 = new
|
|
6304
|
+
const intersection2 = new Vector38();
|
|
6178
6305
|
const hitPoint2 = raycaster.ray.intersectPlane(axisDragPlaneRef.current, intersection2);
|
|
6179
6306
|
if (!hitPoint2) return;
|
|
6180
6307
|
const toPoint = hitPoint2.clone().sub(currentPos);
|
|
@@ -6193,7 +6320,7 @@ function LightHelpers({
|
|
|
6193
6320
|
if (!isDraggingRef.current) return;
|
|
6194
6321
|
const mouse = getMouseNdc(event);
|
|
6195
6322
|
raycaster.setFromCamera(mouse, camera);
|
|
6196
|
-
const intersection = new
|
|
6323
|
+
const intersection = new Vector38();
|
|
6197
6324
|
const hitPoint = raycaster.ray.intersectPlane(dragPlaneRef.current, intersection);
|
|
6198
6325
|
if (!hitPoint) return;
|
|
6199
6326
|
const newPos = [
|
|
@@ -6240,7 +6367,7 @@ function LightHelpers({
|
|
|
6240
6367
|
setActiveTab?.("scene");
|
|
6241
6368
|
setActiveSubTab?.("Lighting");
|
|
6242
6369
|
}
|
|
6243
|
-
const cameraDir = new
|
|
6370
|
+
const cameraDir = new Vector38();
|
|
6244
6371
|
camera.getWorldDirection(cameraDir);
|
|
6245
6372
|
dragPlaneRef.current.setFromNormalAndCoplanarPoint(cameraDir, currentPosRef.current);
|
|
6246
6373
|
isDraggingRef.current = true;
|
|
@@ -6373,17 +6500,17 @@ function LightHelpers({
|
|
|
6373
6500
|
] }),
|
|
6374
6501
|
gizmoVisible && /* @__PURE__ */ jsx19("group", { ref: axisGroupRef, children: axisArrows.map((axis) => {
|
|
6375
6502
|
const axisColor = AXIS_COLORS[axis];
|
|
6376
|
-
const end = new
|
|
6503
|
+
const end = new Vector38(
|
|
6377
6504
|
axis === "x" ? AXIS_ARROW_LENGTH : 0,
|
|
6378
6505
|
axis === "y" ? AXIS_ARROW_LENGTH : 0,
|
|
6379
6506
|
axis === "z" ? AXIS_ARROW_LENGTH : 0
|
|
6380
6507
|
);
|
|
6381
|
-
const conePos = new
|
|
6508
|
+
const conePos = new Vector38(
|
|
6382
6509
|
axis === "x" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
|
|
6383
6510
|
axis === "y" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
|
|
6384
6511
|
axis === "z" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0
|
|
6385
6512
|
);
|
|
6386
|
-
const shaftPos = new
|
|
6513
|
+
const shaftPos = new Vector38(
|
|
6387
6514
|
axis === "x" ? AXIS_ARROW_LENGTH / 2 : 0,
|
|
6388
6515
|
axis === "y" ? AXIS_ARROW_LENGTH / 2 : 0,
|
|
6389
6516
|
axis === "z" ? AXIS_ARROW_LENGTH / 2 : 0
|
|
@@ -6395,7 +6522,7 @@ function LightHelpers({
|
|
|
6395
6522
|
/* @__PURE__ */ jsx19(
|
|
6396
6523
|
Line2,
|
|
6397
6524
|
{
|
|
6398
|
-
points: [new
|
|
6525
|
+
points: [new Vector38(0, 0, 0), end],
|
|
6399
6526
|
color: axisColor,
|
|
6400
6527
|
transparent: true,
|
|
6401
6528
|
opacity: 0.9,
|
|
@@ -6484,7 +6611,7 @@ function findInfluencingBone(hit) {
|
|
|
6484
6611
|
if (!skinIndex || !skinWeight || !position) return null;
|
|
6485
6612
|
const localHit = mesh.worldToLocal(hit.point.clone());
|
|
6486
6613
|
const vertices = [hit.face.a, hit.face.b, hit.face.c];
|
|
6487
|
-
const vertex = new
|
|
6614
|
+
const vertex = new Vector39();
|
|
6488
6615
|
let nearest = vertices[0];
|
|
6489
6616
|
let nearestDist = Infinity;
|
|
6490
6617
|
for (const vi of vertices) {
|
|
@@ -6534,9 +6661,9 @@ function GuidedTourCameraFollow({
|
|
|
6534
6661
|
const scene = useThree11((state) => state.scene);
|
|
6535
6662
|
const nodeRef = useRef12(null);
|
|
6536
6663
|
const hasPrev = useRef12(false);
|
|
6537
|
-
const live = useMemo14(() => new
|
|
6538
|
-
const prev = useMemo14(() => new
|
|
6539
|
-
const goal = useMemo14(() => new
|
|
6664
|
+
const live = useMemo14(() => new Vector39(), []);
|
|
6665
|
+
const prev = useMemo14(() => new Vector39(), []);
|
|
6666
|
+
const goal = useMemo14(() => new Vector39(), []);
|
|
6540
6667
|
const nodeName = annotation?.anchorNodeName;
|
|
6541
6668
|
const localPosition = annotation?.anchorLocalPosition;
|
|
6542
6669
|
useEffect16(() => {
|
|
@@ -6576,7 +6703,7 @@ var DEFAULT_AR_OVERLAY_STATE = {
|
|
|
6576
6703
|
};
|
|
6577
6704
|
function computeMeasureBox(scene) {
|
|
6578
6705
|
const box = new Box34().setFromObject(scene);
|
|
6579
|
-
const size = new
|
|
6706
|
+
const size = new Vector39();
|
|
6580
6707
|
box.getSize(size);
|
|
6581
6708
|
return {
|
|
6582
6709
|
min: [box.min.x, box.min.y, box.min.z],
|
|
@@ -6597,8 +6724,8 @@ function computeUvMeshStats(scene) {
|
|
|
6597
6724
|
function computeModelBounds(scene) {
|
|
6598
6725
|
const box = new Box34().setFromObject(scene);
|
|
6599
6726
|
if (box.isEmpty()) return null;
|
|
6600
|
-
const center = box.getCenter(new
|
|
6601
|
-
const radius = box.getSize(new
|
|
6727
|
+
const center = box.getCenter(new Vector39());
|
|
6728
|
+
const radius = box.getSize(new Vector39()).length() / 2;
|
|
6602
6729
|
if (!Number.isFinite(radius) || radius <= 0) return null;
|
|
6603
6730
|
return { center: [center.x, center.y, center.z], radius };
|
|
6604
6731
|
}
|
|
@@ -6606,7 +6733,7 @@ function fitClippingPlanesToScene(scene, controls) {
|
|
|
6606
6733
|
const camera = controls?.camera;
|
|
6607
6734
|
if (!(camera instanceof PerspectiveCamera3)) return;
|
|
6608
6735
|
const box = new Box34().setFromObject(scene);
|
|
6609
|
-
const size = box.getSize(new
|
|
6736
|
+
const size = box.getSize(new Vector39()).length();
|
|
6610
6737
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
6611
6738
|
camera.near = size / 100;
|
|
6612
6739
|
camera.far = size * 100;
|
|
@@ -6791,8 +6918,8 @@ function ModelViewer({
|
|
|
6791
6918
|
if (!controls || initialCameraStateRef.current) {
|
|
6792
6919
|
return;
|
|
6793
6920
|
}
|
|
6794
|
-
const position = controls.getPosition(new
|
|
6795
|
-
const target = controls.getTarget(new
|
|
6921
|
+
const position = controls.getPosition(new Vector39());
|
|
6922
|
+
const target = controls.getTarget(new Vector39());
|
|
6796
6923
|
initialCameraStateRef.current = {
|
|
6797
6924
|
position: [position.x, position.y, position.z],
|
|
6798
6925
|
target: [target.x, target.y, target.z]
|
|
@@ -7050,7 +7177,7 @@ function ModelViewer({
|
|
|
7050
7177
|
const controls = controlsRef.current;
|
|
7051
7178
|
const scene = sceneRef.current;
|
|
7052
7179
|
if (!controls) return;
|
|
7053
|
-
const target = new
|
|
7180
|
+
const target = new Vector39();
|
|
7054
7181
|
const livePosition = annotationWorldPositionsRef.current.get(
|
|
7055
7182
|
annotation.id
|
|
7056
7183
|
);
|
|
@@ -7069,14 +7196,14 @@ function ModelViewer({
|
|
|
7069
7196
|
const storedNormal = annotation.worldNormal;
|
|
7070
7197
|
if (scene) {
|
|
7071
7198
|
const box = new Box34().setFromObject(scene);
|
|
7072
|
-
const modelCenter = new
|
|
7199
|
+
const modelCenter = new Vector39();
|
|
7073
7200
|
box.getCenter(modelCenter);
|
|
7074
|
-
const modelSize = new
|
|
7201
|
+
const modelSize = new Vector39();
|
|
7075
7202
|
box.getSize(modelSize);
|
|
7076
7203
|
const modelRadius = modelSize.length() * 0.2;
|
|
7077
7204
|
let outwardDir;
|
|
7078
7205
|
if (storedNormal) {
|
|
7079
|
-
outwardDir = new
|
|
7206
|
+
outwardDir = new Vector39(
|
|
7080
7207
|
storedNormal[0],
|
|
7081
7208
|
storedNormal[1],
|
|
7082
7209
|
storedNormal[2]
|
|
@@ -7227,6 +7354,16 @@ function ModelViewer({
|
|
|
7227
7354
|
const isControlledBindings = onObjectBindingsChange !== void 0;
|
|
7228
7355
|
const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
|
|
7229
7356
|
currentObjectBindingsRef.current = currentObjectBindings;
|
|
7357
|
+
const currentBindingTransformSignature = useMemo14(
|
|
7358
|
+
() => Object.entries(currentObjectBindings).map(
|
|
7359
|
+
([bindingKey, binding]) => JSON.stringify([
|
|
7360
|
+
bindingKey,
|
|
7361
|
+
binding.modelObjectId,
|
|
7362
|
+
binding.transform ?? null
|
|
7363
|
+
])
|
|
7364
|
+
).sort().join(""),
|
|
7365
|
+
[currentObjectBindings]
|
|
7366
|
+
);
|
|
7230
7367
|
const currentHiddenObjects = useMemo14(
|
|
7231
7368
|
() => Object.fromEntries(
|
|
7232
7369
|
Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
@@ -7366,7 +7503,7 @@ function ModelViewer({
|
|
|
7366
7503
|
const fallbackY = Math.max(minOffset, (rect.height - height) / 2);
|
|
7367
7504
|
const objectKey = resolveObjectKey(objectId, binding);
|
|
7368
7505
|
const object = objectKey ? nodeRefsMap.current[objectKey] : null;
|
|
7369
|
-
const
|
|
7506
|
+
const clamp3 = (value, max) => {
|
|
7370
7507
|
if (max <= minOffset) {
|
|
7371
7508
|
return minOffset;
|
|
7372
7509
|
}
|
|
@@ -7374,23 +7511,23 @@ function ModelViewer({
|
|
|
7374
7511
|
};
|
|
7375
7512
|
if (canvasContainer && cameraControls && object) {
|
|
7376
7513
|
const canvasRect = canvasContainer.getBoundingClientRect();
|
|
7377
|
-
const objectCenter = new
|
|
7514
|
+
const objectCenter = new Vector39();
|
|
7378
7515
|
new Box34().setFromObject(object).getCenter(objectCenter);
|
|
7379
7516
|
objectCenter.project(cameraControls);
|
|
7380
7517
|
const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
|
|
7381
7518
|
const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
|
|
7382
7519
|
return {
|
|
7383
7520
|
objectId,
|
|
7384
|
-
x:
|
|
7385
|
-
y:
|
|
7521
|
+
x: clamp3(projectedX + 24, maxX),
|
|
7522
|
+
y: clamp3(projectedY - height / 2, maxY)
|
|
7386
7523
|
};
|
|
7387
7524
|
}
|
|
7388
7525
|
const rawX = typeof fallbackCoords?.screenX === "number" ? fallbackCoords.screenX - rect.left : fallbackX;
|
|
7389
7526
|
const rawY = typeof fallbackCoords?.screenY === "number" ? fallbackCoords.screenY - rect.top : fallbackY;
|
|
7390
7527
|
return {
|
|
7391
7528
|
objectId,
|
|
7392
|
-
x:
|
|
7393
|
-
y:
|
|
7529
|
+
x: clamp3(rawX, maxX),
|
|
7530
|
+
y: clamp3(rawY, maxY)
|
|
7394
7531
|
};
|
|
7395
7532
|
},
|
|
7396
7533
|
[resolveObjectKey]
|
|
@@ -7399,7 +7536,6 @@ function ModelViewer({
|
|
|
7399
7536
|
(event) => {
|
|
7400
7537
|
const { objectId, action, binding } = event;
|
|
7401
7538
|
setCurrentAction(event);
|
|
7402
|
-
onAction?.(event);
|
|
7403
7539
|
switch (action.id) {
|
|
7404
7540
|
case "toggle-visibility": {
|
|
7405
7541
|
let targetKey = objectId;
|
|
@@ -7447,6 +7583,7 @@ function ModelViewer({
|
|
|
7447
7583
|
default:
|
|
7448
7584
|
break;
|
|
7449
7585
|
}
|
|
7586
|
+
onAction?.(event);
|
|
7450
7587
|
},
|
|
7451
7588
|
[
|
|
7452
7589
|
applyObjectBindings,
|
|
@@ -7587,7 +7724,8 @@ function ModelViewer({
|
|
|
7587
7724
|
objectBindings: currentObjectBindingsRef.current,
|
|
7588
7725
|
nodeRefs: nodeRefsMap.current,
|
|
7589
7726
|
homeCameraState: initialCameraStateRef.current,
|
|
7590
|
-
captureImage: (options) => captureImage(rendererCaptureRef.current, options)
|
|
7727
|
+
captureImage: (options) => captureImage(rendererCaptureRef.current, options),
|
|
7728
|
+
invalidate: () => rendererCaptureRef.current?.invalidate()
|
|
7591
7729
|
});
|
|
7592
7730
|
}, []);
|
|
7593
7731
|
const fitScene = useCallback4(
|
|
@@ -7599,8 +7737,8 @@ function ModelViewer({
|
|
|
7599
7737
|
}
|
|
7600
7738
|
try {
|
|
7601
7739
|
if (flyIn) {
|
|
7602
|
-
const target = controls.getTarget(new
|
|
7603
|
-
const position = controls.getPosition(new
|
|
7740
|
+
const target = controls.getTarget(new Vector39());
|
|
7741
|
+
const position = controls.getPosition(new Vector39());
|
|
7604
7742
|
const direction = position.clone().sub(target);
|
|
7605
7743
|
if (direction.lengthSq() < 1e-6) {
|
|
7606
7744
|
direction.set(0, 0, 1);
|
|
@@ -7918,6 +8056,13 @@ function ModelViewer({
|
|
|
7918
8056
|
);
|
|
7919
8057
|
setBindingsMismatch(!hasAnyMatch);
|
|
7920
8058
|
}, [modelLoaded, currentObjectBindings]);
|
|
8059
|
+
useEffect16(() => {
|
|
8060
|
+
const scene = sceneRef.current;
|
|
8061
|
+
if (!modelLoaded || !scene) return;
|
|
8062
|
+
scene.updateMatrixWorld(true);
|
|
8063
|
+
setMeasureBox(computeMeasureBox(scene));
|
|
8064
|
+
setModelBounds(computeModelBounds(scene));
|
|
8065
|
+
}, [modelLoaded, currentBindingTransformSignature]);
|
|
7921
8066
|
const clearMeasurements = useCallback4(() => setMeasurePoints([]), []);
|
|
7922
8067
|
const handleMeasurePointerDown = useCallback4(
|
|
7923
8068
|
(event) => {
|
|
@@ -7961,7 +8106,11 @@ function ModelViewer({
|
|
|
7961
8106
|
);
|
|
7962
8107
|
useEffect16(() => {
|
|
7963
8108
|
setMeasurePoints([]);
|
|
7964
|
-
}, [
|
|
8109
|
+
}, [
|
|
8110
|
+
modelUrl,
|
|
8111
|
+
resolvedSceneConfig.model.rotation,
|
|
8112
|
+
currentBindingTransformSignature
|
|
8113
|
+
]);
|
|
7965
8114
|
const handleDownloadMesh = useCallback4(() => {
|
|
7966
8115
|
const matRefs = materialRefsOut.current;
|
|
7967
8116
|
const scene = sceneRef.current;
|
|
@@ -8304,11 +8453,13 @@ function ModelViewer({
|
|
|
8304
8453
|
shadowsEnabled: shadows,
|
|
8305
8454
|
environmentRotation: resolvedSceneConfig.environment.rotation,
|
|
8306
8455
|
environmentConfig: resolvedSceneConfig.environment,
|
|
8307
|
-
modelBounds
|
|
8456
|
+
modelBounds,
|
|
8457
|
+
objectBindings: currentObjectBindings,
|
|
8458
|
+
nodeRefs: nodeRefsMap
|
|
8308
8459
|
}
|
|
8309
8460
|
),
|
|
8310
8461
|
showLightHelpers && resolvedSceneConfig.lighting.lights.map((light) => {
|
|
8311
|
-
if (!light.visible || light.type === "none" || light.type === "hemisphere") {
|
|
8462
|
+
if (!light.visible || light.type === "none" || light.type === "hemisphere" || light.attachment) {
|
|
8312
8463
|
return null;
|
|
8313
8464
|
}
|
|
8314
8465
|
return /* @__PURE__ */ jsx20(
|
|
@@ -8950,7 +9101,7 @@ function ModelViewer({
|
|
|
8950
9101
|
}
|
|
8951
9102
|
|
|
8952
9103
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
8953
|
-
import { useCallback as useCallback9, useEffect as
|
|
9104
|
+
import { useCallback as useCallback9, useEffect as useEffect23, useMemo as useMemo17, useRef as useRef19, useState as useState27 } from "react";
|
|
8954
9105
|
import {
|
|
8955
9106
|
Redo2,
|
|
8956
9107
|
Undo2,
|
|
@@ -8965,32 +9116,13 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
|
|
|
8965
9116
|
|
|
8966
9117
|
// src/hooks/useViewerCamera.ts
|
|
8967
9118
|
import { useCallback as useCallback5, useRef as useRef13, useState as useState11 } from "react";
|
|
8968
|
-
import { Vector3 as
|
|
8969
|
-
|
|
8970
|
-
// src/utils/resolveBindingKey.ts
|
|
8971
|
-
function resolveBindingKey(objectBindings, objectId) {
|
|
8972
|
-
if (objectBindings[objectId]) {
|
|
8973
|
-
return objectId;
|
|
8974
|
-
}
|
|
8975
|
-
const idMatch = Object.entries(objectBindings).find(
|
|
8976
|
-
([, binding]) => binding.id === objectId
|
|
8977
|
-
)?.[0];
|
|
8978
|
-
if (idMatch) {
|
|
8979
|
-
return idMatch;
|
|
8980
|
-
}
|
|
8981
|
-
const modelObjectMatch = Object.entries(objectBindings).find(
|
|
8982
|
-
([, binding]) => binding.modelObjectId === objectId
|
|
8983
|
-
)?.[0];
|
|
8984
|
-
return modelObjectMatch ?? null;
|
|
8985
|
-
}
|
|
8986
|
-
|
|
8987
|
-
// src/hooks/useViewerCamera.ts
|
|
9119
|
+
import { Vector3 as Vector310 } from "three";
|
|
8988
9120
|
function toTuple(vector) {
|
|
8989
9121
|
return [vector.x, vector.y, vector.z];
|
|
8990
9122
|
}
|
|
8991
9123
|
function buildCameraState(camera, controls) {
|
|
8992
|
-
const position = new
|
|
8993
|
-
const target = new
|
|
9124
|
+
const position = new Vector310();
|
|
9125
|
+
const target = new Vector310();
|
|
8994
9126
|
controls.getPosition(position);
|
|
8995
9127
|
controls.getTarget(target);
|
|
8996
9128
|
return {
|
|
@@ -9017,11 +9149,16 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
|
9017
9149
|
controls.smoothTime = originalSmoothTime;
|
|
9018
9150
|
}
|
|
9019
9151
|
}
|
|
9020
|
-
function useViewerCamera() {
|
|
9152
|
+
function useViewerCamera(options = {}) {
|
|
9153
|
+
const optionsRef = useRef13(options);
|
|
9154
|
+
optionsRef.current = options;
|
|
9021
9155
|
const viewerRef = useRef13(null);
|
|
9022
9156
|
const [cameraState, setCameraStateValue] = useState11(
|
|
9023
9157
|
null
|
|
9024
9158
|
);
|
|
9159
|
+
const [cameraPresets, setCameraPresets] = useState11(() => ({ ...options.presets }));
|
|
9160
|
+
const cameraPresetsRef = useRef13(cameraPresets);
|
|
9161
|
+
cameraPresetsRef.current = cameraPresets;
|
|
9025
9162
|
const updateCameraState = useCallback5((nextState) => {
|
|
9026
9163
|
setCameraStateValue(
|
|
9027
9164
|
(previousState) => isSameCameraState(previousState, nextState) ? previousState : nextState
|
|
@@ -9035,16 +9172,56 @@ function useViewerCamera() {
|
|
|
9035
9172
|
);
|
|
9036
9173
|
const homeCameraStateRef = useRef13(null);
|
|
9037
9174
|
const homeSceneRef = useRef13(null);
|
|
9175
|
+
const initializedControlsRef = useRef13(null);
|
|
9038
9176
|
const handleViewerReady = useCallback5((viewer) => {
|
|
9039
9177
|
viewerRef.current = viewer;
|
|
9040
|
-
|
|
9178
|
+
const currentState = buildCameraState(
|
|
9179
|
+
viewer.controls.camera,
|
|
9180
|
+
viewer.controls
|
|
9181
|
+
);
|
|
9182
|
+
updateCameraState(currentState);
|
|
9041
9183
|
if (viewer.scene !== homeSceneRef.current) {
|
|
9042
9184
|
homeSceneRef.current = viewer.scene;
|
|
9043
9185
|
homeCameraStateRef.current = null;
|
|
9186
|
+
initializedControlsRef.current = null;
|
|
9044
9187
|
}
|
|
9045
9188
|
if (viewer.homeCameraState && !homeCameraStateRef.current) {
|
|
9046
9189
|
homeCameraStateRef.current = viewer.homeCameraState;
|
|
9047
9190
|
}
|
|
9191
|
+
const { initialPosition, initialTarget, initialTransition = false } = optionsRef.current;
|
|
9192
|
+
if ((initialPosition || initialTarget) && viewer.scene && // A target-only setup should preserve ModelViewer's fitted or authored
|
|
9193
|
+
// camera position. Wait until that initial framing has settled instead
|
|
9194
|
+
// of cancelling auto-fit while the camera is still at its fallback.
|
|
9195
|
+
(initialPosition || viewer.homeCameraState) && initializedControlsRef.current !== viewer.controls) {
|
|
9196
|
+
initializedControlsRef.current = viewer.controls;
|
|
9197
|
+
if (!viewer.homeCameraState) {
|
|
9198
|
+
markInitialAutoFitOverride(viewer.controls);
|
|
9199
|
+
}
|
|
9200
|
+
const position = initialPosition ?? currentState.position;
|
|
9201
|
+
const target = initialTarget ?? currentState.target;
|
|
9202
|
+
void viewer.controls.setLookAt(
|
|
9203
|
+
position[0],
|
|
9204
|
+
position[1],
|
|
9205
|
+
position[2],
|
|
9206
|
+
target[0],
|
|
9207
|
+
target[1],
|
|
9208
|
+
target[2],
|
|
9209
|
+
initialTransition
|
|
9210
|
+
).then(() => {
|
|
9211
|
+
if (viewerRef.current?.controls !== viewer.controls) {
|
|
9212
|
+
return;
|
|
9213
|
+
}
|
|
9214
|
+
const nextState = buildCameraState(
|
|
9215
|
+
viewer.controls.camera,
|
|
9216
|
+
viewer.controls
|
|
9217
|
+
);
|
|
9218
|
+
homeCameraStateRef.current = {
|
|
9219
|
+
position: nextState.position,
|
|
9220
|
+
target: nextState.target
|
|
9221
|
+
};
|
|
9222
|
+
updateCameraState(nextState);
|
|
9223
|
+
}).catch(() => void 0);
|
|
9224
|
+
}
|
|
9048
9225
|
}, [updateCameraState]);
|
|
9049
9226
|
const resetView = useCallback5(async () => {
|
|
9050
9227
|
const viewer = viewerRef.current;
|
|
@@ -9140,27 +9317,33 @@ function useViewerCamera() {
|
|
|
9140
9317
|
return false;
|
|
9141
9318
|
}
|
|
9142
9319
|
try {
|
|
9320
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9321
|
+
markInitialAutoFitOverride(controls);
|
|
9322
|
+
}
|
|
9143
9323
|
await controls.setTarget(
|
|
9144
9324
|
target[0],
|
|
9145
9325
|
target[1],
|
|
9146
9326
|
target[2],
|
|
9147
9327
|
enableTransition
|
|
9148
9328
|
);
|
|
9329
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9149
9330
|
return true;
|
|
9150
9331
|
} catch {
|
|
9151
9332
|
return false;
|
|
9152
9333
|
}
|
|
9153
9334
|
},
|
|
9154
|
-
[]
|
|
9335
|
+
[updateCameraState]
|
|
9155
9336
|
);
|
|
9156
|
-
const
|
|
9337
|
+
const applyCameraPreset = useCallback5(
|
|
9157
9338
|
async (state, enableTransition = true) => {
|
|
9158
9339
|
const controls = viewerRef.current?.controls;
|
|
9159
9340
|
if (!controls) {
|
|
9160
9341
|
return false;
|
|
9161
9342
|
}
|
|
9162
9343
|
try {
|
|
9163
|
-
|
|
9344
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9345
|
+
markInitialAutoFitOverride(controls);
|
|
9346
|
+
}
|
|
9164
9347
|
const transitions = [
|
|
9165
9348
|
controls.setLookAt(
|
|
9166
9349
|
state.position[0],
|
|
@@ -9184,11 +9367,11 @@ function useViewerCamera() {
|
|
|
9184
9367
|
await Promise.all(transitions);
|
|
9185
9368
|
const camera = controls.camera;
|
|
9186
9369
|
let projectionChanged = false;
|
|
9187
|
-
if (state.fov
|
|
9370
|
+
if (state.fov != null && "fov" in camera && typeof camera.fov === "number") {
|
|
9188
9371
|
camera.fov = state.fov;
|
|
9189
9372
|
projectionChanged = true;
|
|
9190
9373
|
}
|
|
9191
|
-
if ("zoom" in camera && typeof camera.zoom === "number") {
|
|
9374
|
+
if (state.zoom != null && "zoom" in camera && typeof camera.zoom === "number") {
|
|
9192
9375
|
camera.zoom = state.zoom;
|
|
9193
9376
|
projectionChanged = true;
|
|
9194
9377
|
}
|
|
@@ -9203,13 +9386,94 @@ function useViewerCamera() {
|
|
|
9203
9386
|
},
|
|
9204
9387
|
[updateCameraState]
|
|
9205
9388
|
);
|
|
9389
|
+
const setCameraState = useCallback5(
|
|
9390
|
+
(state, enableTransition = true) => applyCameraPreset(state, enableTransition),
|
|
9391
|
+
[applyCameraPreset]
|
|
9392
|
+
);
|
|
9393
|
+
const lookAt = useCallback5(
|
|
9394
|
+
(position, target, enableTransition = true) => applyCameraPreset({ position, target }, enableTransition),
|
|
9395
|
+
[applyCameraPreset]
|
|
9396
|
+
);
|
|
9397
|
+
const orbitTo = useCallback5(
|
|
9398
|
+
async (azimuthAngle, polarAngle, enableTransition = true) => {
|
|
9399
|
+
const controls = viewerRef.current?.controls;
|
|
9400
|
+
if (!controls) {
|
|
9401
|
+
return false;
|
|
9402
|
+
}
|
|
9403
|
+
try {
|
|
9404
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9405
|
+
markInitialAutoFitOverride(controls);
|
|
9406
|
+
}
|
|
9407
|
+
await controls.rotateTo(
|
|
9408
|
+
azimuthAngle,
|
|
9409
|
+
polarAngle,
|
|
9410
|
+
enableTransition
|
|
9411
|
+
);
|
|
9412
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9413
|
+
return true;
|
|
9414
|
+
} catch {
|
|
9415
|
+
return false;
|
|
9416
|
+
}
|
|
9417
|
+
},
|
|
9418
|
+
[updateCameraState]
|
|
9419
|
+
);
|
|
9420
|
+
const dollyTo = useCallback5(
|
|
9421
|
+
async (distance, enableTransition = true) => {
|
|
9422
|
+
const controls = viewerRef.current?.controls;
|
|
9423
|
+
if (!controls) {
|
|
9424
|
+
return false;
|
|
9425
|
+
}
|
|
9426
|
+
try {
|
|
9427
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9428
|
+
markInitialAutoFitOverride(controls);
|
|
9429
|
+
}
|
|
9430
|
+
await controls.dollyTo(distance, enableTransition);
|
|
9431
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9432
|
+
return true;
|
|
9433
|
+
} catch {
|
|
9434
|
+
return false;
|
|
9435
|
+
}
|
|
9436
|
+
},
|
|
9437
|
+
[updateCameraState]
|
|
9438
|
+
);
|
|
9439
|
+
const savePreset = useCallback5((name) => {
|
|
9440
|
+
const controls = viewerRef.current?.controls;
|
|
9441
|
+
const trimmedName = name.trim();
|
|
9442
|
+
if (!controls || !trimmedName) {
|
|
9443
|
+
return null;
|
|
9444
|
+
}
|
|
9445
|
+
const nextState = buildCameraState(controls.camera, controls);
|
|
9446
|
+
cameraPresetsRef.current = {
|
|
9447
|
+
...cameraPresetsRef.current,
|
|
9448
|
+
[trimmedName]: nextState
|
|
9449
|
+
};
|
|
9450
|
+
setCameraPresets((current) => ({
|
|
9451
|
+
...current,
|
|
9452
|
+
[trimmedName]: nextState
|
|
9453
|
+
}));
|
|
9454
|
+
updateCameraState(nextState);
|
|
9455
|
+
return nextState;
|
|
9456
|
+
}, [updateCameraState]);
|
|
9457
|
+
const goToPreset = useCallback5(
|
|
9458
|
+
(name, enableTransition = true) => {
|
|
9459
|
+
const preset = cameraPresetsRef.current[name];
|
|
9460
|
+
return preset ? applyCameraPreset(preset, enableTransition) : Promise.resolve(false);
|
|
9461
|
+
},
|
|
9462
|
+
[applyCameraPreset]
|
|
9463
|
+
);
|
|
9206
9464
|
return {
|
|
9207
9465
|
cameraState,
|
|
9466
|
+
cameraPresets,
|
|
9208
9467
|
resetView,
|
|
9209
9468
|
focusObject,
|
|
9210
9469
|
fitScene,
|
|
9211
9470
|
setCameraTarget,
|
|
9212
9471
|
setCameraState,
|
|
9472
|
+
lookAt,
|
|
9473
|
+
orbitTo,
|
|
9474
|
+
dollyTo,
|
|
9475
|
+
savePreset,
|
|
9476
|
+
goToPreset,
|
|
9213
9477
|
handleCameraChange,
|
|
9214
9478
|
handleViewerReady
|
|
9215
9479
|
};
|
|
@@ -15078,6 +15342,9 @@ function IdentityPanel({
|
|
|
15078
15342
|
] }) });
|
|
15079
15343
|
}
|
|
15080
15344
|
|
|
15345
|
+
// src/components/BindingBuilder/components/BasicsPanel.tsx
|
|
15346
|
+
import { useEffect as useEffect21, useState as useState24 } from "react";
|
|
15347
|
+
|
|
15081
15348
|
// src/components/BindingBuilder/utils/constants.ts
|
|
15082
15349
|
var typeSuggestions = [
|
|
15083
15350
|
"other",
|
|
@@ -15117,6 +15384,31 @@ function BasicsPanel({
|
|
|
15117
15384
|
onToggle,
|
|
15118
15385
|
updateBinding
|
|
15119
15386
|
}) {
|
|
15387
|
+
const [groupDraft, setGroupDraft] = useState24(selectedBinding.group ?? "");
|
|
15388
|
+
const [tagsDraft, setTagsDraft] = useState24(
|
|
15389
|
+
selectedBinding.tags?.join(", ") ?? ""
|
|
15390
|
+
);
|
|
15391
|
+
useEffect21(() => {
|
|
15392
|
+
setGroupDraft(selectedBinding.group ?? "");
|
|
15393
|
+
setTagsDraft(selectedBinding.tags?.join(", ") ?? "");
|
|
15394
|
+
}, [selectedItem, selectedBinding.group, selectedBinding.tags]);
|
|
15395
|
+
const commitGroup = () => {
|
|
15396
|
+
updateBinding(selectedItem, (binding) => ({
|
|
15397
|
+
...binding,
|
|
15398
|
+
group: groupDraft.trim() || void 0
|
|
15399
|
+
}));
|
|
15400
|
+
};
|
|
15401
|
+
const commitTags = () => {
|
|
15402
|
+
const tags = [
|
|
15403
|
+
...new Set(
|
|
15404
|
+
tagsDraft.split(",").map((tag) => tag.trim()).filter(Boolean)
|
|
15405
|
+
)
|
|
15406
|
+
];
|
|
15407
|
+
updateBinding(selectedItem, (binding) => ({
|
|
15408
|
+
...binding,
|
|
15409
|
+
tags: tags.length > 0 ? tags : void 0
|
|
15410
|
+
}));
|
|
15411
|
+
};
|
|
15120
15412
|
return /* @__PURE__ */ jsxs36(BBSection, { title: "Basics", open, onToggle, children: [
|
|
15121
15413
|
/* @__PURE__ */ jsxs36("div", { className: "bb-grid", children: [
|
|
15122
15414
|
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
@@ -15163,6 +15455,38 @@ function BasicsPanel({
|
|
|
15163
15455
|
children: statusOptions.map((option) => /* @__PURE__ */ jsx41("option", { value: option, children: option }, option))
|
|
15164
15456
|
}
|
|
15165
15457
|
)
|
|
15458
|
+
] }),
|
|
15459
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
15460
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Group" }),
|
|
15461
|
+
/* @__PURE__ */ jsx41(
|
|
15462
|
+
"input",
|
|
15463
|
+
{
|
|
15464
|
+
className: "bb-input",
|
|
15465
|
+
placeholder: "paint",
|
|
15466
|
+
value: groupDraft,
|
|
15467
|
+
onChange: (event) => setGroupDraft(event.target.value),
|
|
15468
|
+
onBlur: commitGroup,
|
|
15469
|
+
onKeyDown: (event) => {
|
|
15470
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
)
|
|
15474
|
+
] }),
|
|
15475
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
15476
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Tags" }),
|
|
15477
|
+
/* @__PURE__ */ jsx41(
|
|
15478
|
+
"input",
|
|
15479
|
+
{
|
|
15480
|
+
className: "bb-input",
|
|
15481
|
+
placeholder: "exterior, configurable",
|
|
15482
|
+
value: tagsDraft,
|
|
15483
|
+
onChange: (event) => setTagsDraft(event.target.value),
|
|
15484
|
+
onBlur: commitTags,
|
|
15485
|
+
onKeyDown: (event) => {
|
|
15486
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
15487
|
+
}
|
|
15488
|
+
}
|
|
15489
|
+
)
|
|
15166
15490
|
] })
|
|
15167
15491
|
] }),
|
|
15168
15492
|
/* @__PURE__ */ jsxs36("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
|
|
@@ -15213,7 +15537,7 @@ function BasicsPanel({
|
|
|
15213
15537
|
}
|
|
15214
15538
|
|
|
15215
15539
|
// src/components/BindingBuilder/components/ActionsPanel.tsx
|
|
15216
|
-
import { useEffect as
|
|
15540
|
+
import { useEffect as useEffect22, useState as useState25 } from "react";
|
|
15217
15541
|
|
|
15218
15542
|
// src/components/BindingBuilder/utils/actionCatalog.ts
|
|
15219
15543
|
var ACTION_CATALOG = [
|
|
@@ -15239,19 +15563,19 @@ var ACTION_CATALOG = [
|
|
|
15239
15563
|
id: "toggle-light",
|
|
15240
15564
|
label: "Toggle Light",
|
|
15241
15565
|
handling: "host",
|
|
15242
|
-
description: "Seeded on light bindings.
|
|
15566
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15243
15567
|
},
|
|
15244
15568
|
{
|
|
15245
15569
|
id: "change-light-color",
|
|
15246
15570
|
label: "Change Light Color",
|
|
15247
15571
|
handling: "host",
|
|
15248
|
-
description: "Seeded on light bindings.
|
|
15572
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15249
15573
|
},
|
|
15250
15574
|
{
|
|
15251
15575
|
id: "set-brightness",
|
|
15252
15576
|
label: "Set Brightness",
|
|
15253
15577
|
handling: "host",
|
|
15254
|
-
description: "Seeded on light bindings.
|
|
15578
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15255
15579
|
}
|
|
15256
15580
|
];
|
|
15257
15581
|
var CUSTOM_ACTION_ID = "__custom__";
|
|
@@ -15312,8 +15636,8 @@ function ActionsPanel({
|
|
|
15312
15636
|
onToggle,
|
|
15313
15637
|
updateBinding
|
|
15314
15638
|
}) {
|
|
15315
|
-
const [customRows, setCustomRows] =
|
|
15316
|
-
|
|
15639
|
+
const [customRows, setCustomRows] = useState25(/* @__PURE__ */ new Set());
|
|
15640
|
+
useEffect22(() => {
|
|
15317
15641
|
setCustomRows(/* @__PURE__ */ new Set());
|
|
15318
15642
|
}, [selectedItem]);
|
|
15319
15643
|
const actions = selectedBinding.actions ?? [];
|
|
@@ -15429,8 +15753,8 @@ function ActionsPanel({
|
|
|
15429
15753
|
}
|
|
15430
15754
|
)
|
|
15431
15755
|
] }),
|
|
15432
|
-
catalogEntry ? /* @__PURE__ */ jsx42("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ jsx42("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required
|
|
15433
|
-
"Unrecognised ID
|
|
15756
|
+
catalogEntry ? /* @__PURE__ */ jsx42("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ jsx42("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required, the button renders but can never be identified in onAction." }) : /* @__PURE__ */ jsxs37("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
|
|
15757
|
+
"Unrecognised ID, the viewer has no built-in behavior for it. Add declarative effects and execute them with useViewerActions, or handle it in onAction.",
|
|
15434
15758
|
suggestion ? /* @__PURE__ */ jsxs37(Fragment13, { children: [
|
|
15435
15759
|
" Did you mean ",
|
|
15436
15760
|
/* @__PURE__ */ jsx42(
|
|
@@ -15453,7 +15777,13 @@ function ActionsPanel({
|
|
|
15453
15777
|
"?"
|
|
15454
15778
|
] }) : null
|
|
15455
15779
|
] }),
|
|
15456
|
-
|
|
15780
|
+
action.effects?.length ? /* @__PURE__ */ jsxs37("span", { className: "bb-note", children: [
|
|
15781
|
+
action.effects.length,
|
|
15782
|
+
" declarative effect",
|
|
15783
|
+
action.effects.length === 1 ? "" : "s",
|
|
15784
|
+
", execute with useViewerActions."
|
|
15785
|
+
] }) : null,
|
|
15786
|
+
isDuplicate ? /* @__PURE__ */ jsx42("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID, they will render as two identical buttons." }) : null
|
|
15457
15787
|
] }),
|
|
15458
15788
|
/* @__PURE__ */ jsx42("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx42(
|
|
15459
15789
|
"button",
|
|
@@ -15602,7 +15932,7 @@ function CameraPositionPanel({
|
|
|
15602
15932
|
}
|
|
15603
15933
|
|
|
15604
15934
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
15605
|
-
import { useMemo as useMemo16, useState as
|
|
15935
|
+
import { useMemo as useMemo16, useState as useState26 } from "react";
|
|
15606
15936
|
|
|
15607
15937
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
15608
15938
|
function slugify2(value) {
|
|
@@ -15691,7 +16021,7 @@ function SceneNodesPanel({
|
|
|
15691
16021
|
isOpen = true,
|
|
15692
16022
|
onToggle
|
|
15693
16023
|
}) {
|
|
15694
|
-
const [search, setSearch] =
|
|
16024
|
+
const [search, setSearch] = useState26("");
|
|
15695
16025
|
const nodeKeys = useMemo16(() => Object.keys(bindings), [bindings]);
|
|
15696
16026
|
const filteredNodeKeys = useMemo16(() => {
|
|
15697
16027
|
const query = search.trim().toLowerCase();
|
|
@@ -15922,9 +16252,9 @@ function toBindingsJson(bindings) {
|
|
|
15922
16252
|
}
|
|
15923
16253
|
function toBindingsTypeScript(bindings) {
|
|
15924
16254
|
return [
|
|
15925
|
-
'import
|
|
16255
|
+
'import { defineObjectBindings } from "@liveroom-tech/react-immersive";',
|
|
15926
16256
|
"",
|
|
15927
|
-
`export const objectBindings
|
|
16257
|
+
`export const objectBindings = defineObjectBindings(${JSON.stringify(bindings, null, 2)});`,
|
|
15928
16258
|
""
|
|
15929
16259
|
].join("\n");
|
|
15930
16260
|
}
|
|
@@ -16147,27 +16477,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16147
16477
|
const modelInputRef = useRef19(null);
|
|
16148
16478
|
const bindingsInputRef = useRef19(null);
|
|
16149
16479
|
const sceneConfigInputRef = useRef19(null);
|
|
16150
|
-
const [modelUrl, setModelUrl] =
|
|
16151
|
-
const [modelName, setModelName] =
|
|
16152
|
-
const [selectedItem, setSelectedItem] =
|
|
16480
|
+
const [modelUrl, setModelUrl] = useState27(null);
|
|
16481
|
+
const [modelName, setModelName] = useState27("");
|
|
16482
|
+
const [selectedItem, setSelectedItem] = useState27(null);
|
|
16153
16483
|
const bindingsHistory = useUndoableState({});
|
|
16154
16484
|
const bindings = bindingsHistory.value;
|
|
16155
16485
|
const setBindings = bindingsHistory.setValue;
|
|
16156
|
-
const [loading, setLoading] =
|
|
16157
|
-
const [isExporting, setIsExporting] =
|
|
16158
|
-
const [statusMessage, setStatusMessage] =
|
|
16486
|
+
const [loading, setLoading] = useState27(false);
|
|
16487
|
+
const [isExporting, setIsExporting] = useState27(false);
|
|
16488
|
+
const [statusMessage, setStatusMessage] = useState27(
|
|
16159
16489
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
16160
16490
|
);
|
|
16161
|
-
const [statusTone, setStatusTone] =
|
|
16162
|
-
const [statusBannerVisible, setStatusBannerVisible] =
|
|
16163
|
-
const [metricsDraft, setMetricsDraft] =
|
|
16164
|
-
const [metadataDraft, setMetadataDraft] =
|
|
16165
|
-
const [metricsError, setMetricsError] =
|
|
16166
|
-
const [metadataError, setMetadataError] =
|
|
16167
|
-
const [isPreviewDragActive, setIsPreviewDragActive] =
|
|
16168
|
-
const [isEditorCollapsed, setIsEditorCollapsed] =
|
|
16169
|
-
const [isNodesCollapsed, setIsNodesCollapsed] =
|
|
16170
|
-
const [openObjectSections, setOpenObjectSections] =
|
|
16491
|
+
const [statusTone, setStatusTone] = useState27("default");
|
|
16492
|
+
const [statusBannerVisible, setStatusBannerVisible] = useState27(false);
|
|
16493
|
+
const [metricsDraft, setMetricsDraft] = useState27("{}");
|
|
16494
|
+
const [metadataDraft, setMetadataDraft] = useState27("{}");
|
|
16495
|
+
const [metricsError, setMetricsError] = useState27(null);
|
|
16496
|
+
const [metadataError, setMetadataError] = useState27(null);
|
|
16497
|
+
const [isPreviewDragActive, setIsPreviewDragActive] = useState27(false);
|
|
16498
|
+
const [isEditorCollapsed, setIsEditorCollapsed] = useState27(false);
|
|
16499
|
+
const [isNodesCollapsed, setIsNodesCollapsed] = useState27(false);
|
|
16500
|
+
const [openObjectSections, setOpenObjectSections] = useState27({
|
|
16171
16501
|
identity: true,
|
|
16172
16502
|
basics: true,
|
|
16173
16503
|
material: false,
|
|
@@ -16176,7 +16506,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16176
16506
|
metrics: false,
|
|
16177
16507
|
metadata: false
|
|
16178
16508
|
});
|
|
16179
|
-
const [openMaterialPanels, setOpenMaterialPanels] =
|
|
16509
|
+
const [openMaterialPanels, setOpenMaterialPanels] = useState27({
|
|
16180
16510
|
pbr: true,
|
|
16181
16511
|
roughness: false,
|
|
16182
16512
|
emission: false,
|
|
@@ -16192,24 +16522,24 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16192
16522
|
});
|
|
16193
16523
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
16194
16524
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
16195
|
-
const [activeEditorTab, setActiveEditorTab] =
|
|
16525
|
+
const [activeEditorTab, setActiveEditorTab] = useState27(
|
|
16196
16526
|
"object"
|
|
16197
16527
|
);
|
|
16198
|
-
const [activeSubTab, setActiveSubTab] =
|
|
16199
|
-
const [pbrWorkflow, setPbrWorkflow] =
|
|
16200
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] =
|
|
16528
|
+
const [activeSubTab, setActiveSubTab] = useState27("General");
|
|
16529
|
+
const [pbrWorkflow, setPbrWorkflow] = useState27("metalness");
|
|
16530
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = useState27(createMaterialPanelEnabledState());
|
|
16201
16531
|
const materialPanelItemRef = useRef19(null);
|
|
16202
|
-
const [zoomOnSelected, setZoomOnSelected] =
|
|
16203
|
-
const [highlightOnHover, setHighlightOnHover] =
|
|
16204
|
-
const [resetDialogOpen, setResetDialogOpen] =
|
|
16205
|
-
const [resetTarget, setResetTarget] =
|
|
16532
|
+
const [zoomOnSelected, setZoomOnSelected] = useState27(false);
|
|
16533
|
+
const [highlightOnHover, setHighlightOnHover] = useState27(true);
|
|
16534
|
+
const [resetDialogOpen, setResetDialogOpen] = useState27(false);
|
|
16535
|
+
const [resetTarget, setResetTarget] = useState27(
|
|
16206
16536
|
null
|
|
16207
16537
|
);
|
|
16208
16538
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
16209
16539
|
const sceneConfig = sceneConfigHistory.value;
|
|
16210
16540
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
16211
|
-
const [activeAnnotation, setActiveAnnotation] =
|
|
16212
|
-
const [panelHoveredBinding, setPanelHoveredBinding] =
|
|
16541
|
+
const [activeAnnotation, setActiveAnnotation] = useState27(null);
|
|
16542
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = useState27(null);
|
|
16213
16543
|
const license = useLicense(licenseKey);
|
|
16214
16544
|
const tier = normalizeTier(license.tier);
|
|
16215
16545
|
const bindingBuilderContextValue = useMemo17(
|
|
@@ -16287,7 +16617,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16287
16617
|
},
|
|
16288
16618
|
[]
|
|
16289
16619
|
);
|
|
16290
|
-
|
|
16620
|
+
useEffect23(() => {
|
|
16291
16621
|
if (!selectedBinding) {
|
|
16292
16622
|
setMetricsDraft("{}");
|
|
16293
16623
|
setMetadataDraft("{}");
|
|
@@ -16300,7 +16630,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16300
16630
|
setMetricsError(null);
|
|
16301
16631
|
setMetadataError(null);
|
|
16302
16632
|
}, [selectedBinding]);
|
|
16303
|
-
|
|
16633
|
+
useEffect23(() => {
|
|
16304
16634
|
const derived = createMaterialPanelEnabledState(selectedMaterial);
|
|
16305
16635
|
if (materialPanelItemRef.current !== selectedItem) {
|
|
16306
16636
|
materialPanelItemRef.current = selectedItem;
|
|
@@ -16319,12 +16649,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16319
16649
|
return changed ? next : prev;
|
|
16320
16650
|
});
|
|
16321
16651
|
}, [selectedItem, selectedMaterial]);
|
|
16322
|
-
|
|
16652
|
+
useEffect23(() => {
|
|
16323
16653
|
return () => {
|
|
16324
16654
|
uploadedModelRef.current?.cleanup();
|
|
16325
16655
|
};
|
|
16326
16656
|
}, []);
|
|
16327
|
-
|
|
16657
|
+
useEffect23(() => {
|
|
16328
16658
|
const handleKeyDown = (event) => {
|
|
16329
16659
|
if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
|
|
16330
16660
|
return;
|
|
@@ -16345,7 +16675,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16345
16675
|
window.addEventListener("keydown", handleKeyDown);
|
|
16346
16676
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
16347
16677
|
}, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
|
|
16348
|
-
|
|
16678
|
+
useEffect23(() => {
|
|
16349
16679
|
if (loading || statusTone === "default" || statusTone === "success") return;
|
|
16350
16680
|
setStatusBannerVisible(true);
|
|
16351
16681
|
const timeout = window.setTimeout(
|
|
@@ -17375,7 +17705,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
17375
17705
|
/* @__PURE__ */ jsx46("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
|
|
17376
17706
|
"?"
|
|
17377
17707
|
] }),
|
|
17378
|
-
/* @__PURE__ */ jsx46("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
|
|
17708
|
+
/* @__PURE__ */ jsx46("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, groups, tags, transforms, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
|
|
17379
17709
|
/* @__PURE__ */ jsx46("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
|
|
17380
17710
|
] }),
|
|
17381
17711
|
/* @__PURE__ */ jsxs41("div", { className: "bb-reset-footer", children: [
|
|
@@ -17389,9 +17719,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
17389
17719
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
17390
17720
|
import {
|
|
17391
17721
|
useRef as useRef20,
|
|
17392
|
-
useState as
|
|
17722
|
+
useState as useState28,
|
|
17393
17723
|
useCallback as useCallback10,
|
|
17394
|
-
useEffect as
|
|
17724
|
+
useEffect as useEffect24,
|
|
17395
17725
|
useMemo as useMemo18,
|
|
17396
17726
|
Component as Component3,
|
|
17397
17727
|
Suspense as Suspense3
|
|
@@ -17402,7 +17732,7 @@ import { Canvas as Canvas3 } from "@react-three/fiber";
|
|
|
17402
17732
|
import {
|
|
17403
17733
|
Box3 as Box35,
|
|
17404
17734
|
PerspectiveCamera as PerspectiveCamera4,
|
|
17405
|
-
Vector3 as
|
|
17735
|
+
Vector3 as Vector311
|
|
17406
17736
|
} from "three";
|
|
17407
17737
|
import { SkeletonUtils as SkeletonUtils2 } from "three-stdlib";
|
|
17408
17738
|
|
|
@@ -17441,7 +17771,7 @@ function applyClippingPlanes(scene, controls) {
|
|
|
17441
17771
|
if (!controls) return;
|
|
17442
17772
|
const camera = controls.camera;
|
|
17443
17773
|
if (!(camera instanceof PerspectiveCamera4)) return;
|
|
17444
|
-
const size = new Box35().setFromObject(scene).getSize(new
|
|
17774
|
+
const size = new Box35().setFromObject(scene).getSize(new Vector311()).length();
|
|
17445
17775
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
17446
17776
|
camera.near = size / 100;
|
|
17447
17777
|
camera.far = size * 100;
|
|
@@ -17480,7 +17810,7 @@ function InnerModel({
|
|
|
17480
17810
|
meshopt: true
|
|
17481
17811
|
});
|
|
17482
17812
|
const invalidate = useThree12((state) => state.invalidate);
|
|
17483
|
-
const [hoveredKey, setHoveredKey] =
|
|
17813
|
+
const [hoveredKey, setHoveredKey] = useState28(null);
|
|
17484
17814
|
const scene = useMemo18(
|
|
17485
17815
|
() => SkeletonUtils2.clone(rawScene),
|
|
17486
17816
|
[rawScene]
|
|
@@ -17510,14 +17840,14 @@ function InnerModel({
|
|
|
17510
17840
|
}
|
|
17511
17841
|
return map;
|
|
17512
17842
|
}, [meshMap]);
|
|
17513
|
-
|
|
17843
|
+
useEffect24(
|
|
17514
17844
|
() => () => {
|
|
17515
17845
|
Object.values(perKeyMaterials).forEach((m) => m.dispose());
|
|
17516
17846
|
},
|
|
17517
17847
|
[perKeyMaterials]
|
|
17518
17848
|
);
|
|
17519
17849
|
const notifiedUrl = useRef20(null);
|
|
17520
|
-
|
|
17850
|
+
useEffect24(() => {
|
|
17521
17851
|
if (notifiedUrl.current === url) return;
|
|
17522
17852
|
notifiedUrl.current = url;
|
|
17523
17853
|
scene.updateWorldMatrix(true, true);
|
|
@@ -17525,7 +17855,7 @@ function InnerModel({
|
|
|
17525
17855
|
onMeshesReady(meshMap);
|
|
17526
17856
|
onModelLoaded(scene);
|
|
17527
17857
|
}, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
|
|
17528
|
-
|
|
17858
|
+
useEffect24(() => {
|
|
17529
17859
|
const controller = new AbortController();
|
|
17530
17860
|
void buildSceneBVH(scene, controller.signal);
|
|
17531
17861
|
return () => {
|
|
@@ -17537,22 +17867,22 @@ function InnerModel({
|
|
|
17537
17867
|
() => !exceedsBvhBudget(scene),
|
|
17538
17868
|
[scene]
|
|
17539
17869
|
);
|
|
17540
|
-
|
|
17870
|
+
useEffect24(() => {
|
|
17541
17871
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
17542
17872
|
nodeRefsOut.current[key] = ref;
|
|
17543
17873
|
}
|
|
17544
17874
|
}, [meshMap, nodeRefsOut]);
|
|
17545
|
-
|
|
17875
|
+
useEffect24(() => {
|
|
17546
17876
|
if (!highlightOnHover && hoveredKey !== null) {
|
|
17547
17877
|
setHoveredKey(null);
|
|
17548
17878
|
}
|
|
17549
17879
|
}, [highlightOnHover, hoveredKey]);
|
|
17550
|
-
|
|
17880
|
+
useEffect24(() => {
|
|
17551
17881
|
return () => {
|
|
17552
17882
|
document.body.style.cursor = "default";
|
|
17553
17883
|
};
|
|
17554
17884
|
}, []);
|
|
17555
|
-
|
|
17885
|
+
useEffect24(() => {
|
|
17556
17886
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
17557
17887
|
ref.visible = !hiddenKeys.has(key);
|
|
17558
17888
|
const mat = perKeyMaterials[key];
|
|
@@ -17626,7 +17956,7 @@ function CameraFitter({
|
|
|
17626
17956
|
const invalidate = useThree12((state) => state.invalidate);
|
|
17627
17957
|
const size = useThree12((state) => state.size);
|
|
17628
17958
|
const resizeTimerRef = useRef20(null);
|
|
17629
|
-
|
|
17959
|
+
useEffect24(() => {
|
|
17630
17960
|
fitted.current = false;
|
|
17631
17961
|
invalidate();
|
|
17632
17962
|
}, [invalidate, scene]);
|
|
@@ -17646,7 +17976,7 @@ function CameraFitter({
|
|
|
17646
17976
|
}).catch(() => {
|
|
17647
17977
|
}).finally(onInitialFitComplete);
|
|
17648
17978
|
});
|
|
17649
|
-
|
|
17979
|
+
useEffect24(() => {
|
|
17650
17980
|
if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
|
|
17651
17981
|
return;
|
|
17652
17982
|
}
|
|
@@ -17674,7 +18004,7 @@ function CameraFitter({
|
|
|
17674
18004
|
function ExposureController({ exposure }) {
|
|
17675
18005
|
const gl = useThree12((state) => state.gl);
|
|
17676
18006
|
const invalidate = useThree12((state) => state.invalidate);
|
|
17677
|
-
|
|
18007
|
+
useEffect24(() => {
|
|
17678
18008
|
gl.toneMappingExposure = exposure;
|
|
17679
18009
|
invalidate();
|
|
17680
18010
|
}, [gl, exposure, invalidate]);
|
|
@@ -17782,7 +18112,7 @@ function EnvironmentSection({
|
|
|
17782
18112
|
title,
|
|
17783
18113
|
children
|
|
17784
18114
|
}) {
|
|
17785
|
-
const [collapsed, setCollapsed] =
|
|
18115
|
+
const [collapsed, setCollapsed] = useState28(false);
|
|
17786
18116
|
return /* @__PURE__ */ jsxs42("div", { className: "env-section", children: [
|
|
17787
18117
|
/* @__PURE__ */ jsxs42(
|
|
17788
18118
|
"button",
|
|
@@ -17947,8 +18277,8 @@ function SceneObjectsPanel2({
|
|
|
17947
18277
|
onHover,
|
|
17948
18278
|
onToggleVisibility
|
|
17949
18279
|
}) {
|
|
17950
|
-
const [search, setSearch] =
|
|
17951
|
-
const [rowHovered, setRowHovered] =
|
|
18280
|
+
const [search, setSearch] = useState28("");
|
|
18281
|
+
const [rowHovered, setRowHovered] = useState28(null);
|
|
17952
18282
|
const entries = Object.values(meshKeys).filter(
|
|
17953
18283
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
17954
18284
|
);
|
|
@@ -18077,30 +18407,30 @@ function SimpleModelViewer({
|
|
|
18077
18407
|
const fileInputRef = useRef20(null);
|
|
18078
18408
|
const uploadedModelRef = useRef20(null);
|
|
18079
18409
|
const dragDepthRef = useRef20(0);
|
|
18080
|
-
const [loadedScene, setLoadedScene] =
|
|
18081
|
-
const [largeModelTriangles, setLargeModelTriangles] =
|
|
18410
|
+
const [loadedScene, setLoadedScene] = useState28(null);
|
|
18411
|
+
const [largeModelTriangles, setLargeModelTriangles] = useState28(
|
|
18082
18412
|
null
|
|
18083
18413
|
);
|
|
18084
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] =
|
|
18085
|
-
const [meshKeys, setMeshKeys] =
|
|
18086
|
-
const [hiddenKeys, setHiddenKeys] =
|
|
18087
|
-
const [selectedKey, setSelectedKey] =
|
|
18088
|
-
const [panelHoveredKey, setPanelHoveredKey] =
|
|
18089
|
-
const [uploadedModelUrl, setUploadedModelUrl] =
|
|
18090
|
-
const [uploadedModelName, setUploadedModelName] =
|
|
18414
|
+
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState28(false);
|
|
18415
|
+
const [meshKeys, setMeshKeys] = useState28({});
|
|
18416
|
+
const [hiddenKeys, setHiddenKeys] = useState28(/* @__PURE__ */ new Set());
|
|
18417
|
+
const [selectedKey, setSelectedKey] = useState28(null);
|
|
18418
|
+
const [panelHoveredKey, setPanelHoveredKey] = useState28(null);
|
|
18419
|
+
const [uploadedModelUrl, setUploadedModelUrl] = useState28(null);
|
|
18420
|
+
const [uploadedModelName, setUploadedModelName] = useState28(
|
|
18091
18421
|
null
|
|
18092
18422
|
);
|
|
18093
|
-
const [modelLoadFailed, setModelLoadFailed] =
|
|
18094
|
-
const [isResolvingUpload, setIsResolvingUpload] =
|
|
18095
|
-
const [initialFitComplete, setInitialFitComplete] =
|
|
18096
|
-
const [isDragActive, setIsDragActive] =
|
|
18097
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
18423
|
+
const [modelLoadFailed, setModelLoadFailed] = useState28(false);
|
|
18424
|
+
const [isResolvingUpload, setIsResolvingUpload] = useState28(false);
|
|
18425
|
+
const [initialFitComplete, setInitialFitComplete] = useState28(false);
|
|
18426
|
+
const [isDragActive, setIsDragActive] = useState28(false);
|
|
18427
|
+
const [isPanelOpen, setIsPanelOpen] = useState28(
|
|
18098
18428
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
18099
18429
|
);
|
|
18100
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] =
|
|
18430
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = useState28(
|
|
18101
18431
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
18102
18432
|
);
|
|
18103
|
-
const [envSettings, setEnvSettings] =
|
|
18433
|
+
const [envSettings, setEnvSettings] = useState28({
|
|
18104
18434
|
backgroundEnabled,
|
|
18105
18435
|
backgroundColor,
|
|
18106
18436
|
autoRotate,
|
|
@@ -18183,7 +18513,7 @@ function SimpleModelViewer({
|
|
|
18183
18513
|
},
|
|
18184
18514
|
[handleUploadedModel]
|
|
18185
18515
|
);
|
|
18186
|
-
|
|
18516
|
+
useEffect24(() => {
|
|
18187
18517
|
return () => {
|
|
18188
18518
|
revokeUploadedModelUrl();
|
|
18189
18519
|
};
|
|
@@ -18194,10 +18524,10 @@ function SimpleModelViewer({
|
|
|
18194
18524
|
},
|
|
18195
18525
|
[]
|
|
18196
18526
|
);
|
|
18197
|
-
|
|
18527
|
+
useEffect24(() => {
|
|
18198
18528
|
setEnvSettings((prev) => ({ ...prev, backgroundColor }));
|
|
18199
18529
|
}, [backgroundColor]);
|
|
18200
|
-
|
|
18530
|
+
useEffect24(() => {
|
|
18201
18531
|
resetModelState();
|
|
18202
18532
|
}, [activeModelUrl, resetModelState]);
|
|
18203
18533
|
const handleMeshesReady = useCallback10((meshes) => {
|
|
@@ -18513,9 +18843,9 @@ function SimpleModelViewer({
|
|
|
18513
18843
|
}
|
|
18514
18844
|
|
|
18515
18845
|
// src/hooks/useViewerSelection.ts
|
|
18516
|
-
import { useCallback as useCallback11, useState as
|
|
18846
|
+
import { useCallback as useCallback11, useState as useState29 } from "react";
|
|
18517
18847
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
18518
|
-
const [selectedObjectBinding, setSelectedObjectBinding] =
|
|
18848
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = useState29(
|
|
18519
18849
|
initialSelectedObjectBinding
|
|
18520
18850
|
);
|
|
18521
18851
|
const handleObjectSelect = useCallback11(
|
|
@@ -18562,7 +18892,7 @@ function useObjectBindingIds(objectBindings) {
|
|
|
18562
18892
|
}
|
|
18563
18893
|
|
|
18564
18894
|
// src/hooks/useObjectBindings.ts
|
|
18565
|
-
import { useCallback as useCallback12, useMemo as useMemo21, useState as
|
|
18895
|
+
import { useCallback as useCallback12, useMemo as useMemo21, useState as useState30 } from "react";
|
|
18566
18896
|
|
|
18567
18897
|
// src/utils/deepMerge.ts
|
|
18568
18898
|
function isPlainObject2(value) {
|
|
@@ -18580,6 +18910,27 @@ function deepMerge(base, patch) {
|
|
|
18580
18910
|
return target;
|
|
18581
18911
|
}
|
|
18582
18912
|
|
|
18913
|
+
// src/utils/selectBindings.ts
|
|
18914
|
+
function isSelector(target) {
|
|
18915
|
+
return typeof target === "object" && !Array.isArray(target);
|
|
18916
|
+
}
|
|
18917
|
+
function matchesSelector(binding, selector) {
|
|
18918
|
+
if ("group" in selector) return binding.group === selector.group;
|
|
18919
|
+
if ("tag" in selector) return binding.tags?.includes(selector.tag) ?? false;
|
|
18920
|
+
return binding.type === selector.type;
|
|
18921
|
+
}
|
|
18922
|
+
function selectBindingKeys(bindings, target) {
|
|
18923
|
+
if (isSelector(target)) {
|
|
18924
|
+
return Object.entries(bindings).filter(([, binding]) => matchesSelector(binding, target)).map(([bindingKey]) => bindingKey);
|
|
18925
|
+
}
|
|
18926
|
+
const ids = typeof target === "string" ? [target] : target;
|
|
18927
|
+
const keys = ids.map((id) => resolveBindingKey(bindings, id)).filter((key) => key !== null);
|
|
18928
|
+
return [...new Set(keys)];
|
|
18929
|
+
}
|
|
18930
|
+
function selectBindings(bindings, target) {
|
|
18931
|
+
return selectBindingKeys(bindings, target).map((key) => bindings[key]).filter((binding) => binding !== void 0);
|
|
18932
|
+
}
|
|
18933
|
+
|
|
18583
18934
|
// src/utils/patchBindings.ts
|
|
18584
18935
|
function isBindingEditArray(value) {
|
|
18585
18936
|
return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
|
|
@@ -18588,10 +18939,7 @@ function patchBindings(bindings, objectIdOrEdits, patch) {
|
|
|
18588
18939
|
const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
|
|
18589
18940
|
let next = bindings;
|
|
18590
18941
|
for (const edit of edits) {
|
|
18591
|
-
const
|
|
18592
|
-
for (const id of ids) {
|
|
18593
|
-
const bindingKey = resolveBindingKey(next, id);
|
|
18594
|
-
if (!bindingKey) continue;
|
|
18942
|
+
for (const bindingKey of selectBindingKeys(next, edit.ids)) {
|
|
18595
18943
|
const binding = next[bindingKey];
|
|
18596
18944
|
if (!binding) continue;
|
|
18597
18945
|
const patchedBinding = deepMerge(binding, edit.patch);
|
|
@@ -18606,13 +18954,43 @@ function material(value) {
|
|
|
18606
18954
|
}
|
|
18607
18955
|
|
|
18608
18956
|
// src/hooks/useObjectBindings.ts
|
|
18957
|
+
var TEXTURE_PROPERTY_BY_CHANNEL = {
|
|
18958
|
+
baseColor: "texture",
|
|
18959
|
+
metalness: "metalnessMap",
|
|
18960
|
+
roughness: "roughnessMap",
|
|
18961
|
+
emissive: "emissiveMap",
|
|
18962
|
+
alpha: "alphaMap",
|
|
18963
|
+
normal: "normalMap",
|
|
18964
|
+
bump: "bumpMap",
|
|
18965
|
+
ao: "aoMap",
|
|
18966
|
+
displacement: "displacementMap",
|
|
18967
|
+
clearcoat: "clearcoatMap",
|
|
18968
|
+
clearcoatRoughness: "clearcoatRoughnessMap",
|
|
18969
|
+
clearcoatNormal: "clearcoatNormalMap",
|
|
18970
|
+
anisotropy: "anisotropyMap",
|
|
18971
|
+
transmission: "transmissionMap",
|
|
18972
|
+
thickness: "thicknessMap",
|
|
18973
|
+
sheenColor: "sheenColorMap",
|
|
18974
|
+
sheenRoughness: "sheenRoughnessMap",
|
|
18975
|
+
specularIntensity: "specularIntensityMap",
|
|
18976
|
+
specularColor: "specularColorMap"
|
|
18977
|
+
};
|
|
18978
|
+
function cloneMaterial(value) {
|
|
18979
|
+
if (!value) return void 0;
|
|
18980
|
+
return Object.fromEntries(
|
|
18981
|
+
Object.entries(value).map(([key, field]) => [
|
|
18982
|
+
key,
|
|
18983
|
+
field && typeof field === "object" ? { ...field } : field
|
|
18984
|
+
])
|
|
18985
|
+
);
|
|
18986
|
+
}
|
|
18609
18987
|
function getHiddenObjects(objectBindings) {
|
|
18610
18988
|
return Object.fromEntries(
|
|
18611
18989
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
18612
18990
|
);
|
|
18613
18991
|
}
|
|
18614
18992
|
function useObjectBindings(initialBindings) {
|
|
18615
|
-
const [objectBindings, setObjectBindings] =
|
|
18993
|
+
const [objectBindings, setObjectBindings] = useState30(initialBindings);
|
|
18616
18994
|
const updateObjectBindings = useCallback12(
|
|
18617
18995
|
(objectIdOrEdits, patchValue) => {
|
|
18618
18996
|
setObjectBindings(
|
|
@@ -18626,17 +19004,86 @@ function useObjectBindings(initialBindings) {
|
|
|
18626
19004
|
[]
|
|
18627
19005
|
);
|
|
18628
19006
|
const setMaterial = useCallback12(
|
|
18629
|
-
(
|
|
19007
|
+
(target, value) => updateObjectBindings(target, material(value)),
|
|
18630
19008
|
[updateObjectBindings]
|
|
18631
19009
|
);
|
|
18632
19010
|
const setBaseColor = useCallback12(
|
|
18633
|
-
(
|
|
19011
|
+
(target, color) => setMaterial(target, { baseColor: color }),
|
|
18634
19012
|
[setMaterial]
|
|
18635
19013
|
);
|
|
19014
|
+
const setTexture = useCallback12(
|
|
19015
|
+
(target, texture, channel = "baseColor") => {
|
|
19016
|
+
const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
|
|
19017
|
+
const slot = typeof texture === "string" ? { path: texture } : texture;
|
|
19018
|
+
updateObjectBindings(target, {
|
|
19019
|
+
style: { material: { [property]: slot } }
|
|
19020
|
+
});
|
|
19021
|
+
},
|
|
19022
|
+
[updateObjectBindings]
|
|
19023
|
+
);
|
|
19024
|
+
const clearTexture = useCallback12(
|
|
19025
|
+
(target, channel = "baseColor") => {
|
|
19026
|
+
const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
|
|
19027
|
+
updateObjectBindings(target, {
|
|
19028
|
+
style: { material: { [property]: void 0 } }
|
|
19029
|
+
});
|
|
19030
|
+
},
|
|
19031
|
+
[updateObjectBindings]
|
|
19032
|
+
);
|
|
19033
|
+
const copyMaterial = useCallback12(
|
|
19034
|
+
(source, target) => {
|
|
19035
|
+
setObjectBindings((current) => {
|
|
19036
|
+
const sourceKey = resolveBindingKey(current, source);
|
|
19037
|
+
if (!sourceKey) return current;
|
|
19038
|
+
const sourceMaterial = cloneMaterial(
|
|
19039
|
+
current[sourceKey]?.style?.material
|
|
19040
|
+
);
|
|
19041
|
+
const targetKeys = selectBindingKeys(current, target);
|
|
19042
|
+
if (targetKeys.length === 0) return current;
|
|
19043
|
+
let next = current;
|
|
19044
|
+
for (const targetKey of targetKeys) {
|
|
19045
|
+
const binding = current[targetKey];
|
|
19046
|
+
if (!binding) continue;
|
|
19047
|
+
if (next === current) next = { ...current };
|
|
19048
|
+
next[targetKey] = {
|
|
19049
|
+
...binding,
|
|
19050
|
+
style: {
|
|
19051
|
+
...binding.style,
|
|
19052
|
+
material: cloneMaterial(sourceMaterial)
|
|
19053
|
+
}
|
|
19054
|
+
};
|
|
19055
|
+
}
|
|
19056
|
+
return next;
|
|
19057
|
+
});
|
|
19058
|
+
},
|
|
19059
|
+
[]
|
|
19060
|
+
);
|
|
18636
19061
|
const resetMaterial = useCallback12(
|
|
18637
|
-
(
|
|
19062
|
+
(target) => updateObjectBindings(target, { style: { material: void 0 } }),
|
|
19063
|
+
[updateObjectBindings]
|
|
19064
|
+
);
|
|
19065
|
+
const setTransform = useCallback12(
|
|
19066
|
+
(target, transform) => updateObjectBindings(target, { transform }),
|
|
19067
|
+
[updateObjectBindings]
|
|
19068
|
+
);
|
|
19069
|
+
const resetTransform = useCallback12(
|
|
19070
|
+
(target) => updateObjectBindings(target, { transform: void 0 }),
|
|
18638
19071
|
[updateObjectBindings]
|
|
18639
19072
|
);
|
|
19073
|
+
const updateMetadata = useCallback12(
|
|
19074
|
+
(target, metadata) => updateObjectBindings(target, { metadata }),
|
|
19075
|
+
[updateObjectBindings]
|
|
19076
|
+
);
|
|
19077
|
+
const selectObjectBindings = useCallback12(
|
|
19078
|
+
(target) => selectBindings(objectBindings, target),
|
|
19079
|
+
[objectBindings]
|
|
19080
|
+
);
|
|
19081
|
+
const selectObjectBindingIds = useCallback12(
|
|
19082
|
+
(target) => selectBindingKeys(objectBindings, target).map(
|
|
19083
|
+
(bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
19084
|
+
),
|
|
19085
|
+
[objectBindings]
|
|
19086
|
+
);
|
|
18640
19087
|
const getMaterial = useCallback12(
|
|
18641
19088
|
(objectId) => {
|
|
18642
19089
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
@@ -18645,27 +19092,28 @@ function useObjectBindings(initialBindings) {
|
|
|
18645
19092
|
[objectBindings]
|
|
18646
19093
|
);
|
|
18647
19094
|
const hideObject = useCallback12(
|
|
18648
|
-
(
|
|
19095
|
+
(target) => updateObjectBindings(target, { visible: false }),
|
|
18649
19096
|
[updateObjectBindings]
|
|
18650
19097
|
);
|
|
18651
19098
|
const showObject = useCallback12(
|
|
18652
|
-
(
|
|
19099
|
+
(target) => updateObjectBindings(target, { visible: true }),
|
|
18653
19100
|
[updateObjectBindings]
|
|
18654
19101
|
);
|
|
18655
19102
|
const toggleObjectVisibility = useCallback12(
|
|
18656
|
-
(
|
|
19103
|
+
(target) => {
|
|
18657
19104
|
setObjectBindings((current) => {
|
|
18658
|
-
const
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
19105
|
+
const edits = selectBindingKeys(current, target).reduce(
|
|
19106
|
+
(acc, bindingKey) => {
|
|
19107
|
+
const binding = current[bindingKey];
|
|
19108
|
+
if (!binding) return acc;
|
|
19109
|
+
acc.push({
|
|
19110
|
+
ids: bindingKey,
|
|
19111
|
+
patch: { visible: binding.visible === false }
|
|
19112
|
+
});
|
|
19113
|
+
return acc;
|
|
19114
|
+
},
|
|
19115
|
+
[]
|
|
19116
|
+
);
|
|
18669
19117
|
return patchBindings(current, edits);
|
|
18670
19118
|
});
|
|
18671
19119
|
},
|
|
@@ -18699,8 +19147,16 @@ function useObjectBindings(initialBindings) {
|
|
|
18699
19147
|
updateObjectBindings,
|
|
18700
19148
|
setMaterial,
|
|
18701
19149
|
setBaseColor,
|
|
19150
|
+
setTexture,
|
|
19151
|
+
clearTexture,
|
|
19152
|
+
copyMaterial,
|
|
18702
19153
|
resetMaterial,
|
|
18703
19154
|
getMaterial,
|
|
19155
|
+
setTransform,
|
|
19156
|
+
resetTransform,
|
|
19157
|
+
updateMetadata,
|
|
19158
|
+
selectObjectBindings,
|
|
19159
|
+
selectObjectBindingIds,
|
|
18704
19160
|
hiddenObjects,
|
|
18705
19161
|
hiddenObjectIds,
|
|
18706
19162
|
hideObject,
|
|
@@ -18711,8 +19167,13 @@ function useObjectBindings(initialBindings) {
|
|
|
18711
19167
|
};
|
|
18712
19168
|
}
|
|
18713
19169
|
|
|
19170
|
+
// src/utils/defineObjectBindings.ts
|
|
19171
|
+
function defineObjectBindings(bindings) {
|
|
19172
|
+
return bindings;
|
|
19173
|
+
}
|
|
19174
|
+
|
|
18714
19175
|
// src/hooks/useSceneConfig.ts
|
|
18715
|
-
import { useCallback as useCallback13, useRef as useRef21, useState as
|
|
19176
|
+
import { useCallback as useCallback13, useMemo as useMemo22, useRef as useRef21, useState as useState31 } from "react";
|
|
18716
19177
|
|
|
18717
19178
|
// src/utils/patchSceneConfig.ts
|
|
18718
19179
|
function patchSceneConfig(sceneConfig, patch) {
|
|
@@ -18720,9 +19181,21 @@ function patchSceneConfig(sceneConfig, patch) {
|
|
|
18720
19181
|
}
|
|
18721
19182
|
|
|
18722
19183
|
// src/hooks/useSceneConfig.ts
|
|
19184
|
+
var DEFAULT_LIGHT = {
|
|
19185
|
+
type: "point",
|
|
19186
|
+
color: "#ffffff",
|
|
19187
|
+
intensity: 1,
|
|
19188
|
+
position: [0, 0, 0],
|
|
19189
|
+
distance: 0,
|
|
19190
|
+
decay: 2,
|
|
19191
|
+
attachedToCamera: false,
|
|
19192
|
+
castShadow: false,
|
|
19193
|
+
shadowBias: -5e-4,
|
|
19194
|
+
visible: true
|
|
19195
|
+
};
|
|
18723
19196
|
function useSceneConfig(initialSceneConfig) {
|
|
18724
19197
|
const initialSceneConfigRef = useRef21(null);
|
|
18725
|
-
const [sceneConfig, setSceneConfig] =
|
|
19198
|
+
const [sceneConfig, setSceneConfig] = useState31(() => {
|
|
18726
19199
|
const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
|
|
18727
19200
|
initialSceneConfigRef.current = initial;
|
|
18728
19201
|
return initial;
|
|
@@ -18737,18 +19210,124 @@ function useSceneConfig(initialSceneConfig) {
|
|
|
18737
19210
|
const initial = initialSceneConfigRef.current;
|
|
18738
19211
|
if (initial) setSceneConfig(initial);
|
|
18739
19212
|
}, []);
|
|
19213
|
+
const updateLight = useCallback13(
|
|
19214
|
+
(id, patch) => {
|
|
19215
|
+
updateSceneConfig((current) => ({
|
|
19216
|
+
lighting: {
|
|
19217
|
+
lights: current.lighting.lights.map(
|
|
19218
|
+
(light) => light.id === id ? { ...light, ...patch } : light
|
|
19219
|
+
)
|
|
19220
|
+
}
|
|
19221
|
+
}));
|
|
19222
|
+
},
|
|
19223
|
+
[updateSceneConfig]
|
|
19224
|
+
);
|
|
19225
|
+
const addLight = useCallback13(
|
|
19226
|
+
(light) => {
|
|
19227
|
+
updateSceneConfig((current) => {
|
|
19228
|
+
if (current.lighting.lights.some((item) => item.id === light.id)) {
|
|
19229
|
+
return {};
|
|
19230
|
+
}
|
|
19231
|
+
return {
|
|
19232
|
+
lighting: {
|
|
19233
|
+
lights: [
|
|
19234
|
+
...current.lighting.lights,
|
|
19235
|
+
{ ...DEFAULT_LIGHT, ...light }
|
|
19236
|
+
]
|
|
19237
|
+
}
|
|
19238
|
+
};
|
|
19239
|
+
});
|
|
19240
|
+
},
|
|
19241
|
+
[updateSceneConfig]
|
|
19242
|
+
);
|
|
19243
|
+
const removeLight = useCallback13(
|
|
19244
|
+
(id) => {
|
|
19245
|
+
updateSceneConfig((current) => ({
|
|
19246
|
+
lighting: {
|
|
19247
|
+
lights: current.lighting.lights.filter((light) => light.id !== id)
|
|
19248
|
+
}
|
|
19249
|
+
}));
|
|
19250
|
+
},
|
|
19251
|
+
[updateSceneConfig]
|
|
19252
|
+
);
|
|
19253
|
+
const setLightIntensity = useCallback13(
|
|
19254
|
+
(id, intensity) => updateLight(id, { intensity }),
|
|
19255
|
+
[updateLight]
|
|
19256
|
+
);
|
|
19257
|
+
const setLightColor = useCallback13(
|
|
19258
|
+
(id, color) => updateLight(id, { color }),
|
|
19259
|
+
[updateLight]
|
|
19260
|
+
);
|
|
19261
|
+
const showLight = useCallback13(
|
|
19262
|
+
(id) => updateLight(id, { visible: true }),
|
|
19263
|
+
[updateLight]
|
|
19264
|
+
);
|
|
19265
|
+
const hideLight = useCallback13(
|
|
19266
|
+
(id) => updateLight(id, { visible: false }),
|
|
19267
|
+
[updateLight]
|
|
19268
|
+
);
|
|
19269
|
+
const toggleLight = useCallback13(
|
|
19270
|
+
(id) => {
|
|
19271
|
+
updateSceneConfig((current) => ({
|
|
19272
|
+
lighting: {
|
|
19273
|
+
lights: current.lighting.lights.map(
|
|
19274
|
+
(light) => light.id === id ? { ...light, visible: !light.visible } : light
|
|
19275
|
+
)
|
|
19276
|
+
}
|
|
19277
|
+
}));
|
|
19278
|
+
},
|
|
19279
|
+
[updateSceneConfig]
|
|
19280
|
+
);
|
|
19281
|
+
const attachLight = useCallback13(
|
|
19282
|
+
(id, attachment) => updateLight(id, { attachment, attachedToCamera: false }),
|
|
19283
|
+
[updateLight]
|
|
19284
|
+
);
|
|
19285
|
+
const detachLight = useCallback13(
|
|
19286
|
+
(id) => updateLight(id, { attachment: void 0 }),
|
|
19287
|
+
[updateLight]
|
|
19288
|
+
);
|
|
19289
|
+
const lights = useMemo22(
|
|
19290
|
+
() => ({
|
|
19291
|
+
items: sceneConfig.lighting.lights,
|
|
19292
|
+
get: (id) => sceneConfig.lighting.lights.find((light) => light.id === id),
|
|
19293
|
+
add: addLight,
|
|
19294
|
+
update: updateLight,
|
|
19295
|
+
remove: removeLight,
|
|
19296
|
+
setIntensity: setLightIntensity,
|
|
19297
|
+
setColor: setLightColor,
|
|
19298
|
+
show: showLight,
|
|
19299
|
+
hide: hideLight,
|
|
19300
|
+
toggle: toggleLight,
|
|
19301
|
+
attach: attachLight,
|
|
19302
|
+
detach: detachLight
|
|
19303
|
+
}),
|
|
19304
|
+
[
|
|
19305
|
+
sceneConfig.lighting.lights,
|
|
19306
|
+
addLight,
|
|
19307
|
+
updateLight,
|
|
19308
|
+
removeLight,
|
|
19309
|
+
setLightIntensity,
|
|
19310
|
+
setLightColor,
|
|
19311
|
+
showLight,
|
|
19312
|
+
hideLight,
|
|
19313
|
+
toggleLight,
|
|
19314
|
+
attachLight,
|
|
19315
|
+
detachLight
|
|
19316
|
+
]
|
|
19317
|
+
);
|
|
18740
19318
|
return {
|
|
18741
19319
|
sceneConfig,
|
|
18742
19320
|
setSceneConfig,
|
|
18743
19321
|
updateSceneConfig,
|
|
18744
|
-
resetSceneConfig
|
|
19322
|
+
resetSceneConfig,
|
|
19323
|
+
lights
|
|
18745
19324
|
};
|
|
18746
19325
|
}
|
|
18747
19326
|
|
|
18748
19327
|
// src/hooks/useViewerHover.ts
|
|
18749
|
-
import { useCallback as useCallback14, useState as
|
|
19328
|
+
import { useCallback as useCallback14, useState as useState32 } from "react";
|
|
18750
19329
|
function useViewerHover() {
|
|
18751
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] =
|
|
19330
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = useState32(null);
|
|
18752
19331
|
const handleHoveredObject = useCallback14(
|
|
18753
19332
|
(binding) => {
|
|
18754
19333
|
setHoveredObjectBinding(binding);
|
|
@@ -18762,23 +19341,23 @@ function useViewerHover() {
|
|
|
18762
19341
|
}
|
|
18763
19342
|
|
|
18764
19343
|
// src/hooks/useViewerModel.ts
|
|
18765
|
-
import { useCallback as useCallback15, useState as
|
|
18766
|
-
import { Box3 as Box36, Vector3 as
|
|
19344
|
+
import { useCallback as useCallback15, useState as useState33 } from "react";
|
|
19345
|
+
import { Box3 as Box36, Vector3 as Vector312 } from "three";
|
|
18767
19346
|
function toTuple2(vector) {
|
|
18768
19347
|
return [vector.x, vector.y, vector.z];
|
|
18769
19348
|
}
|
|
18770
19349
|
function useViewerModel() {
|
|
18771
|
-
const [isLoading, setIsLoading] =
|
|
18772
|
-
const [isReady, setIsReady] =
|
|
18773
|
-
const [error, setError] =
|
|
18774
|
-
const [bounds, setBounds] =
|
|
18775
|
-
const [objectCount, setObjectCount] =
|
|
19350
|
+
const [isLoading, setIsLoading] = useState33(true);
|
|
19351
|
+
const [isReady, setIsReady] = useState33(false);
|
|
19352
|
+
const [error, setError] = useState33(null);
|
|
19353
|
+
const [bounds, setBounds] = useState33(null);
|
|
19354
|
+
const [objectCount, setObjectCount] = useState33(0);
|
|
18776
19355
|
const handleModelLoaded = useCallback15((scene) => {
|
|
18777
19356
|
const box = new Box36().setFromObject(scene);
|
|
18778
19357
|
const min = box.min.clone();
|
|
18779
19358
|
const max = box.max.clone();
|
|
18780
|
-
const center = new
|
|
18781
|
-
const size = new
|
|
19359
|
+
const center = new Vector312();
|
|
19360
|
+
const size = new Vector312();
|
|
18782
19361
|
let nextObjectCount = 0;
|
|
18783
19362
|
scene.traverse((child) => {
|
|
18784
19363
|
if ("isMesh" in child && child.isMesh) {
|
|
@@ -18826,61 +19405,777 @@ function useViewerModel() {
|
|
|
18826
19405
|
|
|
18827
19406
|
// src/hooks/useViewerActions.ts
|
|
18828
19407
|
import { useCallback as useCallback16 } from "react";
|
|
18829
|
-
function useViewerActions(
|
|
18830
|
-
|
|
19408
|
+
function useViewerActions({
|
|
19409
|
+
bindings,
|
|
19410
|
+
scene,
|
|
19411
|
+
camera,
|
|
19412
|
+
animations,
|
|
19413
|
+
onAction
|
|
19414
|
+
}) {
|
|
19415
|
+
const {
|
|
19416
|
+
objectBindings,
|
|
19417
|
+
updateObjectBindings,
|
|
19418
|
+
showObject,
|
|
19419
|
+
hideObject,
|
|
19420
|
+
toggleObjectVisibility
|
|
19421
|
+
} = bindings;
|
|
19422
|
+
const updateSceneConfig = scene?.updateSceneConfig;
|
|
19423
|
+
const focusObject = camera?.focusObject;
|
|
19424
|
+
const fitScene = camera?.fitScene;
|
|
19425
|
+
const resetView = camera?.resetView;
|
|
19426
|
+
const setCameraTarget = camera?.setCameraTarget;
|
|
19427
|
+
const setCameraState = camera?.setCameraState;
|
|
19428
|
+
const playAnimation = animations?.play;
|
|
19429
|
+
const pauseAnimation = animations?.pause;
|
|
19430
|
+
const stopAnimation = animations?.stop;
|
|
19431
|
+
const setAnimationSpeed = animations?.setSpeed;
|
|
19432
|
+
const seekAnimation = animations?.seek;
|
|
18831
19433
|
const getActionsForObject = useCallback16(
|
|
18832
19434
|
(objectId) => {
|
|
18833
19435
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
18834
|
-
|
|
18835
|
-
return [];
|
|
18836
|
-
}
|
|
18837
|
-
return objectBindings[bindingKey]?.actions ?? [];
|
|
19436
|
+
return bindingKey ? objectBindings[bindingKey]?.actions ?? [] : [];
|
|
18838
19437
|
},
|
|
18839
19438
|
[objectBindings]
|
|
18840
19439
|
);
|
|
19440
|
+
const handleAction = useCallback16(
|
|
19441
|
+
async (event) => {
|
|
19442
|
+
for (const effect of event.action.effects ?? []) {
|
|
19443
|
+
if ("objectBindings" in effect) {
|
|
19444
|
+
updateObjectBindings(effect.target, effect.objectBindings);
|
|
19445
|
+
continue;
|
|
19446
|
+
}
|
|
19447
|
+
if ("sceneConfig" in effect) {
|
|
19448
|
+
updateSceneConfig?.(effect.sceneConfig);
|
|
19449
|
+
continue;
|
|
19450
|
+
}
|
|
19451
|
+
if ("visibility" in effect) {
|
|
19452
|
+
if (effect.visibility === "show") showObject(effect.target);
|
|
19453
|
+
else if (effect.visibility === "hide") hideObject(effect.target);
|
|
19454
|
+
else toggleObjectVisibility(effect.target);
|
|
19455
|
+
continue;
|
|
19456
|
+
}
|
|
19457
|
+
if ("camera" in effect) {
|
|
19458
|
+
const action2 = effect.camera;
|
|
19459
|
+
if (action2.type === "focus-object") {
|
|
19460
|
+
await focusObject?.(action2.objectId ?? event.objectId);
|
|
19461
|
+
} else if (action2.type === "fit-scene") {
|
|
19462
|
+
await fitScene?.();
|
|
19463
|
+
} else if (action2.type === "reset") {
|
|
19464
|
+
await resetView?.();
|
|
19465
|
+
} else if (action2.type === "set-target") {
|
|
19466
|
+
await setCameraTarget?.(action2.target, action2.transition ?? true);
|
|
19467
|
+
} else {
|
|
19468
|
+
await setCameraState?.(action2.state, action2.transition ?? true);
|
|
19469
|
+
}
|
|
19470
|
+
continue;
|
|
19471
|
+
}
|
|
19472
|
+
const action = effect.animation;
|
|
19473
|
+
if (action.type === "play") playAnimation?.(action.clip);
|
|
19474
|
+
else if (action.type === "pause") pauseAnimation?.();
|
|
19475
|
+
else if (action.type === "stop") stopAnimation?.();
|
|
19476
|
+
else if (action.type === "set-speed") {
|
|
19477
|
+
setAnimationSpeed?.(action.speed);
|
|
19478
|
+
} else {
|
|
19479
|
+
seekAnimation?.(action.time);
|
|
19480
|
+
}
|
|
19481
|
+
}
|
|
19482
|
+
await onAction?.(event);
|
|
19483
|
+
},
|
|
19484
|
+
[
|
|
19485
|
+
fitScene,
|
|
19486
|
+
focusObject,
|
|
19487
|
+
hideObject,
|
|
19488
|
+
onAction,
|
|
19489
|
+
pauseAnimation,
|
|
19490
|
+
playAnimation,
|
|
19491
|
+
resetView,
|
|
19492
|
+
seekAnimation,
|
|
19493
|
+
setAnimationSpeed,
|
|
19494
|
+
setCameraState,
|
|
19495
|
+
setCameraTarget,
|
|
19496
|
+
showObject,
|
|
19497
|
+
stopAnimation,
|
|
19498
|
+
toggleObjectVisibility,
|
|
19499
|
+
updateObjectBindings,
|
|
19500
|
+
updateSceneConfig
|
|
19501
|
+
]
|
|
19502
|
+
);
|
|
18841
19503
|
const runAction = useCallback16(
|
|
18842
|
-
(objectId, actionId) => {
|
|
19504
|
+
async (objectId, actionId) => {
|
|
18843
19505
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
18844
|
-
if (!bindingKey)
|
|
18845
|
-
return null;
|
|
18846
|
-
}
|
|
19506
|
+
if (!bindingKey) return null;
|
|
18847
19507
|
const binding = objectBindings[bindingKey];
|
|
18848
|
-
|
|
18849
|
-
|
|
18850
|
-
}
|
|
18851
|
-
const action = (binding.actions ?? []).find(
|
|
18852
|
-
(entry) => entry.id === actionId
|
|
18853
|
-
);
|
|
18854
|
-
if (!action) {
|
|
18855
|
-
return null;
|
|
18856
|
-
}
|
|
19508
|
+
const action = binding?.actions?.find((entry) => entry.id === actionId);
|
|
19509
|
+
if (!binding || !action) return null;
|
|
18857
19510
|
const event = {
|
|
18858
19511
|
objectId: bindingKey,
|
|
18859
19512
|
action,
|
|
18860
19513
|
binding
|
|
18861
19514
|
};
|
|
18862
|
-
|
|
18863
|
-
onObjectBindingsChange({
|
|
18864
|
-
...objectBindings,
|
|
18865
|
-
[bindingKey]: {
|
|
18866
|
-
...binding,
|
|
18867
|
-
visible: binding.visible === false
|
|
18868
|
-
}
|
|
18869
|
-
});
|
|
18870
|
-
}
|
|
18871
|
-
onAction?.(event);
|
|
19515
|
+
await handleAction(event);
|
|
18872
19516
|
return event;
|
|
18873
19517
|
},
|
|
18874
|
-
[
|
|
19518
|
+
[handleAction, objectBindings]
|
|
18875
19519
|
);
|
|
18876
19520
|
return {
|
|
18877
19521
|
getActionsForObject,
|
|
19522
|
+
handleAction,
|
|
18878
19523
|
runAction
|
|
18879
19524
|
};
|
|
18880
19525
|
}
|
|
18881
19526
|
|
|
19527
|
+
// src/hooks/useViewerConnection.ts
|
|
19528
|
+
import { useCallback as useCallback17, useRef as useRef22 } from "react";
|
|
19529
|
+
function getReadyHandler(consumer) {
|
|
19530
|
+
if (typeof consumer === "function") {
|
|
19531
|
+
return consumer;
|
|
19532
|
+
}
|
|
19533
|
+
return consumer?.handleViewerReady ?? null;
|
|
19534
|
+
}
|
|
19535
|
+
function useViewerConnection(...consumers) {
|
|
19536
|
+
const viewerRef = useRef22(null);
|
|
19537
|
+
const consumersRef = useRef22(consumers);
|
|
19538
|
+
consumersRef.current = consumers;
|
|
19539
|
+
const handleViewerReady = useCallback17((viewer) => {
|
|
19540
|
+
viewerRef.current = viewer;
|
|
19541
|
+
const invoked = /* @__PURE__ */ new Set();
|
|
19542
|
+
for (const consumer of consumersRef.current) {
|
|
19543
|
+
const handler = getReadyHandler(consumer);
|
|
19544
|
+
if (handler && !invoked.has(handler)) {
|
|
19545
|
+
invoked.add(handler);
|
|
19546
|
+
handler(viewer);
|
|
19547
|
+
}
|
|
19548
|
+
}
|
|
19549
|
+
}, []);
|
|
19550
|
+
const getViewer = useCallback17(() => viewerRef.current, []);
|
|
19551
|
+
const captureImage2 = useCallback17((options) => {
|
|
19552
|
+
const viewer = viewerRef.current;
|
|
19553
|
+
if (!viewer) {
|
|
19554
|
+
return Promise.reject(new Error("ModelViewer is not ready yet."));
|
|
19555
|
+
}
|
|
19556
|
+
return viewer.captureImage(options);
|
|
19557
|
+
}, []);
|
|
19558
|
+
const invalidate = useCallback17(() => {
|
|
19559
|
+
const viewer = viewerRef.current;
|
|
19560
|
+
if (!viewer) {
|
|
19561
|
+
return false;
|
|
19562
|
+
}
|
|
19563
|
+
viewer.invalidate();
|
|
19564
|
+
return true;
|
|
19565
|
+
}, []);
|
|
19566
|
+
return {
|
|
19567
|
+
handleViewerReady,
|
|
19568
|
+
getViewer,
|
|
19569
|
+
captureImage: captureImage2,
|
|
19570
|
+
invalidate
|
|
19571
|
+
};
|
|
19572
|
+
}
|
|
19573
|
+
|
|
19574
|
+
// src/hooks/useViewerEffects.ts
|
|
19575
|
+
import { useCallback as useCallback18, useEffect as useEffect25, useRef as useRef23 } from "react";
|
|
19576
|
+
import {
|
|
19577
|
+
Material as Material3,
|
|
19578
|
+
PointLight,
|
|
19579
|
+
Vector3 as Vector313
|
|
19580
|
+
} from "three";
|
|
19581
|
+
var NUMERIC_MATERIAL_KEYS = [
|
|
19582
|
+
"emissiveIntensity",
|
|
19583
|
+
"opacity",
|
|
19584
|
+
"metalness",
|
|
19585
|
+
"roughness",
|
|
19586
|
+
"transmission",
|
|
19587
|
+
"clearcoat",
|
|
19588
|
+
"clearcoatRoughness",
|
|
19589
|
+
"ior",
|
|
19590
|
+
"thickness"
|
|
19591
|
+
];
|
|
19592
|
+
var nextEffectId = 1;
|
|
19593
|
+
function stringList(target) {
|
|
19594
|
+
return typeof target === "string" ? [target] : target;
|
|
19595
|
+
}
|
|
19596
|
+
function clamp2(value, min = 0, max = 1) {
|
|
19597
|
+
return Math.min(max, Math.max(min, value));
|
|
19598
|
+
}
|
|
19599
|
+
function randomBetween(min, max) {
|
|
19600
|
+
return min + Math.random() * (max - min);
|
|
19601
|
+
}
|
|
19602
|
+
function interpolate([from, to], level) {
|
|
19603
|
+
return from + (to - from) * level;
|
|
19604
|
+
}
|
|
19605
|
+
function interpolateVector([from, to], level) {
|
|
19606
|
+
return [
|
|
19607
|
+
from[0] + (to[0] - from[0]) * level,
|
|
19608
|
+
from[1] + (to[1] - from[1]) * level,
|
|
19609
|
+
from[2] + (to[2] - from[2]) * level
|
|
19610
|
+
];
|
|
19611
|
+
}
|
|
19612
|
+
function ease(value, easing) {
|
|
19613
|
+
const t = clamp2(value);
|
|
19614
|
+
switch (easing) {
|
|
19615
|
+
case "ease-in":
|
|
19616
|
+
return t * t;
|
|
19617
|
+
case "ease-out":
|
|
19618
|
+
return 1 - (1 - t) * (1 - t);
|
|
19619
|
+
case "ease-in-out":
|
|
19620
|
+
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
|
19621
|
+
default:
|
|
19622
|
+
return t;
|
|
19623
|
+
}
|
|
19624
|
+
}
|
|
19625
|
+
function materialList(node) {
|
|
19626
|
+
const value = node.material;
|
|
19627
|
+
if (!value) return [];
|
|
19628
|
+
return (Array.isArray(value) ? value : [value]).filter(
|
|
19629
|
+
(entry) => entry instanceof Material3
|
|
19630
|
+
);
|
|
19631
|
+
}
|
|
19632
|
+
function readMaterial(material2) {
|
|
19633
|
+
const snapshot = {
|
|
19634
|
+
opacity: material2.opacity,
|
|
19635
|
+
transparent: material2.transparent
|
|
19636
|
+
};
|
|
19637
|
+
if (material2.color) snapshot.baseColor = `#${material2.color.getHexString()}`;
|
|
19638
|
+
if (material2.emissive) snapshot.emissive = `#${material2.emissive.getHexString()}`;
|
|
19639
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19640
|
+
const value = material2[key];
|
|
19641
|
+
if (typeof value === "number") snapshot[key] = value;
|
|
19642
|
+
}
|
|
19643
|
+
return snapshot;
|
|
19644
|
+
}
|
|
19645
|
+
function applyMaterial(material2, patch) {
|
|
19646
|
+
if (patch.baseColor !== void 0 && material2.color) {
|
|
19647
|
+
material2.color.set(patch.baseColor);
|
|
19648
|
+
}
|
|
19649
|
+
if (patch.emissive !== void 0 && material2.emissive) {
|
|
19650
|
+
material2.emissive.set(patch.emissive);
|
|
19651
|
+
}
|
|
19652
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19653
|
+
const value = patch[key];
|
|
19654
|
+
if (value !== void 0 && typeof material2[key] === "number") {
|
|
19655
|
+
material2[key] = value;
|
|
19656
|
+
}
|
|
19657
|
+
}
|
|
19658
|
+
if (patch.transparent !== void 0) material2.transparent = patch.transparent;
|
|
19659
|
+
material2.needsUpdate = true;
|
|
19660
|
+
}
|
|
19661
|
+
function readTransform(node) {
|
|
19662
|
+
return {
|
|
19663
|
+
position: [node.position.x, node.position.y, node.position.z],
|
|
19664
|
+
rotation: [node.rotation.x, node.rotation.y, node.rotation.z],
|
|
19665
|
+
scale: [node.scale.x, node.scale.y, node.scale.z],
|
|
19666
|
+
visible: node.visible
|
|
19667
|
+
};
|
|
19668
|
+
}
|
|
19669
|
+
function applyTransform(node, patch) {
|
|
19670
|
+
if (patch.position) node.position.set(...patch.position);
|
|
19671
|
+
if (patch.rotation) node.rotation.set(...patch.rotation);
|
|
19672
|
+
if (patch.scale !== void 0) {
|
|
19673
|
+
const scale = typeof patch.scale === "number" ? [patch.scale, patch.scale, patch.scale] : patch.scale;
|
|
19674
|
+
node.scale.set(...scale);
|
|
19675
|
+
}
|
|
19676
|
+
if (patch.visible !== void 0) node.visible = patch.visible;
|
|
19677
|
+
node.updateMatrix();
|
|
19678
|
+
node.updateMatrixWorld(true);
|
|
19679
|
+
}
|
|
19680
|
+
function useViewerEffects(options = {}) {
|
|
19681
|
+
const optionsRef = useRef23(options);
|
|
19682
|
+
optionsRef.current = options;
|
|
19683
|
+
const viewerRef = useRef23(null);
|
|
19684
|
+
const sceneRef = useRef23(null);
|
|
19685
|
+
const frameRef = useRef23(null);
|
|
19686
|
+
const effectsRef = useRef23(/* @__PURE__ */ new Map());
|
|
19687
|
+
const lightsRef = useRef23(/* @__PURE__ */ new Map());
|
|
19688
|
+
const materialSnapshotsRef = useRef23(
|
|
19689
|
+
/* @__PURE__ */ new Map()
|
|
19690
|
+
);
|
|
19691
|
+
const transformSnapshotsRef = useRef23(/* @__PURE__ */ new Map());
|
|
19692
|
+
const resolveObject = useCallback18((target) => {
|
|
19693
|
+
const viewer = viewerRef.current;
|
|
19694
|
+
if (!viewer) return null;
|
|
19695
|
+
const bindingKey = resolveBindingKey(viewer.objectBindings, target);
|
|
19696
|
+
return viewer.nodeRefs[target] ?? (bindingKey ? viewer.nodeRefs[bindingKey] : null) ?? viewer.scene?.getObjectByName(target) ?? null;
|
|
19697
|
+
}, []);
|
|
19698
|
+
const resolveTargets = useCallback18(
|
|
19699
|
+
(target) => {
|
|
19700
|
+
const viewer = viewerRef.current;
|
|
19701
|
+
if (!viewer) {
|
|
19702
|
+
return typeof target === "string" ? [target] : Array.isArray(target) ? target : [];
|
|
19703
|
+
}
|
|
19704
|
+
if (typeof target === "object" && !Array.isArray(target)) {
|
|
19705
|
+
return selectBindingKeys(viewer.objectBindings, target);
|
|
19706
|
+
}
|
|
19707
|
+
return stringList(target).map(
|
|
19708
|
+
(id) => resolveBindingKey(viewer.objectBindings, id) ?? id
|
|
19709
|
+
);
|
|
19710
|
+
},
|
|
19711
|
+
[]
|
|
19712
|
+
);
|
|
19713
|
+
const invalidate = useCallback18(() => {
|
|
19714
|
+
viewerRef.current?.invalidate();
|
|
19715
|
+
}, []);
|
|
19716
|
+
const setMaterialInternal = useCallback18(
|
|
19717
|
+
(target, patch) => {
|
|
19718
|
+
let changed = false;
|
|
19719
|
+
for (const id of resolveTargets(target)) {
|
|
19720
|
+
const node = resolveObject(id);
|
|
19721
|
+
if (!node) continue;
|
|
19722
|
+
for (const entry of materialList(node)) {
|
|
19723
|
+
if (!materialSnapshotsRef.current.has(entry)) {
|
|
19724
|
+
materialSnapshotsRef.current.set(entry, readMaterial(entry));
|
|
19725
|
+
}
|
|
19726
|
+
applyMaterial(entry, patch);
|
|
19727
|
+
changed = true;
|
|
19728
|
+
}
|
|
19729
|
+
}
|
|
19730
|
+
return changed;
|
|
19731
|
+
},
|
|
19732
|
+
[resolveObject, resolveTargets]
|
|
19733
|
+
);
|
|
19734
|
+
const setMaterial = useCallback18(
|
|
19735
|
+
(target, patch) => {
|
|
19736
|
+
const changed = setMaterialInternal(target, patch);
|
|
19737
|
+
if (changed) invalidate();
|
|
19738
|
+
return changed;
|
|
19739
|
+
},
|
|
19740
|
+
[invalidate, setMaterialInternal]
|
|
19741
|
+
);
|
|
19742
|
+
const setTransformInternal = useCallback18(
|
|
19743
|
+
(target, patch) => {
|
|
19744
|
+
let changed = false;
|
|
19745
|
+
for (const id of resolveTargets(target)) {
|
|
19746
|
+
const node = resolveObject(id);
|
|
19747
|
+
if (!node) continue;
|
|
19748
|
+
if (!transformSnapshotsRef.current.has(node)) {
|
|
19749
|
+
transformSnapshotsRef.current.set(node, readTransform(node));
|
|
19750
|
+
}
|
|
19751
|
+
applyTransform(node, patch);
|
|
19752
|
+
changed = true;
|
|
19753
|
+
}
|
|
19754
|
+
return changed;
|
|
19755
|
+
},
|
|
19756
|
+
[resolveObject, resolveTargets]
|
|
19757
|
+
);
|
|
19758
|
+
const setTransform = useCallback18(
|
|
19759
|
+
(target, patch) => {
|
|
19760
|
+
const changed = setTransformInternal(target, patch);
|
|
19761
|
+
if (changed) invalidate();
|
|
19762
|
+
return changed;
|
|
19763
|
+
},
|
|
19764
|
+
[invalidate, setTransformInternal]
|
|
19765
|
+
);
|
|
19766
|
+
const getWorldPosition = useCallback18(
|
|
19767
|
+
(target) => {
|
|
19768
|
+
const node = resolveObject(target);
|
|
19769
|
+
if (!node) return null;
|
|
19770
|
+
node.updateWorldMatrix(true, false);
|
|
19771
|
+
const position = node.getWorldPosition(new Vector313());
|
|
19772
|
+
return [position.x, position.y, position.z];
|
|
19773
|
+
},
|
|
19774
|
+
[resolveObject]
|
|
19775
|
+
);
|
|
19776
|
+
const removePointLight = useCallback18(
|
|
19777
|
+
(id) => {
|
|
19778
|
+
const runtime = lightsRef.current.get(id);
|
|
19779
|
+
if (!runtime) return false;
|
|
19780
|
+
runtime.light.removeFromParent();
|
|
19781
|
+
runtime.light.dispose();
|
|
19782
|
+
lightsRef.current.delete(id);
|
|
19783
|
+
invalidate();
|
|
19784
|
+
return true;
|
|
19785
|
+
},
|
|
19786
|
+
[invalidate]
|
|
19787
|
+
);
|
|
19788
|
+
const addPointLight = useCallback18(
|
|
19789
|
+
(lightOptions) => {
|
|
19790
|
+
const target = resolveObject(lightOptions.target);
|
|
19791
|
+
if (!target) return false;
|
|
19792
|
+
const existing = lightsRef.current.get(lightOptions.id);
|
|
19793
|
+
const light = existing?.light ?? new PointLight();
|
|
19794
|
+
if (existing && existing.target !== lightOptions.target) {
|
|
19795
|
+
light.removeFromParent();
|
|
19796
|
+
}
|
|
19797
|
+
light.name = `react-immersive-runtime-light:${lightOptions.id}`;
|
|
19798
|
+
light.color.set(lightOptions.color ?? "#ffffff");
|
|
19799
|
+
light.intensity = lightOptions.intensity ?? 1;
|
|
19800
|
+
light.distance = lightOptions.distance ?? 0;
|
|
19801
|
+
light.decay = lightOptions.decay ?? 2;
|
|
19802
|
+
light.castShadow = lightOptions.castShadow ?? false;
|
|
19803
|
+
light.position.set(...lightOptions.offset ?? [0, 0, 0]);
|
|
19804
|
+
if (light.parent !== target) target.add(light);
|
|
19805
|
+
lightsRef.current.set(lightOptions.id, {
|
|
19806
|
+
light,
|
|
19807
|
+
target: lightOptions.target
|
|
19808
|
+
});
|
|
19809
|
+
invalidate();
|
|
19810
|
+
return true;
|
|
19811
|
+
},
|
|
19812
|
+
[invalidate, resolveObject]
|
|
19813
|
+
);
|
|
19814
|
+
const updatePointLight = useCallback18(
|
|
19815
|
+
(id, patch) => {
|
|
19816
|
+
const runtime = lightsRef.current.get(id);
|
|
19817
|
+
if (!runtime) return false;
|
|
19818
|
+
if (patch.target && patch.target !== runtime.target) {
|
|
19819
|
+
const target = resolveObject(patch.target);
|
|
19820
|
+
if (!target) return false;
|
|
19821
|
+
runtime.light.removeFromParent();
|
|
19822
|
+
target.add(runtime.light);
|
|
19823
|
+
runtime.target = patch.target;
|
|
19824
|
+
}
|
|
19825
|
+
if (patch.color !== void 0) runtime.light.color.set(patch.color);
|
|
19826
|
+
if (patch.intensity !== void 0) {
|
|
19827
|
+
runtime.light.intensity = patch.intensity;
|
|
19828
|
+
}
|
|
19829
|
+
if (patch.distance !== void 0) runtime.light.distance = patch.distance;
|
|
19830
|
+
if (patch.decay !== void 0) runtime.light.decay = patch.decay;
|
|
19831
|
+
if (patch.offset !== void 0) {
|
|
19832
|
+
runtime.light.position.set(...patch.offset);
|
|
19833
|
+
}
|
|
19834
|
+
if (patch.castShadow !== void 0) {
|
|
19835
|
+
runtime.light.castShadow = patch.castShadow;
|
|
19836
|
+
}
|
|
19837
|
+
invalidate();
|
|
19838
|
+
return true;
|
|
19839
|
+
},
|
|
19840
|
+
[invalidate, resolveObject]
|
|
19841
|
+
);
|
|
19842
|
+
const applyChannels = useCallback18(
|
|
19843
|
+
(channels, levels) => {
|
|
19844
|
+
const levelFor = (index) => levels[index % levels.length] ?? 0;
|
|
19845
|
+
for (const channel of channels.materials ?? []) {
|
|
19846
|
+
resolveTargets(channel.targets).forEach((target, index) => {
|
|
19847
|
+
const level = levelFor(index);
|
|
19848
|
+
const patch = {};
|
|
19849
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19850
|
+
const range = channel.values[key];
|
|
19851
|
+
if (range) patch[key] = interpolate(range, level);
|
|
19852
|
+
}
|
|
19853
|
+
setMaterialInternal(target, patch);
|
|
19854
|
+
});
|
|
19855
|
+
}
|
|
19856
|
+
for (const channel of channels.transforms ?? []) {
|
|
19857
|
+
resolveTargets(channel.targets).forEach((target, index) => {
|
|
19858
|
+
const level = levelFor(index);
|
|
19859
|
+
const patch = {};
|
|
19860
|
+
if (channel.values.position) {
|
|
19861
|
+
patch.position = interpolateVector(channel.values.position, level);
|
|
19862
|
+
}
|
|
19863
|
+
if (channel.values.rotation) {
|
|
19864
|
+
patch.rotation = interpolateVector(channel.values.rotation, level);
|
|
19865
|
+
}
|
|
19866
|
+
if (channel.values.scale) {
|
|
19867
|
+
patch.scale = interpolateVector(channel.values.scale, level);
|
|
19868
|
+
}
|
|
19869
|
+
setTransformInternal(target, patch);
|
|
19870
|
+
});
|
|
19871
|
+
}
|
|
19872
|
+
for (const channel of channels.lights ?? []) {
|
|
19873
|
+
stringList(channel.lights).forEach((id, index) => {
|
|
19874
|
+
const runtime = lightsRef.current.get(id);
|
|
19875
|
+
if (runtime) {
|
|
19876
|
+
runtime.light.intensity = interpolate(
|
|
19877
|
+
channel.intensity,
|
|
19878
|
+
levelFor(index)
|
|
19879
|
+
);
|
|
19880
|
+
}
|
|
19881
|
+
});
|
|
19882
|
+
}
|
|
19883
|
+
},
|
|
19884
|
+
[resolveTargets, setMaterialInternal, setTransformInternal]
|
|
19885
|
+
);
|
|
19886
|
+
const scheduleFrameRef = useRef23(() => void 0);
|
|
19887
|
+
const runFrameRef = useRef23(() => void 0);
|
|
19888
|
+
runFrameRef.current = (now) => {
|
|
19889
|
+
frameRef.current = null;
|
|
19890
|
+
let rendered = false;
|
|
19891
|
+
for (const [id, effect] of effectsRef.current) {
|
|
19892
|
+
if (effect.startedAt === null) effect.startedAt = now;
|
|
19893
|
+
const previous = effect.lastFrameAt ?? now;
|
|
19894
|
+
effect.lastFrameAt = now;
|
|
19895
|
+
const override = effect.override && effect.override.until > now ? effect.override.level : null;
|
|
19896
|
+
if (effect.override && override === null) effect.override = null;
|
|
19897
|
+
const keepRunning = effect.update(
|
|
19898
|
+
now,
|
|
19899
|
+
now - effect.startedAt,
|
|
19900
|
+
Math.min(100, now - previous),
|
|
19901
|
+
override
|
|
19902
|
+
);
|
|
19903
|
+
rendered = true;
|
|
19904
|
+
if (!keepRunning) {
|
|
19905
|
+
effectsRef.current.delete(id);
|
|
19906
|
+
effect.onComplete?.();
|
|
19907
|
+
}
|
|
19908
|
+
}
|
|
19909
|
+
if (rendered) invalidate();
|
|
19910
|
+
if (effectsRef.current.size > 0) scheduleFrameRef.current();
|
|
19911
|
+
};
|
|
19912
|
+
scheduleFrameRef.current = () => {
|
|
19913
|
+
if (frameRef.current !== null || typeof requestAnimationFrame === "undefined") {
|
|
19914
|
+
return;
|
|
19915
|
+
}
|
|
19916
|
+
frameRef.current = requestAnimationFrame((now) => runFrameRef.current(now));
|
|
19917
|
+
};
|
|
19918
|
+
const registerEffect = useCallback18((effect) => {
|
|
19919
|
+
effectsRef.current.set(effect.id, effect);
|
|
19920
|
+
scheduleFrameRef.current();
|
|
19921
|
+
return effect.id;
|
|
19922
|
+
}, []);
|
|
19923
|
+
const transition = useCallback18(
|
|
19924
|
+
(channels, transitionOptions = {}) => {
|
|
19925
|
+
const id = transitionOptions.id ?? `transition-${nextEffectId++}`;
|
|
19926
|
+
const duration = Math.max(1, transitionOptions.duration ?? 400);
|
|
19927
|
+
const easing = transitionOptions.easing ?? "ease-in-out";
|
|
19928
|
+
return registerEffect({
|
|
19929
|
+
id,
|
|
19930
|
+
startedAt: null,
|
|
19931
|
+
lastFrameAt: null,
|
|
19932
|
+
override: null,
|
|
19933
|
+
onComplete: transitionOptions.onComplete,
|
|
19934
|
+
update: (_now, elapsed, _delta, override) => {
|
|
19935
|
+
const progress = override ?? ease(elapsed / duration, easing);
|
|
19936
|
+
applyChannels(channels, [progress]);
|
|
19937
|
+
return elapsed < duration;
|
|
19938
|
+
}
|
|
19939
|
+
});
|
|
19940
|
+
},
|
|
19941
|
+
[applyChannels, registerEffect]
|
|
19942
|
+
);
|
|
19943
|
+
const pulse = useCallback18(
|
|
19944
|
+
(channels, pulseOptions = {}) => {
|
|
19945
|
+
const id = pulseOptions.id ?? `pulse-${nextEffectId++}`;
|
|
19946
|
+
const duration = Math.max(1, pulseOptions.duration ?? 1e3);
|
|
19947
|
+
const minLevel = pulseOptions.minLevel ?? 0;
|
|
19948
|
+
const maxLevel = pulseOptions.maxLevel ?? 1;
|
|
19949
|
+
const repeat = pulseOptions.repeat ?? Infinity;
|
|
19950
|
+
const synchronized = pulseOptions.synchronized ?? true;
|
|
19951
|
+
const unitCount = Math.max(
|
|
19952
|
+
1,
|
|
19953
|
+
...[...channels.materials ?? [], ...channels.transforms ?? []].map(
|
|
19954
|
+
(channel) => resolveTargets(channel.targets).length
|
|
19955
|
+
),
|
|
19956
|
+
...(channels.lights ?? []).map(
|
|
19957
|
+
(channel) => stringList(channel.lights).length
|
|
19958
|
+
)
|
|
19959
|
+
);
|
|
19960
|
+
return registerEffect({
|
|
19961
|
+
id,
|
|
19962
|
+
startedAt: null,
|
|
19963
|
+
lastFrameAt: null,
|
|
19964
|
+
override: null,
|
|
19965
|
+
onComplete: pulseOptions.onComplete,
|
|
19966
|
+
update: (_now, elapsed, _delta, override) => {
|
|
19967
|
+
const cycles = elapsed / duration;
|
|
19968
|
+
const levels = Array.from(
|
|
19969
|
+
{ length: synchronized ? 1 : unitCount },
|
|
19970
|
+
(_, index) => {
|
|
19971
|
+
if (override !== null) return override;
|
|
19972
|
+
const phase = cycles + (synchronized ? 0 : index / unitCount);
|
|
19973
|
+
const wave = 0.5 - Math.cos(phase % 1 * Math.PI * 2) / 2;
|
|
19974
|
+
return minLevel + (maxLevel - minLevel) * wave;
|
|
19975
|
+
}
|
|
19976
|
+
);
|
|
19977
|
+
applyChannels(channels, levels);
|
|
19978
|
+
return repeat === Infinity || cycles < repeat;
|
|
19979
|
+
}
|
|
19980
|
+
});
|
|
19981
|
+
},
|
|
19982
|
+
[applyChannels, registerEffect, resolveTargets]
|
|
19983
|
+
);
|
|
19984
|
+
const timeline = useCallback18(
|
|
19985
|
+
(steps, timelineOptions = {}) => {
|
|
19986
|
+
const id = timelineOptions.id ?? `timeline-${nextEffectId++}`;
|
|
19987
|
+
const repeat = timelineOptions.repeat ?? 1;
|
|
19988
|
+
const normalizedSteps = steps.map((step) => ({
|
|
19989
|
+
...step,
|
|
19990
|
+
duration: Math.max(1, step.duration),
|
|
19991
|
+
hold: Math.max(0, step.hold ?? 0)
|
|
19992
|
+
}));
|
|
19993
|
+
const cycleDuration = Math.max(
|
|
19994
|
+
1,
|
|
19995
|
+
normalizedSteps.reduce(
|
|
19996
|
+
(total, step) => total + step.duration + step.hold,
|
|
19997
|
+
0
|
|
19998
|
+
)
|
|
19999
|
+
);
|
|
20000
|
+
return registerEffect({
|
|
20001
|
+
id,
|
|
20002
|
+
startedAt: null,
|
|
20003
|
+
lastFrameAt: null,
|
|
20004
|
+
override: null,
|
|
20005
|
+
onComplete: timelineOptions.onComplete,
|
|
20006
|
+
update: (_now, elapsed, _delta, override) => {
|
|
20007
|
+
if (normalizedSteps.length === 0 || repeat <= 0) return false;
|
|
20008
|
+
const cycles = elapsed / cycleDuration;
|
|
20009
|
+
if (repeat !== Infinity && cycles >= repeat) {
|
|
20010
|
+
for (const step of normalizedSteps) {
|
|
20011
|
+
applyChannels(step.channels, [1]);
|
|
20012
|
+
}
|
|
20013
|
+
return false;
|
|
20014
|
+
}
|
|
20015
|
+
const cycleTime = elapsed % cycleDuration;
|
|
20016
|
+
let cursor = 0;
|
|
20017
|
+
for (const step of normalizedSteps) {
|
|
20018
|
+
const transitionEnd = cursor + step.duration;
|
|
20019
|
+
const stepEnd = transitionEnd + step.hold;
|
|
20020
|
+
if (cycleTime >= transitionEnd) {
|
|
20021
|
+
applyChannels(step.channels, [override ?? 1]);
|
|
20022
|
+
cursor = stepEnd;
|
|
20023
|
+
continue;
|
|
20024
|
+
}
|
|
20025
|
+
if (cycleTime >= cursor) {
|
|
20026
|
+
const progress = override ?? ease(
|
|
20027
|
+
(cycleTime - cursor) / step.duration,
|
|
20028
|
+
step.easing ?? "ease-in-out"
|
|
20029
|
+
);
|
|
20030
|
+
applyChannels(step.channels, [progress]);
|
|
20031
|
+
break;
|
|
20032
|
+
}
|
|
20033
|
+
}
|
|
20034
|
+
return true;
|
|
20035
|
+
}
|
|
20036
|
+
});
|
|
20037
|
+
},
|
|
20038
|
+
[applyChannels, registerEffect]
|
|
20039
|
+
);
|
|
20040
|
+
const flicker = useCallback18(
|
|
20041
|
+
(channels, flickerOptions = {}) => {
|
|
20042
|
+
const id = flickerOptions.id ?? `flicker-${nextEffectId++}`;
|
|
20043
|
+
const synchronized = flickerOptions.synchronized ?? true;
|
|
20044
|
+
const minLevel = flickerOptions.minLevel ?? 0.15;
|
|
20045
|
+
const maxLevel = flickerOptions.maxLevel ?? 1;
|
|
20046
|
+
const interval = flickerOptions.interval ?? [45, 160];
|
|
20047
|
+
const rawIntervalRange = typeof interval === "number" ? [interval, interval] : interval;
|
|
20048
|
+
const intervalRange = [
|
|
20049
|
+
Math.max(1, Math.min(...rawIntervalRange)),
|
|
20050
|
+
Math.max(1, Math.max(...rawIntervalRange))
|
|
20051
|
+
];
|
|
20052
|
+
const response = clamp2(flickerOptions.response ?? 0.7, 0.01, 1);
|
|
20053
|
+
const blackoutChance = clamp2(flickerOptions.blackoutChance ?? 0.08);
|
|
20054
|
+
const flashChance = clamp2(flickerOptions.flashChance ?? 0.12);
|
|
20055
|
+
const flashLevel = flickerOptions.flashLevel ?? 1.15;
|
|
20056
|
+
const unitCount = Math.max(
|
|
20057
|
+
1,
|
|
20058
|
+
...[...channels.materials ?? [], ...channels.transforms ?? []].map(
|
|
20059
|
+
(channel) => resolveTargets(channel.targets).length
|
|
20060
|
+
),
|
|
20061
|
+
...(channels.lights ?? []).map(
|
|
20062
|
+
(channel) => stringList(channel.lights).length
|
|
20063
|
+
)
|
|
20064
|
+
);
|
|
20065
|
+
const count = synchronized ? 1 : unitCount;
|
|
20066
|
+
const levels = Array.from({ length: count }, () => minLevel);
|
|
20067
|
+
const targets = Array.from({ length: count }, () => minLevel);
|
|
20068
|
+
const nextChanges = Array.from({ length: count }, () => 0);
|
|
20069
|
+
return registerEffect({
|
|
20070
|
+
id,
|
|
20071
|
+
startedAt: null,
|
|
20072
|
+
lastFrameAt: null,
|
|
20073
|
+
override: null,
|
|
20074
|
+
onComplete: flickerOptions.onComplete,
|
|
20075
|
+
update: (now, elapsed, delta, override) => {
|
|
20076
|
+
for (let index = 0; index < count; index += 1) {
|
|
20077
|
+
if (override !== null) {
|
|
20078
|
+
targets[index] = override;
|
|
20079
|
+
} else if (now >= nextChanges[index]) {
|
|
20080
|
+
const roll = Math.random();
|
|
20081
|
+
targets[index] = roll < blackoutChance ? randomBetween(0, Math.min(0.04, minLevel)) : roll < blackoutChance + flashChance ? flashLevel : randomBetween(minLevel, maxLevel);
|
|
20082
|
+
nextChanges[index] = now + randomBetween(...intervalRange);
|
|
20083
|
+
}
|
|
20084
|
+
const frameResponse = 1 - Math.pow(1 - response, Math.max(1, delta) / 16.67);
|
|
20085
|
+
levels[index] += (targets[index] - levels[index]) * frameResponse;
|
|
20086
|
+
}
|
|
20087
|
+
applyChannels(channels, levels);
|
|
20088
|
+
return flickerOptions.duration === void 0 || elapsed < flickerOptions.duration;
|
|
20089
|
+
}
|
|
20090
|
+
});
|
|
20091
|
+
},
|
|
20092
|
+
[applyChannels, registerEffect, resolveTargets]
|
|
20093
|
+
);
|
|
20094
|
+
const overrideEffect = useCallback18(
|
|
20095
|
+
(id, level, duration) => {
|
|
20096
|
+
const effect = effectsRef.current.get(id);
|
|
20097
|
+
if (!effect) return false;
|
|
20098
|
+
const now = typeof performance === "undefined" ? Date.now() : performance.now();
|
|
20099
|
+
effect.override = { level, until: now + Math.max(0, duration) };
|
|
20100
|
+
scheduleFrameRef.current();
|
|
20101
|
+
return true;
|
|
20102
|
+
},
|
|
20103
|
+
[]
|
|
20104
|
+
);
|
|
20105
|
+
const stopEffect = useCallback18((id) => {
|
|
20106
|
+
return effectsRef.current.delete(id);
|
|
20107
|
+
}, []);
|
|
20108
|
+
const stopAllEffects = useCallback18(() => {
|
|
20109
|
+
effectsRef.current.clear();
|
|
20110
|
+
if (frameRef.current !== null && typeof cancelAnimationFrame !== "undefined") {
|
|
20111
|
+
cancelAnimationFrame(frameRef.current);
|
|
20112
|
+
frameRef.current = null;
|
|
20113
|
+
}
|
|
20114
|
+
}, []);
|
|
20115
|
+
const restoreRuntimeValues = useCallback18(() => {
|
|
20116
|
+
for (const [entry, snapshot] of materialSnapshotsRef.current) {
|
|
20117
|
+
applyMaterial(entry, snapshot);
|
|
20118
|
+
}
|
|
20119
|
+
materialSnapshotsRef.current.clear();
|
|
20120
|
+
for (const [node, snapshot] of transformSnapshotsRef.current) {
|
|
20121
|
+
node.position.set(...snapshot.position);
|
|
20122
|
+
node.rotation.set(...snapshot.rotation);
|
|
20123
|
+
node.scale.set(...snapshot.scale);
|
|
20124
|
+
node.visible = snapshot.visible;
|
|
20125
|
+
node.updateMatrix();
|
|
20126
|
+
node.updateMatrixWorld(true);
|
|
20127
|
+
}
|
|
20128
|
+
transformSnapshotsRef.current.clear();
|
|
20129
|
+
for (const runtime of lightsRef.current.values()) {
|
|
20130
|
+
runtime.light.removeFromParent();
|
|
20131
|
+
runtime.light.dispose();
|
|
20132
|
+
}
|
|
20133
|
+
lightsRef.current.clear();
|
|
20134
|
+
invalidate();
|
|
20135
|
+
}, [invalidate]);
|
|
20136
|
+
const resetRuntime = useCallback18(() => {
|
|
20137
|
+
stopAllEffects();
|
|
20138
|
+
restoreRuntimeValues();
|
|
20139
|
+
}, [restoreRuntimeValues, stopAllEffects]);
|
|
20140
|
+
const handleViewerReady = useCallback18(
|
|
20141
|
+
(viewer) => {
|
|
20142
|
+
if (sceneRef.current && sceneRef.current !== viewer.scene) {
|
|
20143
|
+
restoreRuntimeValues();
|
|
20144
|
+
}
|
|
20145
|
+
viewerRef.current = viewer;
|
|
20146
|
+
sceneRef.current = viewer.scene;
|
|
20147
|
+
optionsRef.current.onViewerReady?.(viewer);
|
|
20148
|
+
},
|
|
20149
|
+
[restoreRuntimeValues]
|
|
20150
|
+
);
|
|
20151
|
+
useEffect25(() => {
|
|
20152
|
+
return () => {
|
|
20153
|
+
resetRuntime();
|
|
20154
|
+
viewerRef.current = null;
|
|
20155
|
+
sceneRef.current = null;
|
|
20156
|
+
};
|
|
20157
|
+
}, [resetRuntime]);
|
|
20158
|
+
return {
|
|
20159
|
+
handleViewerReady,
|
|
20160
|
+
setMaterial,
|
|
20161
|
+
setTransform,
|
|
20162
|
+
getWorldPosition,
|
|
20163
|
+
addPointLight,
|
|
20164
|
+
updatePointLight,
|
|
20165
|
+
removePointLight,
|
|
20166
|
+
transition,
|
|
20167
|
+
timeline,
|
|
20168
|
+
pulse,
|
|
20169
|
+
flicker,
|
|
20170
|
+
overrideEffect,
|
|
20171
|
+
stopEffect,
|
|
20172
|
+
stopAllEffects,
|
|
20173
|
+
resetRuntime
|
|
20174
|
+
};
|
|
20175
|
+
}
|
|
20176
|
+
|
|
18882
20177
|
// src/hooks/useShareableViewerState.ts
|
|
18883
|
-
import { useCallback as
|
|
20178
|
+
import { useCallback as useCallback19, useEffect as useEffect26, useRef as useRef24, useState as useState34 } from "react";
|
|
18884
20179
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
18885
20180
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
18886
20181
|
return {
|
|
@@ -18900,8 +20195,8 @@ function useShareableViewerState({
|
|
|
18900
20195
|
debounceMs = 400,
|
|
18901
20196
|
autoSync = true
|
|
18902
20197
|
}) {
|
|
18903
|
-
const lastWrittenRawRef =
|
|
18904
|
-
const buildHash =
|
|
20198
|
+
const lastWrittenRawRef = useRef24(null);
|
|
20199
|
+
const buildHash = useCallback19(() => {
|
|
18905
20200
|
const state = serializeState(
|
|
18906
20201
|
cameraState,
|
|
18907
20202
|
objectBindings,
|
|
@@ -18909,12 +20204,12 @@ function useShareableViewerState({
|
|
|
18909
20204
|
);
|
|
18910
20205
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
18911
20206
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
18912
|
-
const getShareUrl =
|
|
20207
|
+
const getShareUrl = useCallback19(() => {
|
|
18913
20208
|
const url = new URL(window.location.href);
|
|
18914
20209
|
url.hash = buildHash();
|
|
18915
20210
|
return url.toString();
|
|
18916
20211
|
}, [buildHash]);
|
|
18917
|
-
const syncUrl =
|
|
20212
|
+
const syncUrl = useCallback19(() => {
|
|
18918
20213
|
const url = new URL(window.location.href);
|
|
18919
20214
|
url.hash = buildHash();
|
|
18920
20215
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -18922,7 +20217,7 @@ function useShareableViewerState({
|
|
|
18922
20217
|
paramKey
|
|
18923
20218
|
);
|
|
18924
20219
|
}, [buildHash, paramKey]);
|
|
18925
|
-
const latestStateRef =
|
|
20220
|
+
const latestStateRef = useRef24({
|
|
18926
20221
|
cameraState,
|
|
18927
20222
|
objectBindings,
|
|
18928
20223
|
selectedObjectBinding
|
|
@@ -18932,9 +20227,9 @@ function useShareableViewerState({
|
|
|
18932
20227
|
objectBindings,
|
|
18933
20228
|
selectedObjectBinding
|
|
18934
20229
|
};
|
|
18935
|
-
const [restoreAttempted, setRestoreAttempted] =
|
|
18936
|
-
const lastSyncedHashRef =
|
|
18937
|
-
|
|
20230
|
+
const [restoreAttempted, setRestoreAttempted] = useState34(false);
|
|
20231
|
+
const lastSyncedHashRef = useRef24(null);
|
|
20232
|
+
useEffect26(() => {
|
|
18938
20233
|
if (!autoSync || !restoreAttempted) return;
|
|
18939
20234
|
const tick = () => {
|
|
18940
20235
|
const current = latestStateRef.current;
|
|
@@ -18956,9 +20251,9 @@ function useShareableViewerState({
|
|
|
18956
20251
|
const id = window.setInterval(tick, debounceMs);
|
|
18957
20252
|
return () => window.clearInterval(id);
|
|
18958
20253
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
18959
|
-
const lastRestoredRawRef =
|
|
18960
|
-
const restoringRawRef =
|
|
18961
|
-
const restoreFromUrl =
|
|
20254
|
+
const lastRestoredRawRef = useRef24(null);
|
|
20255
|
+
const restoringRawRef = useRef24(null);
|
|
20256
|
+
const restoreFromUrl = useCallback19(async () => {
|
|
18962
20257
|
setRestoreAttempted(true);
|
|
18963
20258
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
18964
20259
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|
|
@@ -19011,7 +20306,7 @@ function useShareableViewerState({
|
|
|
19011
20306
|
setCameraState,
|
|
19012
20307
|
setSelectedObjectBinding
|
|
19013
20308
|
]);
|
|
19014
|
-
|
|
20309
|
+
useEffect26(() => {
|
|
19015
20310
|
const handleHashChange = () => {
|
|
19016
20311
|
void restoreFromUrl();
|
|
19017
20312
|
};
|
|
@@ -19024,13 +20319,17 @@ function useShareableViewerState({
|
|
|
19024
20319
|
}
|
|
19025
20320
|
export {
|
|
19026
20321
|
BindingBuilder,
|
|
20322
|
+
DEFAULT_SCENE_CONFIG,
|
|
19027
20323
|
MATERIAL_BLENDING_MODES,
|
|
19028
20324
|
MATERIAL_SIDES,
|
|
19029
20325
|
ModelViewer,
|
|
19030
20326
|
SimpleModelViewer,
|
|
20327
|
+
defineObjectBindings,
|
|
19031
20328
|
material,
|
|
19032
20329
|
patchBindings,
|
|
19033
20330
|
patchSceneConfig,
|
|
20331
|
+
selectBindingKeys,
|
|
20332
|
+
selectBindings,
|
|
19034
20333
|
useObjectBinding,
|
|
19035
20334
|
useObjectBindingIds,
|
|
19036
20335
|
useObjectBindings,
|
|
@@ -19039,6 +20338,8 @@ export {
|
|
|
19039
20338
|
useViewerActions,
|
|
19040
20339
|
useViewerAnimations,
|
|
19041
20340
|
useViewerCamera,
|
|
20341
|
+
useViewerConnection,
|
|
20342
|
+
useViewerEffects,
|
|
19042
20343
|
useViewerHover,
|
|
19043
20344
|
useViewerModel,
|
|
19044
20345
|
useViewerSelection
|