@almadar/ui 5.140.0 → 5.141.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 +146 -15
- package/dist/avl/index.js +146 -15
- package/dist/{cn-Do5Ra3l3.d.ts → cn-BDwC4QpH.d.ts} +1 -1
- package/dist/{cn-DkGYzPOg.d.cts → cn-rwc3svaX.d.cts} +1 -1
- package/dist/components/index.cjs +146 -15
- package/dist/components/index.d.cts +3 -3
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +146 -15
- package/dist/lib/drawable/three/index.cjs +360 -137
- package/dist/lib/drawable/three/index.d.cts +3 -1
- package/dist/lib/drawable/three/index.d.ts +3 -1
- package/dist/lib/drawable/three/index.js +335 -112
- package/dist/lib/index.d.cts +2 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/{paintDispatch-B5pPeSEp.d.cts → paintDispatch-BQn5Lyx1.d.cts} +160 -3
- package/dist/{paintDispatch-B5pPeSEp.d.ts → paintDispatch-BQn5Lyx1.d.ts} +160 -3
- package/dist/providers/index.cjs +146 -15
- package/dist/providers/index.js +146 -15
- package/dist/runtime/index.cjs +146 -15
- package/dist/runtime/index.js +146 -15
- package/package.json +4 -4
|
@@ -2,7 +2,7 @@ import React3, { forwardRef, useRef, useState, useEffect, useMemo, useImperative
|
|
|
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 THREE10 from 'three';
|
|
6
6
|
import { Vector3, QuadraticBezierCurve3, MathUtils, Quaternion } from 'three';
|
|
7
7
|
import { Grid, OrbitControls, Billboard, Text, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
|
|
8
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -165,6 +165,7 @@ function collectDrawnItems(nodes) {
|
|
|
165
165
|
case "draw-shape":
|
|
166
166
|
case "draw-text":
|
|
167
167
|
case "draw-group":
|
|
168
|
+
case "draw-mesh":
|
|
168
169
|
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
|
|
169
170
|
break;
|
|
170
171
|
case "draw-sprite-layer":
|
|
@@ -490,7 +491,7 @@ function Lighting3D({
|
|
|
490
491
|
"directionalLightHelper",
|
|
491
492
|
{
|
|
492
493
|
args: [
|
|
493
|
-
new
|
|
494
|
+
new THREE10.DirectionalLight(directionalColor, directionalIntensity),
|
|
494
495
|
5
|
|
495
496
|
]
|
|
496
497
|
}
|
|
@@ -517,8 +518,8 @@ function FollowCamera3D({
|
|
|
517
518
|
offset
|
|
518
519
|
}) {
|
|
519
520
|
const { camera } = useThree();
|
|
520
|
-
const look = useRef(new
|
|
521
|
-
const goal = useRef(new
|
|
521
|
+
const look = useRef(new THREE10.Vector3(target[0], target[1], target[2]));
|
|
522
|
+
const goal = useRef(new THREE10.Vector3());
|
|
522
523
|
useFrame((_, delta) => {
|
|
523
524
|
const t = Math.min(1, delta * 5);
|
|
524
525
|
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
@@ -529,6 +530,17 @@ function FollowCamera3D({
|
|
|
529
530
|
return null;
|
|
530
531
|
}
|
|
531
532
|
|
|
533
|
+
// lib/drawable/projector3d.ts
|
|
534
|
+
function create3DProjector(opts = {}) {
|
|
535
|
+
const cellSize = opts.cellSize ?? 1;
|
|
536
|
+
const offsetX = opts.offsetX ?? 0;
|
|
537
|
+
const offsetZ = opts.offsetZ ?? 0;
|
|
538
|
+
return {
|
|
539
|
+
cellSize,
|
|
540
|
+
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
532
544
|
// lib/atlasSlice.ts
|
|
533
545
|
var atlasCache = /* @__PURE__ */ new Map();
|
|
534
546
|
function isTilesheet(a) {
|
|
@@ -663,12 +675,12 @@ function ModelLoader({
|
|
|
663
675
|
if (!loadedModel) return null;
|
|
664
676
|
const cloned = clone(loadedModel);
|
|
665
677
|
cloned.updateMatrixWorld(true);
|
|
666
|
-
const tintColor = tint ? new
|
|
678
|
+
const tintColor = tint ? new THREE10.Color(tint) : null;
|
|
667
679
|
cloned.traverse((child) => {
|
|
668
|
-
if (child instanceof
|
|
680
|
+
if (child instanceof THREE10.Mesh) {
|
|
669
681
|
child.castShadow = castShadow;
|
|
670
682
|
child.receiveShadow = receiveShadow;
|
|
671
|
-
if (tintColor && child.material instanceof
|
|
683
|
+
if (tintColor && child.material instanceof THREE10.MeshStandardMaterial) {
|
|
672
684
|
const mat = child.material.clone();
|
|
673
685
|
mat.color.multiply(tintColor);
|
|
674
686
|
child.material = mat;
|
|
@@ -677,7 +689,7 @@ function ModelLoader({
|
|
|
677
689
|
});
|
|
678
690
|
return cloned;
|
|
679
691
|
}, [loadedModel, castShadow, receiveShadow, tint]);
|
|
680
|
-
const mixer = useMemo(() => model ? new
|
|
692
|
+
const mixer = useMemo(() => model ? new THREE10.AnimationMixer(model) : null, [model]);
|
|
681
693
|
useEffect(() => {
|
|
682
694
|
if (!mixer || !animation || clips.length === 0) return;
|
|
683
695
|
const wanted = animation.toLowerCase();
|
|
@@ -694,8 +706,8 @@ function ModelLoader({
|
|
|
694
706
|
});
|
|
695
707
|
const normFactor = useMemo(() => {
|
|
696
708
|
if (!model) return 1;
|
|
697
|
-
const box = new
|
|
698
|
-
const size = new
|
|
709
|
+
const box = new THREE10.Box3().setFromObject(model);
|
|
710
|
+
const size = new THREE10.Vector3();
|
|
699
711
|
box.getSize(size);
|
|
700
712
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
701
713
|
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
@@ -785,7 +797,7 @@ var warnUnsupported3d = (kind) => {
|
|
|
785
797
|
warnedUnsupported.add(kind);
|
|
786
798
|
mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
|
|
787
799
|
};
|
|
788
|
-
var CrossOriginTextureLoader = class extends
|
|
800
|
+
var CrossOriginTextureLoader = class extends THREE10.TextureLoader {
|
|
789
801
|
constructor() {
|
|
790
802
|
super();
|
|
791
803
|
this.crossOrigin = "anonymous";
|
|
@@ -803,7 +815,7 @@ function useBillboardTexture(url) {
|
|
|
803
815
|
url,
|
|
804
816
|
(texture) => {
|
|
805
817
|
if (!active) return;
|
|
806
|
-
texture.colorSpace =
|
|
818
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
807
819
|
setState({ texture, error: false });
|
|
808
820
|
},
|
|
809
821
|
void 0,
|
|
@@ -835,7 +847,7 @@ function useAtlasFrame(asset) {
|
|
|
835
847
|
return { frame: { x: r.sx, y: r.sy, w: r.sw, h: r.sh }, ready: true };
|
|
836
848
|
}, [asset, tick]);
|
|
837
849
|
}
|
|
838
|
-
function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
850
|
+
function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
839
851
|
const { texture, error: textureError } = useBillboardTexture(node.asset.url);
|
|
840
852
|
const { frame: atlasFrame, ready: atlasReady } = useAtlasFrame(node.asset);
|
|
841
853
|
const frame = node.frame ?? atlasFrame;
|
|
@@ -856,7 +868,7 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
856
868
|
}, [texture, frame, node.height, node.width, anchor, cellSize]);
|
|
857
869
|
const groundGeometry = React3.useMemo(() => {
|
|
858
870
|
if (anchor !== "top-left" || !texture || !atlasReady) return null;
|
|
859
|
-
const g = new
|
|
871
|
+
const g = new THREE10.PlaneGeometry(size.width, size.height);
|
|
860
872
|
g.rotateX(-Math.PI / 2);
|
|
861
873
|
return g;
|
|
862
874
|
}, [anchor, texture, atlasReady, size.width, size.height]);
|
|
@@ -864,15 +876,15 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
864
876
|
if (anchor === "top-left") {
|
|
865
877
|
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: [
|
|
866
878
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
867
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
879
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
|
|
868
880
|
] }) });
|
|
869
881
|
}
|
|
870
882
|
return /* @__PURE__ */ jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxs("mesh", { children: [
|
|
871
883
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
872
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
884
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
|
|
873
885
|
] }) });
|
|
874
886
|
}
|
|
875
|
-
texture.magFilter = texture.minFilter =
|
|
887
|
+
texture.magFilter = texture.minFilter = THREE10.NearestFilter;
|
|
876
888
|
texture.needsUpdate = true;
|
|
877
889
|
if (frame) {
|
|
878
890
|
texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
|
|
@@ -885,8 +897,8 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
885
897
|
map: texture,
|
|
886
898
|
transparent: true,
|
|
887
899
|
alphaTest: 0.1,
|
|
888
|
-
side:
|
|
889
|
-
opacity: node.opacity ?? 1
|
|
900
|
+
side: THREE10.DoubleSide,
|
|
901
|
+
opacity: (node.opacity ?? 1) * groupOpacity
|
|
890
902
|
}
|
|
891
903
|
) }) });
|
|
892
904
|
}
|
|
@@ -898,13 +910,13 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
898
910
|
map: texture,
|
|
899
911
|
transparent: true,
|
|
900
912
|
alphaTest: 0.1,
|
|
901
|
-
side:
|
|
913
|
+
side: THREE10.DoubleSide,
|
|
902
914
|
opacity: node.opacity ?? 1
|
|
903
915
|
}
|
|
904
916
|
)
|
|
905
917
|
] }) });
|
|
906
918
|
}
|
|
907
|
-
function Sprite3D({ node, projector }) {
|
|
919
|
+
function Sprite3D({ node, projector, groupOpacity = 1 }) {
|
|
908
920
|
const asset = node.asset;
|
|
909
921
|
if (!asset?.url || !isValidScenePos(node.position)) return null;
|
|
910
922
|
if (asset.dimension === "3d") {
|
|
@@ -918,7 +930,7 @@ function Sprite3D({ node, projector }) {
|
|
|
918
930
|
{
|
|
919
931
|
url: asset.url,
|
|
920
932
|
scale,
|
|
921
|
-
rotation: [0, node.rotation ?? 0, 0],
|
|
933
|
+
rotation: [0, (node.rotation ?? 0) * 180 / Math.PI, 0],
|
|
922
934
|
animation: node.animation,
|
|
923
935
|
fallbackGeometry: "box",
|
|
924
936
|
castShadow: true,
|
|
@@ -926,9 +938,9 @@ function Sprite3D({ node, projector }) {
|
|
|
926
938
|
}
|
|
927
939
|
) });
|
|
928
940
|
}
|
|
929
|
-
return /* @__PURE__ */ jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize });
|
|
941
|
+
return /* @__PURE__ */ jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize, groupOpacity });
|
|
930
942
|
}
|
|
931
|
-
function Shape3D({ node, projector }) {
|
|
943
|
+
function Shape3D({ node, projector, groupOpacity = 1 }) {
|
|
932
944
|
if (!isValidScenePos(node.position)) return null;
|
|
933
945
|
const world = projector.toWorld(node.position);
|
|
934
946
|
const color = node.fill ?? node.stroke ?? "#ffffff";
|
|
@@ -948,7 +960,7 @@ function Shape3D({ node, projector }) {
|
|
|
948
960
|
}
|
|
949
961
|
case "poly": {
|
|
950
962
|
if (!node.points || node.points.length === 0) return null;
|
|
951
|
-
const s = new
|
|
963
|
+
const s = new THREE10.Shape();
|
|
952
964
|
node.points.forEach((p, i) => {
|
|
953
965
|
if (i === 0) s.moveTo(p.x, p.y);
|
|
954
966
|
else s.lineTo(p.x, p.y);
|
|
@@ -966,7 +978,7 @@ function Shape3D({ node, projector }) {
|
|
|
966
978
|
}
|
|
967
979
|
return /* @__PURE__ */ jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [world[0], world[1] + 0.02, world[2]], children: [
|
|
968
980
|
geometry,
|
|
969
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color, transparent: true, opacity: node.opacity ?? 1, side:
|
|
981
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE10.DoubleSide })
|
|
970
982
|
] });
|
|
971
983
|
}
|
|
972
984
|
function Text3D({ node, projector }) {
|
|
@@ -985,37 +997,246 @@ function Text3D({ node, projector }) {
|
|
|
985
997
|
}
|
|
986
998
|
) });
|
|
987
999
|
}
|
|
988
|
-
|
|
1000
|
+
var MESH_SEGMENTS_DEFAULT = 24;
|
|
1001
|
+
function clampSegments(segments) {
|
|
1002
|
+
const s = segments ?? MESH_SEGMENTS_DEFAULT;
|
|
1003
|
+
return Math.min(64, Math.max(3, Math.round(s)));
|
|
1004
|
+
}
|
|
1005
|
+
function isAnimatedMesh(node) {
|
|
1006
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1007
|
+
}
|
|
1008
|
+
var lerp = (a, b, k) => a + (b - a) * k;
|
|
1009
|
+
var NUMERIC_TRACKS = [
|
|
1010
|
+
"offsetX",
|
|
1011
|
+
"offsetY",
|
|
1012
|
+
"offsetZ",
|
|
1013
|
+
"rotateX",
|
|
1014
|
+
"rotateY",
|
|
1015
|
+
"rotateZ",
|
|
1016
|
+
"scale",
|
|
1017
|
+
"opacity",
|
|
1018
|
+
"emissiveIntensity"
|
|
1019
|
+
];
|
|
1020
|
+
function applyMeshAnimation(node, timeMs) {
|
|
1021
|
+
const anim = node.animation;
|
|
1022
|
+
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
|
|
1023
|
+
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
1024
|
+
const cycle = timeMs / anim.durationMs;
|
|
1025
|
+
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
1026
|
+
const trackValue = (key) => {
|
|
1027
|
+
const defined = frames.filter((f) => f[key] !== void 0);
|
|
1028
|
+
if (defined.length === 0) return void 0;
|
|
1029
|
+
let prev;
|
|
1030
|
+
let next;
|
|
1031
|
+
for (const f of defined) {
|
|
1032
|
+
if (f.at <= t) prev = f;
|
|
1033
|
+
else if (!next) next = f;
|
|
1034
|
+
}
|
|
1035
|
+
if (!prev) return defined[0][key];
|
|
1036
|
+
if (!next) return prev[key];
|
|
1037
|
+
const span = next.at - prev.at;
|
|
1038
|
+
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
1039
|
+
const a = prev[key];
|
|
1040
|
+
const b = next[key];
|
|
1041
|
+
if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
|
|
1042
|
+
return a;
|
|
1043
|
+
};
|
|
1044
|
+
const num = {};
|
|
1045
|
+
for (const key of NUMERIC_TRACKS) {
|
|
1046
|
+
const v = trackValue(key);
|
|
1047
|
+
if (v !== void 0) num[key] = v;
|
|
1048
|
+
}
|
|
1049
|
+
return {
|
|
1050
|
+
offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
|
|
1051
|
+
rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
|
|
1052
|
+
scale: num.scale ?? 1,
|
|
1053
|
+
opacity: num.opacity,
|
|
1054
|
+
emissiveIntensity: num.emissiveIntensity,
|
|
1055
|
+
color: trackValue("color"),
|
|
1056
|
+
emissive: trackValue("emissive")
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
function meshGeometry(node, cellSize) {
|
|
1060
|
+
const seg = clampSegments(node.segments);
|
|
1061
|
+
const r = (node.radius ?? 0.4) * cellSize;
|
|
1062
|
+
const w = (node.width ?? 1) * cellSize;
|
|
1063
|
+
const h = (node.height ?? 1) * cellSize;
|
|
1064
|
+
const d = (node.depth ?? node.width ?? 1) * cellSize;
|
|
1065
|
+
switch (node.shape) {
|
|
1066
|
+
case "box":
|
|
1067
|
+
return { element: /* @__PURE__ */ jsx("boxGeometry", { args: [w, h, d] }), lift: h / 2, size: Math.max(w, h, d) };
|
|
1068
|
+
case "sphere":
|
|
1069
|
+
return { element: /* @__PURE__ */ jsx("sphereGeometry", { args: [r, seg, seg] }), lift: r, size: r * 2 };
|
|
1070
|
+
case "capsule":
|
|
1071
|
+
return { element: /* @__PURE__ */ jsx("capsuleGeometry", { args: [r, h, Math.max(2, Math.round(seg / 2)), seg] }), lift: h / 2 + r, size: h + r * 2 };
|
|
1072
|
+
case "cylinder":
|
|
1073
|
+
return {
|
|
1074
|
+
element: /* @__PURE__ */ jsx("cylinderGeometry", { args: [(node.radiusTop ?? node.radius ?? 0.4) * cellSize, (node.radiusBottom ?? node.radius ?? 0.4) * cellSize, h, seg] }),
|
|
1075
|
+
lift: h / 2,
|
|
1076
|
+
size: Math.max(h, r * 2)
|
|
1077
|
+
};
|
|
1078
|
+
case "cone":
|
|
1079
|
+
return { element: /* @__PURE__ */ jsx("coneGeometry", { args: [r, h, seg] }), lift: h / 2, size: Math.max(h, r * 2) };
|
|
1080
|
+
case "torus": {
|
|
1081
|
+
const tube = (node.tube ?? (node.radius ?? 0.4) / 3) * cellSize;
|
|
1082
|
+
return { element: /* @__PURE__ */ jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
|
|
1083
|
+
}
|
|
1084
|
+
case "plane":
|
|
1085
|
+
return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d) };
|
|
1086
|
+
case "circle":
|
|
1087
|
+
return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2 };
|
|
1088
|
+
default:
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
var SIDE_MAP = {
|
|
1093
|
+
front: THREE10.FrontSide,
|
|
1094
|
+
back: THREE10.BackSide,
|
|
1095
|
+
double: THREE10.DoubleSide
|
|
1096
|
+
};
|
|
1097
|
+
function meshMaterial(mat, opacity, ref) {
|
|
1098
|
+
const m = mat ?? {};
|
|
1099
|
+
const common = {
|
|
1100
|
+
ref,
|
|
1101
|
+
color: m.color ?? "#ffffff",
|
|
1102
|
+
transparent: opacity < 1,
|
|
1103
|
+
opacity,
|
|
1104
|
+
side: SIDE_MAP[m.side ?? "front"]
|
|
1105
|
+
};
|
|
1106
|
+
const emissive = m.emissive ? { emissive: m.emissive, emissiveIntensity: m.emissiveIntensity ?? 1 } : {};
|
|
1107
|
+
switch (m.kind ?? "standard") {
|
|
1108
|
+
case "basic":
|
|
1109
|
+
return /* @__PURE__ */ jsx("meshBasicMaterial", { ...common });
|
|
1110
|
+
case "toon":
|
|
1111
|
+
return /* @__PURE__ */ jsx("meshToonMaterial", { ...common, ...emissive });
|
|
1112
|
+
case "physical":
|
|
1113
|
+
return /* @__PURE__ */ jsx(
|
|
1114
|
+
"meshPhysicalMaterial",
|
|
1115
|
+
{
|
|
1116
|
+
...common,
|
|
1117
|
+
...emissive,
|
|
1118
|
+
metalness: m.metalness ?? 0,
|
|
1119
|
+
roughness: m.roughness ?? 0.5,
|
|
1120
|
+
transmission: m.transmission ?? 0,
|
|
1121
|
+
ior: m.ior ?? 1.5,
|
|
1122
|
+
flatShading: m.flatShading ?? false
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
case "standard":
|
|
1126
|
+
default:
|
|
1127
|
+
return /* @__PURE__ */ jsx(
|
|
1128
|
+
"meshStandardMaterial",
|
|
1129
|
+
{
|
|
1130
|
+
...common,
|
|
1131
|
+
...emissive,
|
|
1132
|
+
metalness: m.metalness ?? 0,
|
|
1133
|
+
roughness: m.roughness ?? 0.5,
|
|
1134
|
+
flatShading: m.flatShading ?? false
|
|
1135
|
+
}
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function Mesh3D({
|
|
1140
|
+
node,
|
|
1141
|
+
projector,
|
|
1142
|
+
groupOpacity = 1
|
|
1143
|
+
}) {
|
|
1144
|
+
const groupRef = useRef(null);
|
|
1145
|
+
const meshRef = useRef(null);
|
|
1146
|
+
const materialRef = useRef(null);
|
|
1147
|
+
const animated = isAnimatedMesh(node);
|
|
1148
|
+
const validPos = isValidScenePos(node.position);
|
|
1149
|
+
const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
|
|
1150
|
+
const geo = useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
|
|
1151
|
+
useFrame(({ clock }) => {
|
|
1152
|
+
if (!animated || !groupRef.current) return;
|
|
1153
|
+
const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
|
|
1154
|
+
if (!state) return;
|
|
1155
|
+
const [wx, wy, wz] = baseWorld;
|
|
1156
|
+
groupRef.current.position.set(
|
|
1157
|
+
wx + state.offset[0] * projector.cellSize,
|
|
1158
|
+
wy + state.offset[2] * projector.cellSize,
|
|
1159
|
+
wz + state.offset[1] * projector.cellSize
|
|
1160
|
+
);
|
|
1161
|
+
groupRef.current.scale.setScalar(state.scale);
|
|
1162
|
+
if (meshRef.current) {
|
|
1163
|
+
const base = node.rotation ?? [0, 0, 0];
|
|
1164
|
+
meshRef.current.rotation.set(base[0] + state.rotate[0], base[1] + state.rotate[1], base[2] + state.rotate[2]);
|
|
1165
|
+
}
|
|
1166
|
+
const mat = materialRef.current;
|
|
1167
|
+
if (mat) {
|
|
1168
|
+
const effective = (state.opacity ?? node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
|
|
1169
|
+
mat.opacity = effective;
|
|
1170
|
+
mat.transparent = effective < 1;
|
|
1171
|
+
if (state.color && mat.color) mat.color.set(state.color);
|
|
1172
|
+
if (state.emissive && mat.emissive) mat.emissive.set(state.emissive);
|
|
1173
|
+
if (state.emissiveIntensity !== void 0 && mat.emissiveIntensity !== void 0) {
|
|
1174
|
+
mat.emissiveIntensity = state.emissiveIntensity;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
if (!validPos || !geo) return null;
|
|
1179
|
+
const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
|
|
1180
|
+
const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
|
|
1181
|
+
const rotation = node.rotation ?? [0, 0, 0];
|
|
1182
|
+
const outlineScale = geo.size > 0 ? 1 + (node.outline?.width ?? 0.05) * projector.cellSize / geo.size : 1;
|
|
1183
|
+
return /* @__PURE__ */ jsxs("group", { ref: groupRef, position: baseWorld, children: [
|
|
1184
|
+
/* @__PURE__ */ jsxs(
|
|
1185
|
+
"mesh",
|
|
1186
|
+
{
|
|
1187
|
+
ref: meshRef,
|
|
1188
|
+
position: [0, lift, 0],
|
|
1189
|
+
rotation,
|
|
1190
|
+
castShadow: node.castShadow ?? true,
|
|
1191
|
+
receiveShadow: node.receiveShadow ?? true,
|
|
1192
|
+
children: [
|
|
1193
|
+
geo.element,
|
|
1194
|
+
meshMaterial(node.material, opacity, materialRef)
|
|
1195
|
+
]
|
|
1196
|
+
}
|
|
1197
|
+
),
|
|
1198
|
+
node.outline && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
|
|
1199
|
+
geo.element,
|
|
1200
|
+
/* @__PURE__ */ jsx(
|
|
1201
|
+
"meshBasicMaterial",
|
|
1202
|
+
{
|
|
1203
|
+
color: node.outline.color ?? "#101014",
|
|
1204
|
+
side: THREE10.BackSide,
|
|
1205
|
+
transparent: opacity < 1,
|
|
1206
|
+
opacity
|
|
1207
|
+
}
|
|
1208
|
+
)
|
|
1209
|
+
] })
|
|
1210
|
+
] });
|
|
1211
|
+
}
|
|
1212
|
+
function Drawable3D({ node, projector, groupOpacity = 1 }) {
|
|
989
1213
|
switch (node.type) {
|
|
990
1214
|
case "draw-sprite":
|
|
991
|
-
return /* @__PURE__ */ jsx(Sprite3D, { node, projector });
|
|
1215
|
+
return /* @__PURE__ */ jsx(Sprite3D, { node, projector, groupOpacity });
|
|
992
1216
|
case "draw-shape":
|
|
993
|
-
return /* @__PURE__ */ jsx(Shape3D, { node, projector });
|
|
1217
|
+
return /* @__PURE__ */ jsx(Shape3D, { node, projector, groupOpacity });
|
|
994
1218
|
case "draw-text":
|
|
995
1219
|
return /* @__PURE__ */ jsx(Text3D, { node, projector });
|
|
1220
|
+
case "draw-mesh":
|
|
1221
|
+
return /* @__PURE__ */ jsx(Mesh3D, { node, projector, groupOpacity });
|
|
996
1222
|
case "draw-sprite-layer":
|
|
997
|
-
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Sprite3D, { node: item, projector }, i)) });
|
|
1223
|
+
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Sprite3D, { node: item, projector, groupOpacity }, i)) });
|
|
998
1224
|
case "draw-shape-layer":
|
|
999
|
-
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Shape3D, { node: item, projector }, i)) });
|
|
1225
|
+
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Shape3D, { node: item, projector, groupOpacity }, i)) });
|
|
1000
1226
|
case "draw-text-layer":
|
|
1001
1227
|
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Text3D, { node: item, projector }, i)) });
|
|
1002
|
-
case "draw-group":
|
|
1003
|
-
|
|
1004
|
-
|
|
1228
|
+
case "draw-group": {
|
|
1229
|
+
if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
|
|
1230
|
+
if (node.clip) warnUnsupported3d("draw-group:clip");
|
|
1231
|
+
const world = projector.toWorld(node.position);
|
|
1232
|
+
const inner = create3DProjector({ cellSize: projector.cellSize });
|
|
1233
|
+
const opacity = (node.opacity ?? 1) * groupOpacity;
|
|
1234
|
+
const s = node.scale ?? 1;
|
|
1235
|
+
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)) });
|
|
1236
|
+
}
|
|
1005
1237
|
}
|
|
1006
1238
|
}
|
|
1007
1239
|
|
|
1008
|
-
// lib/drawable/projector3d.ts
|
|
1009
|
-
function create3DProjector(opts = {}) {
|
|
1010
|
-
const cellSize = opts.cellSize ?? 1;
|
|
1011
|
-
const offsetX = opts.offsetX ?? 0;
|
|
1012
|
-
const offsetZ = opts.offsetZ ?? 0;
|
|
1013
|
-
return {
|
|
1014
|
-
cellSize,
|
|
1015
|
-
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
1240
|
// lib/drawable/three/game3dTheme.ts
|
|
1020
1241
|
var GRID_COLORS_3D = {
|
|
1021
1242
|
cell: "#444444",
|
|
@@ -1051,6 +1272,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1051
1272
|
keyMap,
|
|
1052
1273
|
keyUpMap,
|
|
1053
1274
|
pixelsPerUnit,
|
|
1275
|
+
fov,
|
|
1054
1276
|
children,
|
|
1055
1277
|
drawables
|
|
1056
1278
|
}, ref) => {
|
|
@@ -1140,7 +1362,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1140
1362
|
getCameraPosition: () => {
|
|
1141
1363
|
if (controlsRef.current) {
|
|
1142
1364
|
const pos = controlsRef.current.object.position;
|
|
1143
|
-
return new
|
|
1365
|
+
return new THREE10.Vector3(pos.x, pos.y, pos.z);
|
|
1144
1366
|
}
|
|
1145
1367
|
return null;
|
|
1146
1368
|
},
|
|
@@ -1179,18 +1401,19 @@ var Canvas3DHost = forwardRef(
|
|
|
1179
1401
|
const cx = cameraTarget[0];
|
|
1180
1402
|
const cz = cameraTarget[2];
|
|
1181
1403
|
const d = size * 1;
|
|
1404
|
+
const fovDeg = fov ?? 45;
|
|
1182
1405
|
switch (cameraMode) {
|
|
1183
1406
|
case "isometric":
|
|
1184
|
-
return { position: [cx + d, d * 0.8, cz + d], fov:
|
|
1407
|
+
return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
|
|
1185
1408
|
case "top-down":
|
|
1186
|
-
return { position: [cx, d * 2, cz + d * 0.35], fov:
|
|
1409
|
+
return { position: [cx, d * 2, cz + d * 0.35], fov: fovDeg };
|
|
1187
1410
|
case "follow":
|
|
1188
|
-
return { position: [cx, d * 0.5, cz + d], fov:
|
|
1411
|
+
return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
|
|
1189
1412
|
case "perspective":
|
|
1190
1413
|
default:
|
|
1191
|
-
return { position: [cx + d, d, cz + d], fov:
|
|
1414
|
+
return { position: [cx + d, d, cz + d], fov: fovDeg };
|
|
1192
1415
|
}
|
|
1193
|
-
}, [cameraMode, gridBounds, cellSize, cameraTarget]);
|
|
1416
|
+
}, [cameraMode, gridBounds, cellSize, cameraTarget, fov]);
|
|
1194
1417
|
const followWorld = useMemo(() => {
|
|
1195
1418
|
if (followTarget) return drawableProjector.toWorld(followTarget);
|
|
1196
1419
|
return cameraTarget;
|
|
@@ -1334,7 +1557,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1334
1557
|
dampingFactor: 0.05,
|
|
1335
1558
|
enableZoom: true,
|
|
1336
1559
|
enablePan: true,
|
|
1337
|
-
touches: { ONE:
|
|
1560
|
+
touches: { ONE: THREE10.TOUCH.ROTATE, TWO: THREE10.TOUCH.DOLLY_PAN },
|
|
1338
1561
|
minDistance: 2,
|
|
1339
1562
|
maxDistance: 100,
|
|
1340
1563
|
maxPolarAngle: Math.PI / 2 - 0.1
|
|
@@ -1357,15 +1580,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
|
1357
1580
|
if (initializedRef.current) return;
|
|
1358
1581
|
initializedRef.current = true;
|
|
1359
1582
|
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
1360
|
-
scene.background = new
|
|
1583
|
+
scene.background = new THREE10.Color(background);
|
|
1361
1584
|
} else {
|
|
1362
|
-
const loader = new
|
|
1585
|
+
const loader = new THREE10.TextureLoader();
|
|
1363
1586
|
loader.load(background, (texture) => {
|
|
1364
1587
|
scene.background = texture;
|
|
1365
1588
|
});
|
|
1366
1589
|
}
|
|
1367
1590
|
if (fog) {
|
|
1368
|
-
scene.fog = new
|
|
1591
|
+
scene.fog = new THREE10.Fog(fog.color, fog.near, fog.far);
|
|
1369
1592
|
}
|
|
1370
1593
|
return () => {
|
|
1371
1594
|
scene.background = null;
|
|
@@ -1388,14 +1611,14 @@ var Camera3D = forwardRef(
|
|
|
1388
1611
|
}, ref) => {
|
|
1389
1612
|
const { camera, set, viewport } = useThree();
|
|
1390
1613
|
const controlsRef = useRef(null);
|
|
1391
|
-
const initialPosition = useRef(new
|
|
1392
|
-
const initialTarget = useRef(new
|
|
1614
|
+
const initialPosition = useRef(new THREE10.Vector3(...position));
|
|
1615
|
+
const initialTarget = useRef(new THREE10.Vector3(...target));
|
|
1393
1616
|
useEffect(() => {
|
|
1394
1617
|
let newCamera;
|
|
1395
1618
|
if (mode === "isometric") {
|
|
1396
1619
|
const aspect = viewport.aspect;
|
|
1397
1620
|
const size = 10 / zoom;
|
|
1398
|
-
newCamera = new
|
|
1621
|
+
newCamera = new THREE10.OrthographicCamera(
|
|
1399
1622
|
-size * aspect,
|
|
1400
1623
|
size * aspect,
|
|
1401
1624
|
size,
|
|
@@ -1404,7 +1627,7 @@ var Camera3D = forwardRef(
|
|
|
1404
1627
|
1e3
|
|
1405
1628
|
);
|
|
1406
1629
|
} else {
|
|
1407
|
-
newCamera = new
|
|
1630
|
+
newCamera = new THREE10.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
|
|
1408
1631
|
}
|
|
1409
1632
|
newCamera.position.copy(initialPosition.current);
|
|
1410
1633
|
newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
|
|
@@ -1445,8 +1668,8 @@ var Camera3D = forwardRef(
|
|
|
1445
1668
|
}
|
|
1446
1669
|
},
|
|
1447
1670
|
getViewBounds: () => {
|
|
1448
|
-
const min = new
|
|
1449
|
-
const max = new
|
|
1671
|
+
const min = new THREE10.Vector3(-10, -10, -10);
|
|
1672
|
+
const max = new THREE10.Vector3(10, 10, 10);
|
|
1450
1673
|
return { min, max };
|
|
1451
1674
|
}
|
|
1452
1675
|
}));
|
|
@@ -1488,7 +1711,7 @@ var AssetLoader = class {
|
|
|
1488
1711
|
__publicField(this, "textureCache");
|
|
1489
1712
|
__publicField(this, "loadingPromises");
|
|
1490
1713
|
this.objLoader = new OBJLoader();
|
|
1491
|
-
this.textureLoader = new
|
|
1714
|
+
this.textureLoader = new THREE10.TextureLoader();
|
|
1492
1715
|
this.modelCache = /* @__PURE__ */ new Map();
|
|
1493
1716
|
this.textureCache = /* @__PURE__ */ new Map();
|
|
1494
1717
|
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
@@ -1562,7 +1785,7 @@ var AssetLoader = class {
|
|
|
1562
1785
|
return this.loadingPromises.get(`texture:${url}`);
|
|
1563
1786
|
}
|
|
1564
1787
|
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
1565
|
-
texture.colorSpace =
|
|
1788
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
1566
1789
|
this.textureCache.set(url, texture);
|
|
1567
1790
|
this.loadingPromises.delete(`texture:${url}`);
|
|
1568
1791
|
return texture;
|
|
@@ -1636,7 +1859,7 @@ var AssetLoader = class {
|
|
|
1636
1859
|
});
|
|
1637
1860
|
this.modelCache.forEach((model) => {
|
|
1638
1861
|
model.scene.traverse((child) => {
|
|
1639
|
-
if (child instanceof
|
|
1862
|
+
if (child instanceof THREE10.Mesh) {
|
|
1640
1863
|
child.geometry.dispose();
|
|
1641
1864
|
if (Array.isArray(child.material)) {
|
|
1642
1865
|
child.material.forEach((m) => m.dispose());
|
|
@@ -1686,21 +1909,21 @@ function useThree4(options = {}) {
|
|
|
1686
1909
|
const [isReady, setIsReady] = useState(false);
|
|
1687
1910
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
1688
1911
|
const initialCameraPosition = useMemo(
|
|
1689
|
-
() => new
|
|
1912
|
+
() => new THREE10.Vector3(...opts.cameraPosition),
|
|
1690
1913
|
[]
|
|
1691
1914
|
);
|
|
1692
1915
|
useEffect(() => {
|
|
1693
1916
|
if (!containerRef.current) return;
|
|
1694
1917
|
const container = containerRef.current;
|
|
1695
1918
|
const { clientWidth, clientHeight } = container;
|
|
1696
|
-
const scene = new
|
|
1697
|
-
scene.background = new
|
|
1919
|
+
const scene = new THREE10.Scene();
|
|
1920
|
+
scene.background = new THREE10.Color(opts.backgroundColor);
|
|
1698
1921
|
sceneRef.current = scene;
|
|
1699
1922
|
let camera;
|
|
1700
1923
|
const aspect = clientWidth / clientHeight;
|
|
1701
1924
|
if (opts.cameraMode === "isometric") {
|
|
1702
1925
|
const size = 10;
|
|
1703
|
-
camera = new
|
|
1926
|
+
camera = new THREE10.OrthographicCamera(
|
|
1704
1927
|
-size * aspect,
|
|
1705
1928
|
size * aspect,
|
|
1706
1929
|
size,
|
|
@@ -1709,11 +1932,11 @@ function useThree4(options = {}) {
|
|
|
1709
1932
|
1e3
|
|
1710
1933
|
);
|
|
1711
1934
|
} else {
|
|
1712
|
-
camera = new
|
|
1935
|
+
camera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
1713
1936
|
}
|
|
1714
1937
|
camera.position.copy(initialCameraPosition);
|
|
1715
1938
|
cameraRef.current = camera;
|
|
1716
|
-
const renderer = new
|
|
1939
|
+
const renderer = new THREE10.WebGLRenderer({
|
|
1717
1940
|
antialias: true,
|
|
1718
1941
|
alpha: true,
|
|
1719
1942
|
canvas: canvasRef.current || void 0
|
|
@@ -1721,7 +1944,7 @@ function useThree4(options = {}) {
|
|
|
1721
1944
|
renderer.setSize(clientWidth, clientHeight);
|
|
1722
1945
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
1723
1946
|
renderer.shadowMap.enabled = opts.shadows;
|
|
1724
|
-
renderer.shadowMap.type =
|
|
1947
|
+
renderer.shadowMap.type = THREE10.PCFSoftShadowMap;
|
|
1725
1948
|
rendererRef.current = renderer;
|
|
1726
1949
|
const controls = new OrbitControls$1(camera, renderer.domElement);
|
|
1727
1950
|
controls.enableDamping = true;
|
|
@@ -1730,16 +1953,16 @@ function useThree4(options = {}) {
|
|
|
1730
1953
|
controls.maxDistance = 100;
|
|
1731
1954
|
controls.maxPolarAngle = Math.PI / 2 - 0.1;
|
|
1732
1955
|
controlsRef.current = controls;
|
|
1733
|
-
const ambientLight = new
|
|
1956
|
+
const ambientLight = new THREE10.AmbientLight(16777215, 0.6);
|
|
1734
1957
|
scene.add(ambientLight);
|
|
1735
|
-
const directionalLight = new
|
|
1958
|
+
const directionalLight = new THREE10.DirectionalLight(16777215, 0.8);
|
|
1736
1959
|
directionalLight.position.set(10, 20, 10);
|
|
1737
1960
|
directionalLight.castShadow = opts.shadows;
|
|
1738
1961
|
directionalLight.shadow.mapSize.width = 2048;
|
|
1739
1962
|
directionalLight.shadow.mapSize.height = 2048;
|
|
1740
1963
|
scene.add(directionalLight);
|
|
1741
1964
|
if (opts.showGrid) {
|
|
1742
|
-
const gridHelper = new
|
|
1965
|
+
const gridHelper = new THREE10.GridHelper(
|
|
1743
1966
|
opts.gridSize,
|
|
1744
1967
|
opts.gridSize,
|
|
1745
1968
|
4473924,
|
|
@@ -1757,10 +1980,10 @@ function useThree4(options = {}) {
|
|
|
1757
1980
|
const handleResize = () => {
|
|
1758
1981
|
const { clientWidth: width, clientHeight: height } = container;
|
|
1759
1982
|
setDimensions({ width, height });
|
|
1760
|
-
if (camera instanceof
|
|
1983
|
+
if (camera instanceof THREE10.PerspectiveCamera) {
|
|
1761
1984
|
camera.aspect = width / height;
|
|
1762
1985
|
camera.updateProjectionMatrix();
|
|
1763
|
-
} else if (camera instanceof
|
|
1986
|
+
} else if (camera instanceof THREE10.OrthographicCamera) {
|
|
1764
1987
|
const aspect2 = width / height;
|
|
1765
1988
|
const size = 10;
|
|
1766
1989
|
camera.left = -size * aspect2;
|
|
@@ -1791,7 +2014,7 @@ function useThree4(options = {}) {
|
|
|
1791
2014
|
let newCamera;
|
|
1792
2015
|
if (opts.cameraMode === "isometric") {
|
|
1793
2016
|
const size = 10;
|
|
1794
|
-
newCamera = new
|
|
2017
|
+
newCamera = new THREE10.OrthographicCamera(
|
|
1795
2018
|
-size * aspect,
|
|
1796
2019
|
size * aspect,
|
|
1797
2020
|
size,
|
|
@@ -1800,7 +2023,7 @@ function useThree4(options = {}) {
|
|
|
1800
2023
|
1e3
|
|
1801
2024
|
);
|
|
1802
2025
|
} else {
|
|
1803
|
-
newCamera = new
|
|
2026
|
+
newCamera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
1804
2027
|
}
|
|
1805
2028
|
newCamera.position.copy(currentPos);
|
|
1806
2029
|
cameraRef.current = newCamera;
|
|
@@ -2130,8 +2353,8 @@ function useSceneGraph() {
|
|
|
2130
2353
|
}
|
|
2131
2354
|
function useRaycaster(options) {
|
|
2132
2355
|
const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
|
|
2133
|
-
const raycaster = useRef(new
|
|
2134
|
-
const mouse = useRef(new
|
|
2356
|
+
const raycaster = useRef(new THREE10.Raycaster());
|
|
2357
|
+
const mouse = useRef(new THREE10.Vector2());
|
|
2135
2358
|
const clientToNDC = useCallback(
|
|
2136
2359
|
(clientX, clientY) => {
|
|
2137
2360
|
if (!canvas) {
|
|
@@ -2201,8 +2424,8 @@ function useRaycaster(options) {
|
|
|
2201
2424
|
const ndc = clientToNDC(clientX, clientY);
|
|
2202
2425
|
mouse.current.set(ndc.x, ndc.y);
|
|
2203
2426
|
raycaster.current.setFromCamera(mouse.current, camera);
|
|
2204
|
-
const plane = new
|
|
2205
|
-
const target = new
|
|
2427
|
+
const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), 0);
|
|
2428
|
+
const target = new THREE10.Vector3();
|
|
2206
2429
|
const intersection = raycaster.current.ray.intersectPlane(plane, target);
|
|
2207
2430
|
if (intersection) {
|
|
2208
2431
|
const gridX = Math.round((target.x - offsetX) / cellSize);
|
|
@@ -2240,7 +2463,7 @@ function useRaycaster(options) {
|
|
|
2240
2463
|
return {
|
|
2241
2464
|
gridX: gridCoords.x,
|
|
2242
2465
|
gridZ: gridCoords.z,
|
|
2243
|
-
worldPosition: new
|
|
2466
|
+
worldPosition: new THREE10.Vector3(
|
|
2244
2467
|
gridCoords.x * cellSize + offsetX,
|
|
2245
2468
|
0,
|
|
2246
2469
|
gridCoords.z * cellSize + offsetZ
|
|
@@ -2270,7 +2493,7 @@ var DEFAULT_CONFIG = {
|
|
|
2270
2493
|
};
|
|
2271
2494
|
function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
|
|
2272
2495
|
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
2273
|
-
return new
|
|
2496
|
+
return new THREE10.Vector3(
|
|
2274
2497
|
gridX * opts.cellSize + opts.offsetX,
|
|
2275
2498
|
opts.elevation,
|
|
2276
2499
|
gridZ * opts.cellSize + opts.offsetZ
|
|
@@ -2284,17 +2507,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
|
|
|
2284
2507
|
};
|
|
2285
2508
|
}
|
|
2286
2509
|
function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
|
|
2287
|
-
const raycaster = new
|
|
2288
|
-
const mouse = new
|
|
2510
|
+
const raycaster = new THREE10.Raycaster();
|
|
2511
|
+
const mouse = new THREE10.Vector2(mouseX, mouseY);
|
|
2289
2512
|
raycaster.setFromCamera(mouse, camera);
|
|
2290
|
-
const plane = new
|
|
2291
|
-
const target = new
|
|
2513
|
+
const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), -planeY);
|
|
2514
|
+
const target = new THREE10.Vector3();
|
|
2292
2515
|
const intersection = raycaster.ray.intersectPlane(plane, target);
|
|
2293
2516
|
return intersection ? target : null;
|
|
2294
2517
|
}
|
|
2295
2518
|
function raycastToObjects(camera, mouseX, mouseY, objects) {
|
|
2296
|
-
const raycaster = new
|
|
2297
|
-
const mouse = new
|
|
2519
|
+
const raycaster = new THREE10.Raycaster();
|
|
2520
|
+
const mouse = new THREE10.Vector2(mouseX, mouseY);
|
|
2298
2521
|
raycaster.setFromCamera(mouse, camera);
|
|
2299
2522
|
const intersects = raycaster.intersectObjects(objects, true);
|
|
2300
2523
|
return intersects.length > 0 ? intersects[0] : null;
|
|
@@ -2346,14 +2569,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
|
|
|
2346
2569
|
return cells;
|
|
2347
2570
|
}
|
|
2348
2571
|
function createGridHighlight(color = 16776960, opacity = 0.3) {
|
|
2349
|
-
const geometry = new
|
|
2350
|
-
const material = new
|
|
2572
|
+
const geometry = new THREE10.PlaneGeometry(0.95, 0.95);
|
|
2573
|
+
const material = new THREE10.MeshBasicMaterial({
|
|
2351
2574
|
color,
|
|
2352
2575
|
transparent: true,
|
|
2353
2576
|
opacity,
|
|
2354
|
-
side:
|
|
2577
|
+
side: THREE10.DoubleSide
|
|
2355
2578
|
});
|
|
2356
|
-
const mesh = new
|
|
2579
|
+
const mesh = new THREE10.Mesh(geometry, material);
|
|
2357
2580
|
mesh.rotation.x = -Math.PI / 2;
|
|
2358
2581
|
mesh.position.y = 0.01;
|
|
2359
2582
|
return mesh;
|
|
@@ -2366,31 +2589,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
|
|
|
2366
2589
|
};
|
|
2367
2590
|
}
|
|
2368
2591
|
function isInFrustum(position, camera, padding = 0) {
|
|
2369
|
-
const frustum = new
|
|
2370
|
-
const projScreenMatrix = new
|
|
2592
|
+
const frustum = new THREE10.Frustum();
|
|
2593
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2371
2594
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2372
2595
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2373
|
-
const sphere = new
|
|
2596
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2374
2597
|
return frustum.intersectsSphere(sphere);
|
|
2375
2598
|
}
|
|
2376
2599
|
function filterByFrustum(positions, camera, padding = 0) {
|
|
2377
|
-
const frustum = new
|
|
2378
|
-
const projScreenMatrix = new
|
|
2600
|
+
const frustum = new THREE10.Frustum();
|
|
2601
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2379
2602
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2380
2603
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2381
2604
|
return positions.filter((position) => {
|
|
2382
|
-
const sphere = new
|
|
2605
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2383
2606
|
return frustum.intersectsSphere(sphere);
|
|
2384
2607
|
});
|
|
2385
2608
|
}
|
|
2386
2609
|
function getVisibleIndices(positions, camera, padding = 0) {
|
|
2387
|
-
const frustum = new
|
|
2388
|
-
const projScreenMatrix = new
|
|
2610
|
+
const frustum = new THREE10.Frustum();
|
|
2611
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2389
2612
|
const visible = /* @__PURE__ */ new Set();
|
|
2390
2613
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2391
2614
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2392
2615
|
positions.forEach((position, index) => {
|
|
2393
|
-
const sphere = new
|
|
2616
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2394
2617
|
if (frustum.intersectsSphere(sphere)) {
|
|
2395
2618
|
visible.add(index);
|
|
2396
2619
|
}
|
|
@@ -2414,7 +2637,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
|
|
|
2414
2637
|
return lodIndices;
|
|
2415
2638
|
}
|
|
2416
2639
|
function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
|
|
2417
|
-
const dummy = new
|
|
2640
|
+
const dummy = new THREE10.Object3D();
|
|
2418
2641
|
let visibleCount = 0;
|
|
2419
2642
|
positions.forEach((position, index) => {
|
|
2420
2643
|
if (visibleIndices.has(index)) {
|
|
@@ -5197,12 +5420,12 @@ function useAvl3DConfig() {
|
|
|
5197
5420
|
}
|
|
5198
5421
|
function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
5199
5422
|
const { camera } = useThree();
|
|
5200
|
-
const targetPosVec = useRef(new
|
|
5201
|
-
const targetLookVec = useRef(new
|
|
5423
|
+
const targetPosVec = useRef(new THREE10.Vector3(...targetPosition));
|
|
5424
|
+
const targetLookVec = useRef(new THREE10.Vector3(...targetLookAt));
|
|
5202
5425
|
const isAnimating = useRef(false);
|
|
5203
5426
|
useEffect(() => {
|
|
5204
|
-
const newTarget = new
|
|
5205
|
-
const newLookAt = new
|
|
5427
|
+
const newTarget = new THREE10.Vector3(...targetPosition);
|
|
5428
|
+
const newLookAt = new THREE10.Vector3(...targetLookAt);
|
|
5206
5429
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
5207
5430
|
targetPosVec.current.copy(newTarget);
|
|
5208
5431
|
targetLookVec.current.copy(newLookAt);
|
|
@@ -5217,9 +5440,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
|
5217
5440
|
useFrame((_, delta) => {
|
|
5218
5441
|
if (!isAnimating.current) return;
|
|
5219
5442
|
const speed = 3;
|
|
5220
|
-
camera.position.x =
|
|
5221
|
-
camera.position.y =
|
|
5222
|
-
camera.position.z =
|
|
5443
|
+
camera.position.x = THREE10.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
|
|
5444
|
+
camera.position.y = THREE10.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
|
|
5445
|
+
camera.position.z = THREE10.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
|
|
5223
5446
|
camera.lookAt(targetLookVec.current);
|
|
5224
5447
|
const dist = camera.position.distanceTo(targetPosVec.current);
|
|
5225
5448
|
if (dist < 0.05) {
|
|
@@ -5234,7 +5457,7 @@ function SceneFade({ animating, children }) {
|
|
|
5234
5457
|
useFrame((_, delta) => {
|
|
5235
5458
|
if (!groupRef.current) return;
|
|
5236
5459
|
const target = animating ? 0 : 1;
|
|
5237
|
-
opacityRef.current =
|
|
5460
|
+
opacityRef.current = THREE10.MathUtils.damp(opacityRef.current, target, 5, delta);
|
|
5238
5461
|
groupRef.current.visible = opacityRef.current > 0.05;
|
|
5239
5462
|
const s = 0.9 + opacityRef.current * 0.1;
|
|
5240
5463
|
groupRef.current.scale.setScalar(s);
|