@almadar/ui 5.142.0 → 5.144.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +173 -35
- package/dist/avl/index.js +174 -36
- package/dist/{avl-schema-parser-DAs12kxP.d.ts → avl-schema-parser-BRJ77Yze.d.ts} +17 -3
- package/dist/{avl-schema-parser-wOkfbwAh.d.cts → avl-schema-parser-DVmrgdwc.d.cts} +17 -3
- package/dist/{cn-rwc3svaX.d.cts → cn-BdGFhYe3.d.cts} +1 -1
- package/dist/{cn-BDwC4QpH.d.ts → cn-DNkqAWZK.d.ts} +1 -1
- package/dist/components/index.cjs +277 -163
- package/dist/components/index.d.cts +9 -7
- package/dist/components/index.d.ts +9 -7
- package/dist/components/index.js +278 -164
- package/dist/lib/drawable/three/index.cjs +449 -195
- package/dist/lib/drawable/three/index.d.cts +3 -3
- package/dist/lib/drawable/three/index.d.ts +3 -3
- package/dist/lib/drawable/three/index.js +425 -171
- package/dist/lib/index.d.cts +2 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/marketing/index.cjs +6 -1
- package/dist/marketing/index.js +6 -1
- package/dist/{paintDispatch-BQn5Lyx1.d.cts → paintDispatch-Cb_hQj4Y.d.cts} +79 -33
- package/dist/{paintDispatch-BQn5Lyx1.d.ts → paintDispatch-Cb_hQj4Y.d.ts} +79 -33
- package/dist/providers/index.cjs +173 -35
- package/dist/providers/index.js +174 -36
- package/dist/runtime/index.cjs +173 -35
- package/dist/runtime/index.js +174 -36
- package/package.json +9 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React3, { forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle, useCallback,
|
|
1
|
+
import React3, { createContext, forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle, useCallback, useContext, Component, useReducer } from 'react';
|
|
2
2
|
import { EventBusContext, useTraitScopeChain } from '@almadar/ui/providers';
|
|
3
3
|
import { createLogger } from '@almadar/logger';
|
|
4
4
|
import { Canvas, useThree, useFrame } from '@react-three/fiber';
|
|
5
|
-
import * as
|
|
5
|
+
import * as THREE5 from 'three';
|
|
6
6
|
import { Vector3, QuadraticBezierCurve3, MathUtils, Quaternion } from 'three';
|
|
7
7
|
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
|
|
8
8
|
import { Grid, OrbitControls, Billboard, Text, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
|
|
@@ -507,7 +507,7 @@ function Lighting3D({
|
|
|
507
507
|
"directionalLightHelper",
|
|
508
508
|
{
|
|
509
509
|
args: [
|
|
510
|
-
new
|
|
510
|
+
new THREE5.DirectionalLight(directionalColor, directionalIntensity),
|
|
511
511
|
5
|
|
512
512
|
]
|
|
513
513
|
}
|
|
@@ -564,8 +564,8 @@ function FollowCamera3D({
|
|
|
564
564
|
offset
|
|
565
565
|
}) {
|
|
566
566
|
const { camera } = useThree();
|
|
567
|
-
const look = useRef(new
|
|
568
|
-
const goal = useRef(new
|
|
567
|
+
const look = useRef(new THREE5.Vector3(target[0], target[1], target[2]));
|
|
568
|
+
const goal = useRef(new THREE5.Vector3());
|
|
569
569
|
useFrame((_, delta) => {
|
|
570
570
|
const t = Math.min(1, delta * 5);
|
|
571
571
|
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
@@ -586,7 +586,109 @@ function create3DProjector(opts = {}) {
|
|
|
586
586
|
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
587
587
|
};
|
|
588
588
|
}
|
|
589
|
+
var BoneStore = class {
|
|
590
|
+
constructor() {
|
|
591
|
+
__publicField(this, "bones", /* @__PURE__ */ new Map());
|
|
592
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
593
|
+
}
|
|
594
|
+
register(name, bone) {
|
|
595
|
+
this.bones.set(name, bone);
|
|
596
|
+
this.notify();
|
|
597
|
+
return () => {
|
|
598
|
+
if (this.bones.get(name) === bone) {
|
|
599
|
+
this.bones.delete(name);
|
|
600
|
+
this.notify();
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
get(name) {
|
|
605
|
+
return this.bones.get(name);
|
|
606
|
+
}
|
|
607
|
+
subscribe(listener) {
|
|
608
|
+
this.listeners.add(listener);
|
|
609
|
+
return () => this.listeners.delete(listener);
|
|
610
|
+
}
|
|
611
|
+
notify() {
|
|
612
|
+
for (const l of this.listeners) l();
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
var BoneRegistryContext = createContext(null);
|
|
616
|
+
function isAnimatedGroup(node) {
|
|
617
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
618
|
+
}
|
|
619
|
+
var MESH_SEGMENTS_DEFAULT = 24;
|
|
620
|
+
function polyhedronBounds(vertices) {
|
|
621
|
+
if (!vertices || vertices.length < 3) return null;
|
|
622
|
+
const min = [Infinity, Infinity, Infinity];
|
|
623
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
624
|
+
for (const v of vertices) {
|
|
625
|
+
if (v.length < 3 || !v.every((c) => Number.isFinite(c))) return null;
|
|
626
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
627
|
+
min[axis] = Math.min(min[axis], v[axis]);
|
|
628
|
+
max[axis] = Math.max(max[axis], v[axis]);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return { min, max };
|
|
632
|
+
}
|
|
633
|
+
function clampSegments(segments) {
|
|
634
|
+
const s = segments ?? MESH_SEGMENTS_DEFAULT;
|
|
635
|
+
return Math.min(64, Math.max(3, Math.round(s)));
|
|
636
|
+
}
|
|
637
|
+
function isAnimatedMesh(node) {
|
|
638
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
639
|
+
}
|
|
640
|
+
var lerp = (a, b, k) => a + (b - a) * k;
|
|
589
641
|
var NUMERIC_TRACKS = [
|
|
642
|
+
"offsetX",
|
|
643
|
+
"offsetY",
|
|
644
|
+
"offsetZ",
|
|
645
|
+
"rotateX",
|
|
646
|
+
"rotateY",
|
|
647
|
+
"rotateZ",
|
|
648
|
+
"scale",
|
|
649
|
+
"opacity",
|
|
650
|
+
"emissiveIntensity"
|
|
651
|
+
];
|
|
652
|
+
function applyMeshAnimation(node, timeMs) {
|
|
653
|
+
const anim = node.animation;
|
|
654
|
+
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
|
|
655
|
+
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
656
|
+
const cycle = timeMs / anim.durationMs;
|
|
657
|
+
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
658
|
+
const trackValue = (key) => {
|
|
659
|
+
const defined = frames.filter((f) => f[key] !== void 0);
|
|
660
|
+
if (defined.length === 0) return void 0;
|
|
661
|
+
let prev;
|
|
662
|
+
let next;
|
|
663
|
+
for (const f of defined) {
|
|
664
|
+
if (f.at <= t) prev = f;
|
|
665
|
+
else if (!next) next = f;
|
|
666
|
+
}
|
|
667
|
+
if (!prev) return defined[0][key];
|
|
668
|
+
if (!next) return prev[key];
|
|
669
|
+
const span = next.at - prev.at;
|
|
670
|
+
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
671
|
+
const a = prev[key];
|
|
672
|
+
const b = next[key];
|
|
673
|
+
if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
|
|
674
|
+
return a;
|
|
675
|
+
};
|
|
676
|
+
const num = {};
|
|
677
|
+
for (const key of NUMERIC_TRACKS) {
|
|
678
|
+
const v = trackValue(key);
|
|
679
|
+
if (v !== void 0) num[key] = v;
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
|
|
683
|
+
rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
|
|
684
|
+
scale: num.scale ?? 1,
|
|
685
|
+
opacity: num.opacity,
|
|
686
|
+
emissiveIntensity: num.emissiveIntensity,
|
|
687
|
+
color: trackValue("color"),
|
|
688
|
+
emissive: trackValue("emissive")
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
var NUMERIC_TRACKS2 = [
|
|
590
692
|
"offsetX",
|
|
591
693
|
"offsetY",
|
|
592
694
|
"rotate",
|
|
@@ -602,7 +704,7 @@ var NUMERIC_TRACKS = [
|
|
|
602
704
|
function isAnimatedShape(node) {
|
|
603
705
|
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
604
706
|
}
|
|
605
|
-
var
|
|
707
|
+
var lerp2 = (a, b, k) => a + (b - a) * k;
|
|
606
708
|
function applyShapeAnimation(node, timeMs) {
|
|
607
709
|
const anim = node.animation;
|
|
608
710
|
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
|
|
@@ -625,10 +727,10 @@ function applyShapeAnimation(node, timeMs) {
|
|
|
625
727
|
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
626
728
|
const a = prev[key];
|
|
627
729
|
const b = next[key];
|
|
628
|
-
if (typeof a === "number" && typeof b === "number") return
|
|
730
|
+
if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
|
|
629
731
|
return a;
|
|
630
732
|
};
|
|
631
|
-
for (const key of
|
|
733
|
+
for (const key of NUMERIC_TRACKS2) {
|
|
632
734
|
const v = trackValue(key);
|
|
633
735
|
if (v !== void 0) out[key] = v;
|
|
634
736
|
}
|
|
@@ -649,7 +751,7 @@ function applyShapeAnimation(node, timeMs) {
|
|
|
649
751
|
if (prevSh?.shadow && nextSh?.shadow) {
|
|
650
752
|
const span = nextSh.at - prevSh.at;
|
|
651
753
|
const k = span > 0 ? (t - prevSh.at) / span : 1;
|
|
652
|
-
out.shadow = { color: prevSh.shadow.color, blur:
|
|
754
|
+
out.shadow = { color: prevSh.shadow.color, blur: lerp2(prevSh.shadow.blur, nextSh.shadow.blur, k) };
|
|
653
755
|
} else {
|
|
654
756
|
out.shadow = sh;
|
|
655
757
|
}
|
|
@@ -663,6 +765,10 @@ var atlasCache = /* @__PURE__ */ new Map();
|
|
|
663
765
|
function isTilesheet(a) {
|
|
664
766
|
return typeof a.tileWidth === "number";
|
|
665
767
|
}
|
|
768
|
+
function isSpriteSheetAtlas(a) {
|
|
769
|
+
const s = a;
|
|
770
|
+
return typeof s.frameWidth === "number" && typeof s.frameHeight === "number" && typeof s.animations === "object";
|
|
771
|
+
}
|
|
666
772
|
function getAtlas(url, onReady) {
|
|
667
773
|
if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
|
|
668
774
|
atlasCache.set(url, void 0);
|
|
@@ -699,6 +805,7 @@ function subRectFor(atlas, sprite) {
|
|
|
699
805
|
sh: atlas.tileHeight
|
|
700
806
|
};
|
|
701
807
|
}
|
|
808
|
+
if (isSpriteSheetAtlas(atlas)) return null;
|
|
702
809
|
const st = atlas.subTextures[sprite];
|
|
703
810
|
if (!st) return null;
|
|
704
811
|
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
@@ -792,12 +899,12 @@ function ModelLoader({
|
|
|
792
899
|
if (!loadedModel) return null;
|
|
793
900
|
const cloned = clone(loadedModel);
|
|
794
901
|
cloned.updateMatrixWorld(true);
|
|
795
|
-
const tintColor = tint ? new
|
|
902
|
+
const tintColor = tint ? new THREE5.Color(tint) : null;
|
|
796
903
|
cloned.traverse((child) => {
|
|
797
|
-
if (child instanceof
|
|
904
|
+
if (child instanceof THREE5.Mesh) {
|
|
798
905
|
child.castShadow = castShadow;
|
|
799
906
|
child.receiveShadow = receiveShadow;
|
|
800
|
-
if (tintColor && child.material instanceof
|
|
907
|
+
if (tintColor && child.material instanceof THREE5.MeshStandardMaterial) {
|
|
801
908
|
const mat = child.material.clone();
|
|
802
909
|
mat.color.multiply(tintColor);
|
|
803
910
|
child.material = mat;
|
|
@@ -806,7 +913,7 @@ function ModelLoader({
|
|
|
806
913
|
});
|
|
807
914
|
return cloned;
|
|
808
915
|
}, [loadedModel, castShadow, receiveShadow, tint]);
|
|
809
|
-
const mixer = useMemo(() => model ? new
|
|
916
|
+
const mixer = useMemo(() => model ? new THREE5.AnimationMixer(model) : null, [model]);
|
|
810
917
|
useEffect(() => {
|
|
811
918
|
if (!mixer || !animation || clips.length === 0) return;
|
|
812
919
|
const wanted = animation.toLowerCase();
|
|
@@ -823,8 +930,8 @@ function ModelLoader({
|
|
|
823
930
|
});
|
|
824
931
|
const normFactor = useMemo(() => {
|
|
825
932
|
if (!model) return 1;
|
|
826
|
-
const box = new
|
|
827
|
-
const size = new
|
|
933
|
+
const box = new THREE5.Box3().setFromObject(model);
|
|
934
|
+
const size = new THREE5.Vector3();
|
|
828
935
|
box.getSize(size);
|
|
829
936
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
830
937
|
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
@@ -914,7 +1021,7 @@ var warnUnsupported3d = (kind) => {
|
|
|
914
1021
|
warnedUnsupported.add(kind);
|
|
915
1022
|
mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
|
|
916
1023
|
};
|
|
917
|
-
var CrossOriginTextureLoader = class extends
|
|
1024
|
+
var CrossOriginTextureLoader = class extends THREE5.TextureLoader {
|
|
918
1025
|
constructor() {
|
|
919
1026
|
super();
|
|
920
1027
|
this.crossOrigin = "anonymous";
|
|
@@ -932,7 +1039,7 @@ function useBillboardTexture(url) {
|
|
|
932
1039
|
url,
|
|
933
1040
|
(texture) => {
|
|
934
1041
|
if (!active) return;
|
|
935
|
-
texture.colorSpace =
|
|
1042
|
+
texture.colorSpace = THREE5.SRGBColorSpace;
|
|
936
1043
|
setState({ texture, error: false });
|
|
937
1044
|
},
|
|
938
1045
|
void 0,
|
|
@@ -985,7 +1092,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
|
985
1092
|
}, [texture, frame, node.height, node.width, anchor, cellSize]);
|
|
986
1093
|
const groundGeometry = React3.useMemo(() => {
|
|
987
1094
|
if (anchor !== "top-left" || !texture || !atlasReady) return null;
|
|
988
|
-
const g = new
|
|
1095
|
+
const g = new THREE5.PlaneGeometry(size.width, size.height);
|
|
989
1096
|
g.rotateX(-Math.PI / 2);
|
|
990
1097
|
return g;
|
|
991
1098
|
}, [anchor, texture, atlasReady, size.width, size.height]);
|
|
@@ -993,15 +1100,15 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
|
993
1100
|
if (anchor === "top-left") {
|
|
994
1101
|
return /* @__PURE__ */ jsx("group", { position: [world[0] + size.width / 2, 0.02, world[2] + size.height / 2], children: /* @__PURE__ */ jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [
|
|
995
1102
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
996
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
1103
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE5.DoubleSide })
|
|
997
1104
|
] }) });
|
|
998
1105
|
}
|
|
999
1106
|
return /* @__PURE__ */ jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxs("mesh", { children: [
|
|
1000
1107
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
1001
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
1108
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE5.DoubleSide })
|
|
1002
1109
|
] }) });
|
|
1003
1110
|
}
|
|
1004
|
-
texture.magFilter = texture.minFilter =
|
|
1111
|
+
texture.magFilter = texture.minFilter = THREE5.NearestFilter;
|
|
1005
1112
|
texture.needsUpdate = true;
|
|
1006
1113
|
if (frame) {
|
|
1007
1114
|
texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
|
|
@@ -1014,7 +1121,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
|
1014
1121
|
map: texture,
|
|
1015
1122
|
transparent: true,
|
|
1016
1123
|
alphaTest: 0.1,
|
|
1017
|
-
side:
|
|
1124
|
+
side: THREE5.DoubleSide,
|
|
1018
1125
|
opacity: (node.opacity ?? 1) * groupOpacity
|
|
1019
1126
|
}
|
|
1020
1127
|
) }) });
|
|
@@ -1027,7 +1134,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
|
1027
1134
|
map: texture,
|
|
1028
1135
|
transparent: true,
|
|
1029
1136
|
alphaTest: 0.1,
|
|
1030
|
-
side:
|
|
1137
|
+
side: THREE5.DoubleSide,
|
|
1031
1138
|
opacity: node.opacity ?? 1
|
|
1032
1139
|
}
|
|
1033
1140
|
)
|
|
@@ -1096,7 +1203,7 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
|
|
|
1096
1203
|
}
|
|
1097
1204
|
case "poly": {
|
|
1098
1205
|
if (!node.points || node.points.length === 0) return null;
|
|
1099
|
-
const s = new
|
|
1206
|
+
const s = new THREE5.Shape();
|
|
1100
1207
|
node.points.forEach((p, i) => {
|
|
1101
1208
|
if (i === 0) s.moveTo(p.x, p.y);
|
|
1102
1209
|
else s.lineTo(p.x, p.y);
|
|
@@ -1114,7 +1221,7 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
|
|
|
1114
1221
|
}
|
|
1115
1222
|
return /* @__PURE__ */ jsx("group", { ref: groupRef, position: [world[0], world[1] + 0.02, world[2]], children: /* @__PURE__ */ jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [
|
|
1116
1223
|
geometry,
|
|
1117
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side:
|
|
1224
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE5.DoubleSide })
|
|
1118
1225
|
] }) });
|
|
1119
1226
|
}
|
|
1120
1227
|
function Text3D({ node, projector, groupOpacity = 1 }) {
|
|
@@ -1135,64 +1242,66 @@ function Text3D({ node, projector, groupOpacity = 1 }) {
|
|
|
1135
1242
|
}
|
|
1136
1243
|
) });
|
|
1137
1244
|
}
|
|
1138
|
-
var
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1245
|
+
var GROUND_ROTATION = [-Math.PI / 2, 0, 0];
|
|
1246
|
+
var textureCache = /* @__PURE__ */ new Map();
|
|
1247
|
+
function getMeshTexture(url) {
|
|
1248
|
+
const cached = textureCache.get(url);
|
|
1249
|
+
if (cached) return cached;
|
|
1250
|
+
const texture = new THREE5.TextureLoader().load(url);
|
|
1251
|
+
texture.flipY = false;
|
|
1252
|
+
texture.colorSpace = THREE5.SRGBColorSpace;
|
|
1253
|
+
texture.wrapS = THREE5.RepeatWrapping;
|
|
1254
|
+
texture.wrapT = THREE5.RepeatWrapping;
|
|
1255
|
+
textureCache.set(url, texture);
|
|
1256
|
+
return texture;
|
|
1142
1257
|
}
|
|
1143
|
-
function
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
];
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
let
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
if (f.at <= t) prev = f;
|
|
1171
|
-
else if (!next) next = f;
|
|
1258
|
+
function polyhedronGeometry(node, cellSize) {
|
|
1259
|
+
const verts = node.vertices;
|
|
1260
|
+
const faces = node.faces;
|
|
1261
|
+
const bounds = polyhedronBounds(verts);
|
|
1262
|
+
if (!verts || !bounds || !faces || faces.length === 0) return null;
|
|
1263
|
+
const positions = new Float32Array(verts.length * 3);
|
|
1264
|
+
for (let i = 0; i < verts.length; i++) {
|
|
1265
|
+
positions[i * 3] = verts[i][0] * cellSize;
|
|
1266
|
+
positions[i * 3 + 1] = verts[i][2] * cellSize;
|
|
1267
|
+
positions[i * 3 + 2] = verts[i][1] * cellSize;
|
|
1268
|
+
}
|
|
1269
|
+
const index = [];
|
|
1270
|
+
for (const face of faces) {
|
|
1271
|
+
if (face.length < 3) continue;
|
|
1272
|
+
const [a, b, c] = face;
|
|
1273
|
+
if (a === b || b === c || a === c) continue;
|
|
1274
|
+
if (![a, b, c].every((i) => Number.isInteger(i) && i >= 0 && i < verts.length)) continue;
|
|
1275
|
+
index.push(a, c, b);
|
|
1276
|
+
}
|
|
1277
|
+
if (index.length === 0) return null;
|
|
1278
|
+
const geometry = new THREE5.BufferGeometry();
|
|
1279
|
+
geometry.setAttribute("position", new THREE5.BufferAttribute(positions, 3));
|
|
1280
|
+
if (node.uvs && node.uvs.length === verts.length) {
|
|
1281
|
+
const uv = new Float32Array(verts.length * 2);
|
|
1282
|
+
for (let i = 0; i < verts.length; i++) {
|
|
1283
|
+
uv[i * 2] = node.uvs[i][0];
|
|
1284
|
+
uv[i * 2 + 1] = node.uvs[i][1];
|
|
1172
1285
|
}
|
|
1173
|
-
|
|
1174
|
-
if (!next) return prev[key];
|
|
1175
|
-
const span = next.at - prev.at;
|
|
1176
|
-
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
1177
|
-
const a = prev[key];
|
|
1178
|
-
const b = next[key];
|
|
1179
|
-
if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
|
|
1180
|
-
return a;
|
|
1181
|
-
};
|
|
1182
|
-
const num = {};
|
|
1183
|
-
for (const key of NUMERIC_TRACKS2) {
|
|
1184
|
-
const v = trackValue(key);
|
|
1185
|
-
if (v !== void 0) num[key] = v;
|
|
1286
|
+
geometry.setAttribute("uv", new THREE5.BufferAttribute(uv, 2));
|
|
1186
1287
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1288
|
+
geometry.setIndex(index);
|
|
1289
|
+
geometry.computeVertexNormals();
|
|
1290
|
+
const skin = node.skin;
|
|
1291
|
+
if (skin && skin.indices.length === verts.length && skin.weights.length === verts.length) {
|
|
1292
|
+
const skinIndex = new Uint16Array(verts.length * 4);
|
|
1293
|
+
const skinWeight = new Float32Array(verts.length * 4);
|
|
1294
|
+
for (let i = 0; i < verts.length; i++) {
|
|
1295
|
+
for (let k = 0; k < 4; k++) {
|
|
1296
|
+
skinIndex[i * 4 + k] = skin.indices[i][k] ?? 0;
|
|
1297
|
+
skinWeight[i * 4 + k] = skin.weights[i][k] ?? 0;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
geometry.setAttribute("skinIndex", new THREE5.BufferAttribute(skinIndex, 4));
|
|
1301
|
+
geometry.setAttribute("skinWeight", new THREE5.BufferAttribute(skinWeight, 4));
|
|
1302
|
+
}
|
|
1303
|
+
const size = Math.max(bounds.max[0] - bounds.min[0], bounds.max[1] - bounds.min[1], bounds.max[2] - bounds.min[2]) * cellSize;
|
|
1304
|
+
return { geometry, lift: -bounds.min[2] * cellSize, size };
|
|
1196
1305
|
}
|
|
1197
1306
|
function meshGeometry(node, cellSize) {
|
|
1198
1307
|
const seg = clampSegments(node.segments);
|
|
@@ -1220,17 +1329,20 @@ function meshGeometry(node, cellSize) {
|
|
|
1220
1329
|
return { element: /* @__PURE__ */ jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
|
|
1221
1330
|
}
|
|
1222
1331
|
case "plane":
|
|
1223
|
-
return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d) };
|
|
1332
|
+
return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d), baseRotation: GROUND_ROTATION };
|
|
1224
1333
|
case "circle":
|
|
1225
|
-
return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2 };
|
|
1334
|
+
return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2, baseRotation: GROUND_ROTATION };
|
|
1335
|
+
case "polyhedron":
|
|
1336
|
+
return polyhedronGeometry(node, cellSize);
|
|
1226
1337
|
default:
|
|
1227
1338
|
return null;
|
|
1228
1339
|
}
|
|
1229
1340
|
}
|
|
1341
|
+
var warnedPolyhedronOutline = false;
|
|
1230
1342
|
var SIDE_MAP = {
|
|
1231
|
-
front:
|
|
1232
|
-
back:
|
|
1233
|
-
double:
|
|
1343
|
+
front: THREE5.FrontSide,
|
|
1344
|
+
back: THREE5.BackSide,
|
|
1345
|
+
double: THREE5.DoubleSide
|
|
1234
1346
|
};
|
|
1235
1347
|
function meshMaterial(mat, opacity, ref) {
|
|
1236
1348
|
const m = mat ?? {};
|
|
@@ -1239,7 +1351,8 @@ function meshMaterial(mat, opacity, ref) {
|
|
|
1239
1351
|
color: m.color ?? "#ffffff",
|
|
1240
1352
|
transparent: opacity < 1,
|
|
1241
1353
|
opacity,
|
|
1242
|
-
side: SIDE_MAP[m.side ?? "front"]
|
|
1354
|
+
side: SIDE_MAP[m.side ?? "front"],
|
|
1355
|
+
...m.map ? { map: getMeshTexture(m.map) } : {}
|
|
1243
1356
|
};
|
|
1244
1357
|
const emissive = m.emissive ? { emissive: m.emissive, emissiveIntensity: m.emissiveIntensity ?? 1 } : {};
|
|
1245
1358
|
switch (m.kind ?? "standard") {
|
|
@@ -1274,6 +1387,53 @@ function meshMaterial(mat, opacity, ref) {
|
|
|
1274
1387
|
);
|
|
1275
1388
|
}
|
|
1276
1389
|
}
|
|
1390
|
+
function SkinnedPolyhedron3D({
|
|
1391
|
+
node,
|
|
1392
|
+
skin,
|
|
1393
|
+
geometry,
|
|
1394
|
+
cellSize,
|
|
1395
|
+
opacity
|
|
1396
|
+
}) {
|
|
1397
|
+
const boneStore = useContext(BoneRegistryContext);
|
|
1398
|
+
const meshRef = useRef(null);
|
|
1399
|
+
const [registryTick, setRegistryTick] = useState(0);
|
|
1400
|
+
useEffect(() => boneStore?.subscribe(() => setRegistryTick((t) => t + 1)), [boneStore]);
|
|
1401
|
+
useEffect(() => {
|
|
1402
|
+
const mesh = meshRef.current;
|
|
1403
|
+
if (!mesh || !boneStore) return;
|
|
1404
|
+
const bones = [];
|
|
1405
|
+
for (const name of skin.bones) {
|
|
1406
|
+
const bone = boneStore.get(name);
|
|
1407
|
+
if (!bone) return;
|
|
1408
|
+
bones.push(bone);
|
|
1409
|
+
}
|
|
1410
|
+
if (mesh.skeleton && mesh.skeleton.bones.length === bones.length && mesh.skeleton.bones.every((b, i) => b === bones[i])) return;
|
|
1411
|
+
if (skin.inverseBindMatrices.length !== bones.length) return;
|
|
1412
|
+
const inverses = skin.inverseBindMatrices.map((m) => {
|
|
1413
|
+
const mat = new THREE5.Matrix4().fromArray(m);
|
|
1414
|
+
mat.elements[12] *= cellSize;
|
|
1415
|
+
mat.elements[13] *= cellSize;
|
|
1416
|
+
mat.elements[14] *= cellSize;
|
|
1417
|
+
return mat;
|
|
1418
|
+
});
|
|
1419
|
+
mesh.bind(new THREE5.Skeleton(bones, inverses), new THREE5.Matrix4());
|
|
1420
|
+
}, [registryTick, skin, boneStore, geometry, cellSize]);
|
|
1421
|
+
useEffect(() => {
|
|
1422
|
+
const mesh = meshRef.current;
|
|
1423
|
+
return () => mesh?.skeleton?.dispose();
|
|
1424
|
+
}, []);
|
|
1425
|
+
return /* @__PURE__ */ jsx(
|
|
1426
|
+
"skinnedMesh",
|
|
1427
|
+
{
|
|
1428
|
+
ref: meshRef,
|
|
1429
|
+
geometry,
|
|
1430
|
+
frustumCulled: false,
|
|
1431
|
+
castShadow: node.castShadow ?? true,
|
|
1432
|
+
receiveShadow: node.receiveShadow ?? true,
|
|
1433
|
+
children: meshMaterial(node.material, opacity)
|
|
1434
|
+
}
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1277
1437
|
function Mesh3D({
|
|
1278
1438
|
node,
|
|
1279
1439
|
projector,
|
|
@@ -1286,6 +1446,10 @@ function Mesh3D({
|
|
|
1286
1446
|
const validPos = isValidScenePos(node.position);
|
|
1287
1447
|
const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
|
|
1288
1448
|
const geo = useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
|
|
1449
|
+
useEffect(() => {
|
|
1450
|
+
const g = geo?.geometry;
|
|
1451
|
+
return () => g?.dispose();
|
|
1452
|
+
}, [geo]);
|
|
1289
1453
|
useFrame(({ clock }) => {
|
|
1290
1454
|
if (!animated || !groupRef.current) return;
|
|
1291
1455
|
const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
|
|
@@ -1298,8 +1462,13 @@ function Mesh3D({
|
|
|
1298
1462
|
);
|
|
1299
1463
|
groupRef.current.scale.setScalar(state.scale);
|
|
1300
1464
|
if (meshRef.current) {
|
|
1465
|
+
const shapeRot = geo?.baseRotation ?? [0, 0, 0];
|
|
1301
1466
|
const base = node.rotation ?? [0, 0, 0];
|
|
1302
|
-
meshRef.current.rotation.set(
|
|
1467
|
+
meshRef.current.rotation.set(
|
|
1468
|
+
shapeRot[0] + base[0] + state.rotate[0],
|
|
1469
|
+
shapeRot[1] + base[1] + state.rotate[1],
|
|
1470
|
+
shapeRot[2] + base[2] + state.rotate[2]
|
|
1471
|
+
);
|
|
1303
1472
|
}
|
|
1304
1473
|
const mat = materialRef.current;
|
|
1305
1474
|
if (mat) {
|
|
@@ -1316,13 +1485,28 @@ function Mesh3D({
|
|
|
1316
1485
|
if (!validPos || !geo) return null;
|
|
1317
1486
|
const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
|
|
1318
1487
|
const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
|
|
1319
|
-
|
|
1488
|
+
if (node.skin && geo.geometry) {
|
|
1489
|
+
return /* @__PURE__ */ jsx("group", { position: baseWorld, children: /* @__PURE__ */ jsx(SkinnedPolyhedron3D, { node, skin: node.skin, geometry: geo.geometry, cellSize: projector.cellSize, opacity }) });
|
|
1490
|
+
}
|
|
1491
|
+
const nodeRotation = node.rotation ?? [0, 0, 0];
|
|
1492
|
+
const shapeRotation = geo.baseRotation ?? [0, 0, 0];
|
|
1493
|
+
const rotation = [
|
|
1494
|
+
shapeRotation[0] + nodeRotation[0],
|
|
1495
|
+
shapeRotation[1] + nodeRotation[1],
|
|
1496
|
+
shapeRotation[2] + nodeRotation[2]
|
|
1497
|
+
];
|
|
1320
1498
|
const outlineScale = geo.size > 0 ? 1 + (node.outline?.width ?? 0.05) * projector.cellSize / geo.size : 1;
|
|
1499
|
+
if (node.outline && geo.geometry && !warnedPolyhedronOutline) {
|
|
1500
|
+
warnedPolyhedronOutline = true;
|
|
1501
|
+
console.warn('[draw-mesh] outline is not yet supported on shape "polyhedron" \u2014 skipped');
|
|
1502
|
+
}
|
|
1503
|
+
const geometryProp = geo.geometry ? { geometry: geo.geometry } : {};
|
|
1321
1504
|
return /* @__PURE__ */ jsxs("group", { ref: groupRef, position: baseWorld, children: [
|
|
1322
1505
|
/* @__PURE__ */ jsxs(
|
|
1323
1506
|
"mesh",
|
|
1324
1507
|
{
|
|
1325
1508
|
ref: meshRef,
|
|
1509
|
+
...geometryProp,
|
|
1326
1510
|
position: [0, lift, 0],
|
|
1327
1511
|
rotation,
|
|
1328
1512
|
castShadow: node.castShadow ?? true,
|
|
@@ -1333,13 +1517,13 @@ function Mesh3D({
|
|
|
1333
1517
|
]
|
|
1334
1518
|
}
|
|
1335
1519
|
),
|
|
1336
|
-
node.outline && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
|
|
1520
|
+
node.outline && !geo.geometry && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
|
|
1337
1521
|
geo.element,
|
|
1338
1522
|
/* @__PURE__ */ jsx(
|
|
1339
1523
|
"meshBasicMaterial",
|
|
1340
1524
|
{
|
|
1341
1525
|
color: node.outline.color ?? "#101014",
|
|
1342
|
-
side:
|
|
1526
|
+
side: THREE5.BackSide,
|
|
1343
1527
|
transparent: opacity < 1,
|
|
1344
1528
|
opacity
|
|
1345
1529
|
}
|
|
@@ -1366,14 +1550,53 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
|
|
|
1366
1550
|
case "draw-group": {
|
|
1367
1551
|
if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
|
|
1368
1552
|
if (node.clip) warnUnsupported3d("draw-group:clip");
|
|
1369
|
-
|
|
1370
|
-
const inner = create3DProjector({ cellSize: projector.cellSize });
|
|
1371
|
-
const opacity = (node.opacity ?? 1) * groupOpacity;
|
|
1372
|
-
const s = node.scale ?? 1;
|
|
1373
|
-
return /* @__PURE__ */ jsx("group", { position: world, rotation: [0, -(node.rotate ?? 0), 0], scale: [s, s, s], children: node.items.map((item, i) => /* @__PURE__ */ jsx(Drawable3D, { node: item, projector: inner, groupOpacity: opacity }, i)) });
|
|
1553
|
+
return /* @__PURE__ */ jsx(Group3D, { node, projector, groupOpacity });
|
|
1374
1554
|
}
|
|
1375
1555
|
}
|
|
1376
1556
|
}
|
|
1557
|
+
function Group3D({
|
|
1558
|
+
node,
|
|
1559
|
+
projector,
|
|
1560
|
+
groupOpacity
|
|
1561
|
+
}) {
|
|
1562
|
+
const ref = useRef(null);
|
|
1563
|
+
const animated = isAnimatedGroup(node);
|
|
1564
|
+
const world = projector.toWorld(node.position);
|
|
1565
|
+
const inner = create3DProjector({ cellSize: projector.cellSize });
|
|
1566
|
+
const opacity = (node.opacity ?? 1) * groupOpacity;
|
|
1567
|
+
const s = node.scale ?? 1;
|
|
1568
|
+
const baseRotation = node.rotation ?? [0, -(node.rotate ?? 0), 0];
|
|
1569
|
+
const parentStore = useContext(BoneRegistryContext);
|
|
1570
|
+
const scopedStore = useMemo(() => node.skeleton ? new BoneStore() : null, [node.skeleton]);
|
|
1571
|
+
const boneStore = scopedStore ?? parentStore;
|
|
1572
|
+
const boneObject = useMemo(() => node.bone ? new THREE5.Bone() : null, [node.bone]);
|
|
1573
|
+
useEffect(() => {
|
|
1574
|
+
if (!node.bone || !boneStore || !boneObject) return;
|
|
1575
|
+
return boneStore.register(node.bone, boneObject);
|
|
1576
|
+
}, [node.bone, boneStore, boneObject]);
|
|
1577
|
+
useFrame(({ clock }) => {
|
|
1578
|
+
if (!animated || !ref.current) return;
|
|
1579
|
+
const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
|
|
1580
|
+
if (!state) return;
|
|
1581
|
+
const cell = projector.cellSize;
|
|
1582
|
+
ref.current.position.set(
|
|
1583
|
+
world[0] + state.offset[0] * cell,
|
|
1584
|
+
world[1] + state.offset[2] * cell,
|
|
1585
|
+
world[2] + state.offset[1] * cell
|
|
1586
|
+
);
|
|
1587
|
+
ref.current.rotation.set(
|
|
1588
|
+
baseRotation[0] + state.rotate[0],
|
|
1589
|
+
baseRotation[1] + state.rotate[1],
|
|
1590
|
+
baseRotation[2] + state.rotate[2]
|
|
1591
|
+
);
|
|
1592
|
+
ref.current.scale.setScalar(s * state.scale);
|
|
1593
|
+
});
|
|
1594
|
+
const children = /* @__PURE__ */ jsxs("group", { ref, position: world, rotation: baseRotation, scale: [s, s, s], children: [
|
|
1595
|
+
boneObject && /* @__PURE__ */ jsx("primitive", { object: boneObject }),
|
|
1596
|
+
node.items.map((item, i) => /* @__PURE__ */ jsx(Drawable3D, { node: item, projector: inner, groupOpacity: opacity }, i))
|
|
1597
|
+
] });
|
|
1598
|
+
return scopedStore ? /* @__PURE__ */ jsx(BoneRegistryContext.Provider, { value: scopedStore, children }) : children;
|
|
1599
|
+
}
|
|
1377
1600
|
|
|
1378
1601
|
// lib/drawable/three/game3dTheme.ts
|
|
1379
1602
|
var GRID_COLORS_3D = {
|
|
@@ -1386,10 +1609,26 @@ function cn(...inputs) {
|
|
|
1386
1609
|
}
|
|
1387
1610
|
var DEFAULT_GRID_CONFIG = {
|
|
1388
1611
|
cellSize: 1};
|
|
1612
|
+
function CameraPose({
|
|
1613
|
+
position,
|
|
1614
|
+
fov,
|
|
1615
|
+
controls
|
|
1616
|
+
}) {
|
|
1617
|
+
const camera = useThree((s) => s.camera);
|
|
1618
|
+
useEffect(() => {
|
|
1619
|
+
camera.position.set(position[0], position[1], position[2]);
|
|
1620
|
+
if (camera.isPerspectiveCamera) {
|
|
1621
|
+
camera.fov = fov;
|
|
1622
|
+
camera.updateProjectionMatrix();
|
|
1623
|
+
}
|
|
1624
|
+
controls.current?.update();
|
|
1625
|
+
}, [camera, position, fov, controls]);
|
|
1626
|
+
return null;
|
|
1627
|
+
}
|
|
1389
1628
|
function RoomEnvironment3D() {
|
|
1390
1629
|
const { gl, scene } = useThree(({ gl: gl2, scene: scene2 }) => ({ gl: gl2, scene: scene2 }));
|
|
1391
1630
|
useEffect(() => {
|
|
1392
|
-
const pmremGenerator = new
|
|
1631
|
+
const pmremGenerator = new THREE5.PMREMGenerator(gl);
|
|
1393
1632
|
const envTexture = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
|
|
1394
1633
|
scene.environment = envTexture;
|
|
1395
1634
|
return () => {
|
|
@@ -1425,6 +1664,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1425
1664
|
keyUpMap,
|
|
1426
1665
|
pixelsPerUnit,
|
|
1427
1666
|
fov,
|
|
1667
|
+
azimuth,
|
|
1428
1668
|
lighting,
|
|
1429
1669
|
post,
|
|
1430
1670
|
children,
|
|
@@ -1505,6 +1745,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1505
1745
|
}),
|
|
1506
1746
|
[gridBounds, cellSize]
|
|
1507
1747
|
);
|
|
1748
|
+
const boneStore = useMemo(() => new BoneStore(), []);
|
|
1508
1749
|
const drawableProjector = useMemo(
|
|
1509
1750
|
() => create3DProjector({
|
|
1510
1751
|
cellSize: gridConfig.cellSize,
|
|
@@ -1517,7 +1758,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1517
1758
|
getCameraPosition: () => {
|
|
1518
1759
|
if (controlsRef.current) {
|
|
1519
1760
|
const pos = controlsRef.current.object.position;
|
|
1520
|
-
return new
|
|
1761
|
+
return new THREE5.Vector3(pos.x, pos.y, pos.z);
|
|
1521
1762
|
}
|
|
1522
1763
|
return null;
|
|
1523
1764
|
},
|
|
@@ -1557,18 +1798,28 @@ var Canvas3DHost = forwardRef(
|
|
|
1557
1798
|
const cz = cameraTarget[2];
|
|
1558
1799
|
const d = size * 1;
|
|
1559
1800
|
const fovDeg = fov ?? 45;
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1801
|
+
const base = (() => {
|
|
1802
|
+
switch (cameraMode) {
|
|
1803
|
+
case "isometric":
|
|
1804
|
+
return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
|
|
1805
|
+
case "top-down":
|
|
1806
|
+
return { position: [cx, d * 2, cz + d * 0.35], fov: fovDeg };
|
|
1807
|
+
case "front":
|
|
1808
|
+
return { position: [cx, d * 0.32, cz + d * 1.15], fov: fovDeg };
|
|
1809
|
+
case "follow":
|
|
1810
|
+
return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
|
|
1811
|
+
case "perspective":
|
|
1812
|
+
default:
|
|
1813
|
+
return { position: [cx + d, d, cz + d], fov: fovDeg };
|
|
1814
|
+
}
|
|
1815
|
+
})();
|
|
1816
|
+
if (!azimuth) return base;
|
|
1817
|
+
const ox = base.position[0] - cx;
|
|
1818
|
+
const oz = base.position[2] - cz;
|
|
1819
|
+
const c = Math.cos(azimuth);
|
|
1820
|
+
const s = Math.sin(azimuth);
|
|
1821
|
+
return { position: [cx + ox * c - oz * s, base.position[1], cz + ox * s + oz * c], fov: base.fov };
|
|
1822
|
+
}, [cameraMode, gridBounds, cellSize, cameraTarget, fov, azimuth]);
|
|
1572
1823
|
const followWorld = useMemo(() => {
|
|
1573
1824
|
if (followTarget) return drawableProjector.toWorld(followTarget);
|
|
1574
1825
|
return cameraTarget;
|
|
@@ -1628,6 +1879,8 @@ var Canvas3DHost = forwardRef(
|
|
|
1628
1879
|
Canvas,
|
|
1629
1880
|
{
|
|
1630
1881
|
shadows,
|
|
1882
|
+
flat: lighting?.toneMapping === "none",
|
|
1883
|
+
gl: { preserveDrawingBuffer: true },
|
|
1631
1884
|
camera: {
|
|
1632
1885
|
position: cameraConfig.position,
|
|
1633
1886
|
fov: cameraConfig.fov,
|
|
@@ -1642,6 +1895,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1642
1895
|
},
|
|
1643
1896
|
children: [
|
|
1644
1897
|
/* @__PURE__ */ jsx(CameraController3D, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
1898
|
+
/* @__PURE__ */ jsx(CameraPose, { position: cameraConfig.position, fov: cameraConfig.fov, controls: controlsRef }),
|
|
1645
1899
|
(cameraMode === "follow" || cameraMode === "chase") && /* @__PURE__ */ jsx(FollowCamera3D, { target: followWorld, offset: followOffset }),
|
|
1646
1900
|
/* @__PURE__ */ jsx(
|
|
1647
1901
|
Lighting3D,
|
|
@@ -1686,7 +1940,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1686
1940
|
fadeStrength: 1
|
|
1687
1941
|
}
|
|
1688
1942
|
),
|
|
1689
|
-
allDrawables.length > 0 && /* @__PURE__ */ jsx("group", { children: allDrawables.map((node, i) => /* @__PURE__ */ jsx(Drawable3D, { node, projector: drawableProjector }, i)) }),
|
|
1943
|
+
allDrawables.length > 0 && /* @__PURE__ */ jsx(BoneRegistryContext.Provider, { value: boneStore, children: /* @__PURE__ */ jsx("group", { children: allDrawables.map((node, i) => /* @__PURE__ */ jsx(Drawable3D, { node, projector: drawableProjector }, i)) }) }),
|
|
1690
1944
|
(tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxs(
|
|
1691
1945
|
"mesh",
|
|
1692
1946
|
{
|
|
@@ -1722,7 +1976,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1722
1976
|
dampingFactor: 0.05,
|
|
1723
1977
|
enableZoom: true,
|
|
1724
1978
|
enablePan: true,
|
|
1725
|
-
touches: { ONE:
|
|
1979
|
+
touches: { ONE: THREE5.TOUCH.ROTATE, TWO: THREE5.TOUCH.DOLLY_PAN },
|
|
1726
1980
|
minDistance: 2,
|
|
1727
1981
|
maxDistance: 100,
|
|
1728
1982
|
maxPolarAngle: Math.PI / 2 - 0.1
|
|
@@ -1745,15 +1999,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
|
1745
1999
|
if (initializedRef.current) return;
|
|
1746
2000
|
initializedRef.current = true;
|
|
1747
2001
|
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
1748
|
-
scene.background = new
|
|
2002
|
+
scene.background = new THREE5.Color(background);
|
|
1749
2003
|
} else {
|
|
1750
|
-
const loader = new
|
|
2004
|
+
const loader = new THREE5.TextureLoader();
|
|
1751
2005
|
loader.load(background, (texture) => {
|
|
1752
2006
|
scene.background = texture;
|
|
1753
2007
|
});
|
|
1754
2008
|
}
|
|
1755
2009
|
if (fog) {
|
|
1756
|
-
scene.fog = new
|
|
2010
|
+
scene.fog = new THREE5.Fog(fog.color, fog.near, fog.far);
|
|
1757
2011
|
}
|
|
1758
2012
|
return () => {
|
|
1759
2013
|
scene.background = null;
|
|
@@ -1776,14 +2030,14 @@ var Camera3D = forwardRef(
|
|
|
1776
2030
|
}, ref) => {
|
|
1777
2031
|
const { camera, set, viewport } = useThree();
|
|
1778
2032
|
const controlsRef = useRef(null);
|
|
1779
|
-
const initialPosition = useRef(new
|
|
1780
|
-
const initialTarget = useRef(new
|
|
2033
|
+
const initialPosition = useRef(new THREE5.Vector3(...position));
|
|
2034
|
+
const initialTarget = useRef(new THREE5.Vector3(...target));
|
|
1781
2035
|
useEffect(() => {
|
|
1782
2036
|
let newCamera;
|
|
1783
2037
|
if (mode === "isometric") {
|
|
1784
2038
|
const aspect = viewport.aspect;
|
|
1785
2039
|
const size = 10 / zoom;
|
|
1786
|
-
newCamera = new
|
|
2040
|
+
newCamera = new THREE5.OrthographicCamera(
|
|
1787
2041
|
-size * aspect,
|
|
1788
2042
|
size * aspect,
|
|
1789
2043
|
size,
|
|
@@ -1792,7 +2046,7 @@ var Camera3D = forwardRef(
|
|
|
1792
2046
|
1e3
|
|
1793
2047
|
);
|
|
1794
2048
|
} else {
|
|
1795
|
-
newCamera = new
|
|
2049
|
+
newCamera = new THREE5.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
|
|
1796
2050
|
}
|
|
1797
2051
|
newCamera.position.copy(initialPosition.current);
|
|
1798
2052
|
newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
|
|
@@ -1833,8 +2087,8 @@ var Camera3D = forwardRef(
|
|
|
1833
2087
|
}
|
|
1834
2088
|
},
|
|
1835
2089
|
getViewBounds: () => {
|
|
1836
|
-
const min = new
|
|
1837
|
-
const max = new
|
|
2090
|
+
const min = new THREE5.Vector3(-10, -10, -10);
|
|
2091
|
+
const max = new THREE5.Vector3(10, 10, 10);
|
|
1838
2092
|
return { min, max };
|
|
1839
2093
|
}
|
|
1840
2094
|
}));
|
|
@@ -1876,7 +2130,7 @@ var AssetLoader = class {
|
|
|
1876
2130
|
__publicField(this, "textureCache");
|
|
1877
2131
|
__publicField(this, "loadingPromises");
|
|
1878
2132
|
this.objLoader = new OBJLoader();
|
|
1879
|
-
this.textureLoader = new
|
|
2133
|
+
this.textureLoader = new THREE5.TextureLoader();
|
|
1880
2134
|
this.modelCache = /* @__PURE__ */ new Map();
|
|
1881
2135
|
this.textureCache = /* @__PURE__ */ new Map();
|
|
1882
2136
|
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
@@ -1950,7 +2204,7 @@ var AssetLoader = class {
|
|
|
1950
2204
|
return this.loadingPromises.get(`texture:${url}`);
|
|
1951
2205
|
}
|
|
1952
2206
|
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
1953
|
-
texture.colorSpace =
|
|
2207
|
+
texture.colorSpace = THREE5.SRGBColorSpace;
|
|
1954
2208
|
this.textureCache.set(url, texture);
|
|
1955
2209
|
this.loadingPromises.delete(`texture:${url}`);
|
|
1956
2210
|
return texture;
|
|
@@ -2024,7 +2278,7 @@ var AssetLoader = class {
|
|
|
2024
2278
|
});
|
|
2025
2279
|
this.modelCache.forEach((model) => {
|
|
2026
2280
|
model.scene.traverse((child) => {
|
|
2027
|
-
if (child instanceof
|
|
2281
|
+
if (child instanceof THREE5.Mesh) {
|
|
2028
2282
|
child.geometry.dispose();
|
|
2029
2283
|
if (Array.isArray(child.material)) {
|
|
2030
2284
|
child.material.forEach((m) => m.dispose());
|
|
@@ -2074,21 +2328,21 @@ function useThree5(options = {}) {
|
|
|
2074
2328
|
const [isReady, setIsReady] = useState(false);
|
|
2075
2329
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
2076
2330
|
const initialCameraPosition = useMemo(
|
|
2077
|
-
() => new
|
|
2331
|
+
() => new THREE5.Vector3(...opts.cameraPosition),
|
|
2078
2332
|
[]
|
|
2079
2333
|
);
|
|
2080
2334
|
useEffect(() => {
|
|
2081
2335
|
if (!containerRef.current) return;
|
|
2082
2336
|
const container = containerRef.current;
|
|
2083
2337
|
const { clientWidth, clientHeight } = container;
|
|
2084
|
-
const scene = new
|
|
2085
|
-
scene.background = new
|
|
2338
|
+
const scene = new THREE5.Scene();
|
|
2339
|
+
scene.background = new THREE5.Color(opts.backgroundColor);
|
|
2086
2340
|
sceneRef.current = scene;
|
|
2087
2341
|
let camera;
|
|
2088
2342
|
const aspect = clientWidth / clientHeight;
|
|
2089
2343
|
if (opts.cameraMode === "isometric") {
|
|
2090
2344
|
const size = 10;
|
|
2091
|
-
camera = new
|
|
2345
|
+
camera = new THREE5.OrthographicCamera(
|
|
2092
2346
|
-size * aspect,
|
|
2093
2347
|
size * aspect,
|
|
2094
2348
|
size,
|
|
@@ -2097,11 +2351,11 @@ function useThree5(options = {}) {
|
|
|
2097
2351
|
1e3
|
|
2098
2352
|
);
|
|
2099
2353
|
} else {
|
|
2100
|
-
camera = new
|
|
2354
|
+
camera = new THREE5.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
2101
2355
|
}
|
|
2102
2356
|
camera.position.copy(initialCameraPosition);
|
|
2103
2357
|
cameraRef.current = camera;
|
|
2104
|
-
const renderer = new
|
|
2358
|
+
const renderer = new THREE5.WebGLRenderer({
|
|
2105
2359
|
antialias: true,
|
|
2106
2360
|
alpha: true,
|
|
2107
2361
|
canvas: canvasRef.current || void 0
|
|
@@ -2109,7 +2363,7 @@ function useThree5(options = {}) {
|
|
|
2109
2363
|
renderer.setSize(clientWidth, clientHeight);
|
|
2110
2364
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
2111
2365
|
renderer.shadowMap.enabled = opts.shadows;
|
|
2112
|
-
renderer.shadowMap.type =
|
|
2366
|
+
renderer.shadowMap.type = THREE5.PCFSoftShadowMap;
|
|
2113
2367
|
rendererRef.current = renderer;
|
|
2114
2368
|
const controls = new OrbitControls$1(camera, renderer.domElement);
|
|
2115
2369
|
controls.enableDamping = true;
|
|
@@ -2118,16 +2372,16 @@ function useThree5(options = {}) {
|
|
|
2118
2372
|
controls.maxDistance = 100;
|
|
2119
2373
|
controls.maxPolarAngle = Math.PI / 2 - 0.1;
|
|
2120
2374
|
controlsRef.current = controls;
|
|
2121
|
-
const ambientLight = new
|
|
2375
|
+
const ambientLight = new THREE5.AmbientLight(16777215, 0.6);
|
|
2122
2376
|
scene.add(ambientLight);
|
|
2123
|
-
const directionalLight = new
|
|
2377
|
+
const directionalLight = new THREE5.DirectionalLight(16777215, 0.8);
|
|
2124
2378
|
directionalLight.position.set(10, 20, 10);
|
|
2125
2379
|
directionalLight.castShadow = opts.shadows;
|
|
2126
2380
|
directionalLight.shadow.mapSize.width = 2048;
|
|
2127
2381
|
directionalLight.shadow.mapSize.height = 2048;
|
|
2128
2382
|
scene.add(directionalLight);
|
|
2129
2383
|
if (opts.showGrid) {
|
|
2130
|
-
const gridHelper = new
|
|
2384
|
+
const gridHelper = new THREE5.GridHelper(
|
|
2131
2385
|
opts.gridSize,
|
|
2132
2386
|
opts.gridSize,
|
|
2133
2387
|
4473924,
|
|
@@ -2145,10 +2399,10 @@ function useThree5(options = {}) {
|
|
|
2145
2399
|
const handleResize = () => {
|
|
2146
2400
|
const { clientWidth: width, clientHeight: height } = container;
|
|
2147
2401
|
setDimensions({ width, height });
|
|
2148
|
-
if (camera instanceof
|
|
2402
|
+
if (camera instanceof THREE5.PerspectiveCamera) {
|
|
2149
2403
|
camera.aspect = width / height;
|
|
2150
2404
|
camera.updateProjectionMatrix();
|
|
2151
|
-
} else if (camera instanceof
|
|
2405
|
+
} else if (camera instanceof THREE5.OrthographicCamera) {
|
|
2152
2406
|
const aspect2 = width / height;
|
|
2153
2407
|
const size = 10;
|
|
2154
2408
|
camera.left = -size * aspect2;
|
|
@@ -2179,7 +2433,7 @@ function useThree5(options = {}) {
|
|
|
2179
2433
|
let newCamera;
|
|
2180
2434
|
if (opts.cameraMode === "isometric") {
|
|
2181
2435
|
const size = 10;
|
|
2182
|
-
newCamera = new
|
|
2436
|
+
newCamera = new THREE5.OrthographicCamera(
|
|
2183
2437
|
-size * aspect,
|
|
2184
2438
|
size * aspect,
|
|
2185
2439
|
size,
|
|
@@ -2188,7 +2442,7 @@ function useThree5(options = {}) {
|
|
|
2188
2442
|
1e3
|
|
2189
2443
|
);
|
|
2190
2444
|
} else {
|
|
2191
|
-
newCamera = new
|
|
2445
|
+
newCamera = new THREE5.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
2192
2446
|
}
|
|
2193
2447
|
newCamera.position.copy(currentPos);
|
|
2194
2448
|
cameraRef.current = newCamera;
|
|
@@ -2518,8 +2772,8 @@ function useSceneGraph() {
|
|
|
2518
2772
|
}
|
|
2519
2773
|
function useRaycaster(options) {
|
|
2520
2774
|
const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
|
|
2521
|
-
const raycaster = useRef(new
|
|
2522
|
-
const mouse = useRef(new
|
|
2775
|
+
const raycaster = useRef(new THREE5.Raycaster());
|
|
2776
|
+
const mouse = useRef(new THREE5.Vector2());
|
|
2523
2777
|
const clientToNDC = useCallback(
|
|
2524
2778
|
(clientX, clientY) => {
|
|
2525
2779
|
if (!canvas) {
|
|
@@ -2589,8 +2843,8 @@ function useRaycaster(options) {
|
|
|
2589
2843
|
const ndc = clientToNDC(clientX, clientY);
|
|
2590
2844
|
mouse.current.set(ndc.x, ndc.y);
|
|
2591
2845
|
raycaster.current.setFromCamera(mouse.current, camera);
|
|
2592
|
-
const plane = new
|
|
2593
|
-
const target = new
|
|
2846
|
+
const plane = new THREE5.Plane(new THREE5.Vector3(0, 1, 0), 0);
|
|
2847
|
+
const target = new THREE5.Vector3();
|
|
2594
2848
|
const intersection = raycaster.current.ray.intersectPlane(plane, target);
|
|
2595
2849
|
if (intersection) {
|
|
2596
2850
|
const gridX = Math.round((target.x - offsetX) / cellSize);
|
|
@@ -2628,7 +2882,7 @@ function useRaycaster(options) {
|
|
|
2628
2882
|
return {
|
|
2629
2883
|
gridX: gridCoords.x,
|
|
2630
2884
|
gridZ: gridCoords.z,
|
|
2631
|
-
worldPosition: new
|
|
2885
|
+
worldPosition: new THREE5.Vector3(
|
|
2632
2886
|
gridCoords.x * cellSize + offsetX,
|
|
2633
2887
|
0,
|
|
2634
2888
|
gridCoords.z * cellSize + offsetZ
|
|
@@ -2658,7 +2912,7 @@ var DEFAULT_CONFIG = {
|
|
|
2658
2912
|
};
|
|
2659
2913
|
function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
|
|
2660
2914
|
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
2661
|
-
return new
|
|
2915
|
+
return new THREE5.Vector3(
|
|
2662
2916
|
gridX * opts.cellSize + opts.offsetX,
|
|
2663
2917
|
opts.elevation,
|
|
2664
2918
|
gridZ * opts.cellSize + opts.offsetZ
|
|
@@ -2672,17 +2926,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
|
|
|
2672
2926
|
};
|
|
2673
2927
|
}
|
|
2674
2928
|
function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
|
|
2675
|
-
const raycaster = new
|
|
2676
|
-
const mouse = new
|
|
2929
|
+
const raycaster = new THREE5.Raycaster();
|
|
2930
|
+
const mouse = new THREE5.Vector2(mouseX, mouseY);
|
|
2677
2931
|
raycaster.setFromCamera(mouse, camera);
|
|
2678
|
-
const plane = new
|
|
2679
|
-
const target = new
|
|
2932
|
+
const plane = new THREE5.Plane(new THREE5.Vector3(0, 1, 0), -planeY);
|
|
2933
|
+
const target = new THREE5.Vector3();
|
|
2680
2934
|
const intersection = raycaster.ray.intersectPlane(plane, target);
|
|
2681
2935
|
return intersection ? target : null;
|
|
2682
2936
|
}
|
|
2683
2937
|
function raycastToObjects(camera, mouseX, mouseY, objects) {
|
|
2684
|
-
const raycaster = new
|
|
2685
|
-
const mouse = new
|
|
2938
|
+
const raycaster = new THREE5.Raycaster();
|
|
2939
|
+
const mouse = new THREE5.Vector2(mouseX, mouseY);
|
|
2686
2940
|
raycaster.setFromCamera(mouse, camera);
|
|
2687
2941
|
const intersects = raycaster.intersectObjects(objects, true);
|
|
2688
2942
|
return intersects.length > 0 ? intersects[0] : null;
|
|
@@ -2734,14 +2988,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
|
|
|
2734
2988
|
return cells;
|
|
2735
2989
|
}
|
|
2736
2990
|
function createGridHighlight(color = 16776960, opacity = 0.3) {
|
|
2737
|
-
const geometry = new
|
|
2738
|
-
const material = new
|
|
2991
|
+
const geometry = new THREE5.PlaneGeometry(0.95, 0.95);
|
|
2992
|
+
const material = new THREE5.MeshBasicMaterial({
|
|
2739
2993
|
color,
|
|
2740
2994
|
transparent: true,
|
|
2741
2995
|
opacity,
|
|
2742
|
-
side:
|
|
2996
|
+
side: THREE5.DoubleSide
|
|
2743
2997
|
});
|
|
2744
|
-
const mesh = new
|
|
2998
|
+
const mesh = new THREE5.Mesh(geometry, material);
|
|
2745
2999
|
mesh.rotation.x = -Math.PI / 2;
|
|
2746
3000
|
mesh.position.y = 0.01;
|
|
2747
3001
|
return mesh;
|
|
@@ -2754,31 +3008,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
|
|
|
2754
3008
|
};
|
|
2755
3009
|
}
|
|
2756
3010
|
function isInFrustum(position, camera, padding = 0) {
|
|
2757
|
-
const frustum = new
|
|
2758
|
-
const projScreenMatrix = new
|
|
3011
|
+
const frustum = new THREE5.Frustum();
|
|
3012
|
+
const projScreenMatrix = new THREE5.Matrix4();
|
|
2759
3013
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2760
3014
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2761
|
-
const sphere = new
|
|
3015
|
+
const sphere = new THREE5.Sphere(position, padding);
|
|
2762
3016
|
return frustum.intersectsSphere(sphere);
|
|
2763
3017
|
}
|
|
2764
3018
|
function filterByFrustum(positions, camera, padding = 0) {
|
|
2765
|
-
const frustum = new
|
|
2766
|
-
const projScreenMatrix = new
|
|
3019
|
+
const frustum = new THREE5.Frustum();
|
|
3020
|
+
const projScreenMatrix = new THREE5.Matrix4();
|
|
2767
3021
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2768
3022
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2769
3023
|
return positions.filter((position) => {
|
|
2770
|
-
const sphere = new
|
|
3024
|
+
const sphere = new THREE5.Sphere(position, padding);
|
|
2771
3025
|
return frustum.intersectsSphere(sphere);
|
|
2772
3026
|
});
|
|
2773
3027
|
}
|
|
2774
3028
|
function getVisibleIndices(positions, camera, padding = 0) {
|
|
2775
|
-
const frustum = new
|
|
2776
|
-
const projScreenMatrix = new
|
|
3029
|
+
const frustum = new THREE5.Frustum();
|
|
3030
|
+
const projScreenMatrix = new THREE5.Matrix4();
|
|
2777
3031
|
const visible = /* @__PURE__ */ new Set();
|
|
2778
3032
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2779
3033
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2780
3034
|
positions.forEach((position, index) => {
|
|
2781
|
-
const sphere = new
|
|
3035
|
+
const sphere = new THREE5.Sphere(position, padding);
|
|
2782
3036
|
if (frustum.intersectsSphere(sphere)) {
|
|
2783
3037
|
visible.add(index);
|
|
2784
3038
|
}
|
|
@@ -2802,7 +3056,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
|
|
|
2802
3056
|
return lodIndices;
|
|
2803
3057
|
}
|
|
2804
3058
|
function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
|
|
2805
|
-
const dummy = new
|
|
3059
|
+
const dummy = new THREE5.Object3D();
|
|
2806
3060
|
let visibleCount = 0;
|
|
2807
3061
|
positions.forEach((position, index) => {
|
|
2808
3062
|
if (visibleIndices.has(index)) {
|
|
@@ -5585,12 +5839,12 @@ function useAvl3DConfig() {
|
|
|
5585
5839
|
}
|
|
5586
5840
|
function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
5587
5841
|
const { camera } = useThree();
|
|
5588
|
-
const targetPosVec = useRef(new
|
|
5589
|
-
const targetLookVec = useRef(new
|
|
5842
|
+
const targetPosVec = useRef(new THREE5.Vector3(...targetPosition));
|
|
5843
|
+
const targetLookVec = useRef(new THREE5.Vector3(...targetLookAt));
|
|
5590
5844
|
const isAnimating = useRef(false);
|
|
5591
5845
|
useEffect(() => {
|
|
5592
|
-
const newTarget = new
|
|
5593
|
-
const newLookAt = new
|
|
5846
|
+
const newTarget = new THREE5.Vector3(...targetPosition);
|
|
5847
|
+
const newLookAt = new THREE5.Vector3(...targetLookAt);
|
|
5594
5848
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
5595
5849
|
targetPosVec.current.copy(newTarget);
|
|
5596
5850
|
targetLookVec.current.copy(newLookAt);
|
|
@@ -5605,9 +5859,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
|
5605
5859
|
useFrame((_, delta) => {
|
|
5606
5860
|
if (!isAnimating.current) return;
|
|
5607
5861
|
const speed = 3;
|
|
5608
|
-
camera.position.x =
|
|
5609
|
-
camera.position.y =
|
|
5610
|
-
camera.position.z =
|
|
5862
|
+
camera.position.x = THREE5.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
|
|
5863
|
+
camera.position.y = THREE5.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
|
|
5864
|
+
camera.position.z = THREE5.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
|
|
5611
5865
|
camera.lookAt(targetLookVec.current);
|
|
5612
5866
|
const dist = camera.position.distanceTo(targetPosVec.current);
|
|
5613
5867
|
if (dist < 0.05) {
|
|
@@ -5622,7 +5876,7 @@ function SceneFade({ animating, children }) {
|
|
|
5622
5876
|
useFrame((_, delta) => {
|
|
5623
5877
|
if (!groupRef.current) return;
|
|
5624
5878
|
const target = animating ? 0 : 1;
|
|
5625
|
-
opacityRef.current =
|
|
5879
|
+
opacityRef.current = THREE5.MathUtils.damp(opacityRef.current, target, 5, delta);
|
|
5626
5880
|
groupRef.current.visible = opacityRef.current > 0.05;
|
|
5627
5881
|
const s = 0.9 + opacityRef.current * 0.1;
|
|
5628
5882
|
groupRef.current.scale.setScalar(s);
|