@almadar/ui 5.142.0 → 5.143.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.
@@ -1,8 +1,8 @@
1
- import React3, { forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle, useCallback, createContext, useContext, Component, useReducer } from 'react';
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 THREE10 from 'three';
5
+ import * as THREE11 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 THREE10.DirectionalLight(directionalColor, directionalIntensity),
510
+ new THREE11.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 THREE10.Vector3(target[0], target[1], target[2]));
568
- const goal = useRef(new THREE10.Vector3());
567
+ const look = useRef(new THREE11.Vector3(target[0], target[1], target[2]));
568
+ const goal = useRef(new THREE11.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 lerp = (a, b, k) => a + (b - a) * k;
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 lerp(a, b, k);
730
+ if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
629
731
  return a;
630
732
  };
631
- for (const key of NUMERIC_TRACKS) {
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: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
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
  }
@@ -792,12 +894,12 @@ function ModelLoader({
792
894
  if (!loadedModel) return null;
793
895
  const cloned = clone(loadedModel);
794
896
  cloned.updateMatrixWorld(true);
795
- const tintColor = tint ? new THREE10.Color(tint) : null;
897
+ const tintColor = tint ? new THREE11.Color(tint) : null;
796
898
  cloned.traverse((child) => {
797
- if (child instanceof THREE10.Mesh) {
899
+ if (child instanceof THREE11.Mesh) {
798
900
  child.castShadow = castShadow;
799
901
  child.receiveShadow = receiveShadow;
800
- if (tintColor && child.material instanceof THREE10.MeshStandardMaterial) {
902
+ if (tintColor && child.material instanceof THREE11.MeshStandardMaterial) {
801
903
  const mat = child.material.clone();
802
904
  mat.color.multiply(tintColor);
803
905
  child.material = mat;
@@ -806,7 +908,7 @@ function ModelLoader({
806
908
  });
807
909
  return cloned;
808
910
  }, [loadedModel, castShadow, receiveShadow, tint]);
809
- const mixer = useMemo(() => model ? new THREE10.AnimationMixer(model) : null, [model]);
911
+ const mixer = useMemo(() => model ? new THREE11.AnimationMixer(model) : null, [model]);
810
912
  useEffect(() => {
811
913
  if (!mixer || !animation || clips.length === 0) return;
812
914
  const wanted = animation.toLowerCase();
@@ -823,8 +925,8 @@ function ModelLoader({
823
925
  });
824
926
  const normFactor = useMemo(() => {
825
927
  if (!model) return 1;
826
- const box = new THREE10.Box3().setFromObject(model);
827
- const size = new THREE10.Vector3();
928
+ const box = new THREE11.Box3().setFromObject(model);
929
+ const size = new THREE11.Vector3();
828
930
  box.getSize(size);
829
931
  const maxDim = Math.max(size.x, size.y, size.z);
830
932
  if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
@@ -914,7 +1016,7 @@ var warnUnsupported3d = (kind) => {
914
1016
  warnedUnsupported.add(kind);
915
1017
  mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
916
1018
  };
917
- var CrossOriginTextureLoader = class extends THREE10.TextureLoader {
1019
+ var CrossOriginTextureLoader = class extends THREE11.TextureLoader {
918
1020
  constructor() {
919
1021
  super();
920
1022
  this.crossOrigin = "anonymous";
@@ -932,7 +1034,7 @@ function useBillboardTexture(url) {
932
1034
  url,
933
1035
  (texture) => {
934
1036
  if (!active) return;
935
- texture.colorSpace = THREE10.SRGBColorSpace;
1037
+ texture.colorSpace = THREE11.SRGBColorSpace;
936
1038
  setState({ texture, error: false });
937
1039
  },
938
1040
  void 0,
@@ -985,7 +1087,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
985
1087
  }, [texture, frame, node.height, node.width, anchor, cellSize]);
986
1088
  const groundGeometry = React3.useMemo(() => {
987
1089
  if (anchor !== "top-left" || !texture || !atlasReady) return null;
988
- const g = new THREE10.PlaneGeometry(size.width, size.height);
1090
+ const g = new THREE11.PlaneGeometry(size.width, size.height);
989
1091
  g.rotateX(-Math.PI / 2);
990
1092
  return g;
991
1093
  }, [anchor, texture, atlasReady, size.width, size.height]);
@@ -993,15 +1095,15 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
993
1095
  if (anchor === "top-left") {
994
1096
  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
1097
  /* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
996
- /* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
1098
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE11.DoubleSide })
997
1099
  ] }) });
998
1100
  }
999
1101
  return /* @__PURE__ */ jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxs("mesh", { children: [
1000
1102
  /* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
1001
- /* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
1103
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE11.DoubleSide })
1002
1104
  ] }) });
1003
1105
  }
1004
- texture.magFilter = texture.minFilter = THREE10.NearestFilter;
1106
+ texture.magFilter = texture.minFilter = THREE11.NearestFilter;
1005
1107
  texture.needsUpdate = true;
1006
1108
  if (frame) {
1007
1109
  texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
@@ -1014,7 +1116,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
1014
1116
  map: texture,
1015
1117
  transparent: true,
1016
1118
  alphaTest: 0.1,
1017
- side: THREE10.DoubleSide,
1119
+ side: THREE11.DoubleSide,
1018
1120
  opacity: (node.opacity ?? 1) * groupOpacity
1019
1121
  }
1020
1122
  ) }) });
@@ -1027,7 +1129,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
1027
1129
  map: texture,
1028
1130
  transparent: true,
1029
1131
  alphaTest: 0.1,
1030
- side: THREE10.DoubleSide,
1132
+ side: THREE11.DoubleSide,
1031
1133
  opacity: node.opacity ?? 1
1032
1134
  }
1033
1135
  )
@@ -1096,7 +1198,7 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
1096
1198
  }
1097
1199
  case "poly": {
1098
1200
  if (!node.points || node.points.length === 0) return null;
1099
- const s = new THREE10.Shape();
1201
+ const s = new THREE11.Shape();
1100
1202
  node.points.forEach((p, i) => {
1101
1203
  if (i === 0) s.moveTo(p.x, p.y);
1102
1204
  else s.lineTo(p.x, p.y);
@@ -1114,7 +1216,7 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
1114
1216
  }
1115
1217
  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
1218
  geometry,
1117
- /* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE10.DoubleSide })
1219
+ /* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE11.DoubleSide })
1118
1220
  ] }) });
1119
1221
  }
1120
1222
  function Text3D({ node, projector, groupOpacity = 1 }) {
@@ -1135,64 +1237,46 @@ function Text3D({ node, projector, groupOpacity = 1 }) {
1135
1237
  }
1136
1238
  ) });
1137
1239
  }
1138
- var MESH_SEGMENTS_DEFAULT = 24;
1139
- function clampSegments(segments) {
1140
- const s = segments ?? MESH_SEGMENTS_DEFAULT;
1141
- return Math.min(64, Math.max(3, Math.round(s)));
1142
- }
1143
- function isAnimatedMesh(node) {
1144
- return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
1145
- }
1146
- var lerp2 = (a, b, k) => a + (b - a) * k;
1147
- var NUMERIC_TRACKS2 = [
1148
- "offsetX",
1149
- "offsetY",
1150
- "offsetZ",
1151
- "rotateX",
1152
- "rotateY",
1153
- "rotateZ",
1154
- "scale",
1155
- "opacity",
1156
- "emissiveIntensity"
1157
- ];
1158
- function applyMeshAnimation(node, timeMs) {
1159
- const anim = node.animation;
1160
- if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
1161
- const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
1162
- const cycle = timeMs / anim.durationMs;
1163
- const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
1164
- const trackValue = (key) => {
1165
- const defined = frames.filter((f) => f[key] !== void 0);
1166
- if (defined.length === 0) return void 0;
1167
- let prev;
1168
- let next;
1169
- for (const f of defined) {
1170
- if (f.at <= t) prev = f;
1171
- else if (!next) next = f;
1240
+ var GROUND_ROTATION = [-Math.PI / 2, 0, 0];
1241
+ function polyhedronGeometry(node, cellSize) {
1242
+ const verts = node.vertices;
1243
+ const faces = node.faces;
1244
+ const bounds = polyhedronBounds(verts);
1245
+ if (!verts || !bounds || !faces || faces.length === 0) return null;
1246
+ const positions = new Float32Array(verts.length * 3);
1247
+ for (let i = 0; i < verts.length; i++) {
1248
+ positions[i * 3] = verts[i][0] * cellSize;
1249
+ positions[i * 3 + 1] = verts[i][2] * cellSize;
1250
+ positions[i * 3 + 2] = verts[i][1] * cellSize;
1251
+ }
1252
+ const index = [];
1253
+ for (const face of faces) {
1254
+ if (face.length < 3) continue;
1255
+ const [a, b, c] = face;
1256
+ if (a === b || b === c || a === c) continue;
1257
+ if (![a, b, c].every((i) => Number.isInteger(i) && i >= 0 && i < verts.length)) continue;
1258
+ index.push(a, c, b);
1259
+ }
1260
+ if (index.length === 0) return null;
1261
+ const geometry = new THREE11.BufferGeometry();
1262
+ geometry.setAttribute("position", new THREE11.BufferAttribute(positions, 3));
1263
+ geometry.setIndex(index);
1264
+ geometry.computeVertexNormals();
1265
+ const skin = node.skin;
1266
+ if (skin && skin.indices.length === verts.length && skin.weights.length === verts.length) {
1267
+ const skinIndex = new Uint16Array(verts.length * 4);
1268
+ const skinWeight = new Float32Array(verts.length * 4);
1269
+ for (let i = 0; i < verts.length; i++) {
1270
+ for (let k = 0; k < 4; k++) {
1271
+ skinIndex[i * 4 + k] = skin.indices[i][k] ?? 0;
1272
+ skinWeight[i * 4 + k] = skin.weights[i][k] ?? 0;
1273
+ }
1172
1274
  }
1173
- if (!prev) return defined[0][key];
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;
1275
+ geometry.setAttribute("skinIndex", new THREE11.BufferAttribute(skinIndex, 4));
1276
+ geometry.setAttribute("skinWeight", new THREE11.BufferAttribute(skinWeight, 4));
1186
1277
  }
1187
- return {
1188
- offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
1189
- rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
1190
- scale: num.scale ?? 1,
1191
- opacity: num.opacity,
1192
- emissiveIntensity: num.emissiveIntensity,
1193
- color: trackValue("color"),
1194
- emissive: trackValue("emissive")
1195
- };
1278
+ const size = Math.max(bounds.max[0] - bounds.min[0], bounds.max[1] - bounds.min[1], bounds.max[2] - bounds.min[2]) * cellSize;
1279
+ return { geometry, lift: -bounds.min[2] * cellSize, size };
1196
1280
  }
1197
1281
  function meshGeometry(node, cellSize) {
1198
1282
  const seg = clampSegments(node.segments);
@@ -1220,17 +1304,20 @@ function meshGeometry(node, cellSize) {
1220
1304
  return { element: /* @__PURE__ */ jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
1221
1305
  }
1222
1306
  case "plane":
1223
- return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d) };
1307
+ return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d), baseRotation: GROUND_ROTATION };
1224
1308
  case "circle":
1225
- return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2 };
1309
+ return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2, baseRotation: GROUND_ROTATION };
1310
+ case "polyhedron":
1311
+ return polyhedronGeometry(node, cellSize);
1226
1312
  default:
1227
1313
  return null;
1228
1314
  }
1229
1315
  }
1316
+ var warnedPolyhedronOutline = false;
1230
1317
  var SIDE_MAP = {
1231
- front: THREE10.FrontSide,
1232
- back: THREE10.BackSide,
1233
- double: THREE10.DoubleSide
1318
+ front: THREE11.FrontSide,
1319
+ back: THREE11.BackSide,
1320
+ double: THREE11.DoubleSide
1234
1321
  };
1235
1322
  function meshMaterial(mat, opacity, ref) {
1236
1323
  const m = mat ?? {};
@@ -1274,6 +1361,53 @@ function meshMaterial(mat, opacity, ref) {
1274
1361
  );
1275
1362
  }
1276
1363
  }
1364
+ function SkinnedPolyhedron3D({
1365
+ node,
1366
+ skin,
1367
+ geometry,
1368
+ cellSize,
1369
+ opacity
1370
+ }) {
1371
+ const boneStore = useContext(BoneRegistryContext);
1372
+ const meshRef = useRef(null);
1373
+ const [registryTick, setRegistryTick] = useState(0);
1374
+ useEffect(() => boneStore?.subscribe(() => setRegistryTick((t) => t + 1)), [boneStore]);
1375
+ useEffect(() => {
1376
+ const mesh = meshRef.current;
1377
+ if (!mesh || !boneStore) return;
1378
+ const bones = [];
1379
+ for (const name of skin.bones) {
1380
+ const bone = boneStore.get(name);
1381
+ if (!bone) return;
1382
+ bones.push(bone);
1383
+ }
1384
+ if (mesh.skeleton && mesh.skeleton.bones.length === bones.length && mesh.skeleton.bones.every((b, i) => b === bones[i])) return;
1385
+ if (skin.inverseBindMatrices.length !== bones.length) return;
1386
+ const inverses = skin.inverseBindMatrices.map((m) => {
1387
+ const mat = new THREE11.Matrix4().fromArray(m);
1388
+ mat.elements[12] *= cellSize;
1389
+ mat.elements[13] *= cellSize;
1390
+ mat.elements[14] *= cellSize;
1391
+ return mat;
1392
+ });
1393
+ mesh.bind(new THREE11.Skeleton(bones, inverses), new THREE11.Matrix4());
1394
+ }, [registryTick, skin, boneStore, geometry, cellSize]);
1395
+ useEffect(() => {
1396
+ const mesh = meshRef.current;
1397
+ return () => mesh?.skeleton?.dispose();
1398
+ }, []);
1399
+ return /* @__PURE__ */ jsx(
1400
+ "skinnedMesh",
1401
+ {
1402
+ ref: meshRef,
1403
+ geometry,
1404
+ frustumCulled: false,
1405
+ castShadow: node.castShadow ?? true,
1406
+ receiveShadow: node.receiveShadow ?? true,
1407
+ children: meshMaterial(node.material, opacity)
1408
+ }
1409
+ );
1410
+ }
1277
1411
  function Mesh3D({
1278
1412
  node,
1279
1413
  projector,
@@ -1286,6 +1420,10 @@ function Mesh3D({
1286
1420
  const validPos = isValidScenePos(node.position);
1287
1421
  const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
1288
1422
  const geo = useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
1423
+ useEffect(() => {
1424
+ const g = geo?.geometry;
1425
+ return () => g?.dispose();
1426
+ }, [geo]);
1289
1427
  useFrame(({ clock }) => {
1290
1428
  if (!animated || !groupRef.current) return;
1291
1429
  const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
@@ -1298,8 +1436,13 @@ function Mesh3D({
1298
1436
  );
1299
1437
  groupRef.current.scale.setScalar(state.scale);
1300
1438
  if (meshRef.current) {
1439
+ const shapeRot = geo?.baseRotation ?? [0, 0, 0];
1301
1440
  const base = node.rotation ?? [0, 0, 0];
1302
- meshRef.current.rotation.set(base[0] + state.rotate[0], base[1] + state.rotate[1], base[2] + state.rotate[2]);
1441
+ meshRef.current.rotation.set(
1442
+ shapeRot[0] + base[0] + state.rotate[0],
1443
+ shapeRot[1] + base[1] + state.rotate[1],
1444
+ shapeRot[2] + base[2] + state.rotate[2]
1445
+ );
1303
1446
  }
1304
1447
  const mat = materialRef.current;
1305
1448
  if (mat) {
@@ -1316,13 +1459,28 @@ function Mesh3D({
1316
1459
  if (!validPos || !geo) return null;
1317
1460
  const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
1318
1461
  const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
1319
- const rotation = node.rotation ?? [0, 0, 0];
1462
+ if (node.skin && geo.geometry) {
1463
+ return /* @__PURE__ */ jsx("group", { position: baseWorld, children: /* @__PURE__ */ jsx(SkinnedPolyhedron3D, { node, skin: node.skin, geometry: geo.geometry, cellSize: projector.cellSize, opacity }) });
1464
+ }
1465
+ const nodeRotation = node.rotation ?? [0, 0, 0];
1466
+ const shapeRotation = geo.baseRotation ?? [0, 0, 0];
1467
+ const rotation = [
1468
+ shapeRotation[0] + nodeRotation[0],
1469
+ shapeRotation[1] + nodeRotation[1],
1470
+ shapeRotation[2] + nodeRotation[2]
1471
+ ];
1320
1472
  const outlineScale = geo.size > 0 ? 1 + (node.outline?.width ?? 0.05) * projector.cellSize / geo.size : 1;
1473
+ if (node.outline && geo.geometry && !warnedPolyhedronOutline) {
1474
+ warnedPolyhedronOutline = true;
1475
+ console.warn('[draw-mesh] outline is not yet supported on shape "polyhedron" \u2014 skipped');
1476
+ }
1477
+ const geometryProp = geo.geometry ? { geometry: geo.geometry } : {};
1321
1478
  return /* @__PURE__ */ jsxs("group", { ref: groupRef, position: baseWorld, children: [
1322
1479
  /* @__PURE__ */ jsxs(
1323
1480
  "mesh",
1324
1481
  {
1325
1482
  ref: meshRef,
1483
+ ...geometryProp,
1326
1484
  position: [0, lift, 0],
1327
1485
  rotation,
1328
1486
  castShadow: node.castShadow ?? true,
@@ -1333,13 +1491,13 @@ function Mesh3D({
1333
1491
  ]
1334
1492
  }
1335
1493
  ),
1336
- node.outline && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
1494
+ node.outline && !geo.geometry && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
1337
1495
  geo.element,
1338
1496
  /* @__PURE__ */ jsx(
1339
1497
  "meshBasicMaterial",
1340
1498
  {
1341
1499
  color: node.outline.color ?? "#101014",
1342
- side: THREE10.BackSide,
1500
+ side: THREE11.BackSide,
1343
1501
  transparent: opacity < 1,
1344
1502
  opacity
1345
1503
  }
@@ -1366,14 +1524,53 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1366
1524
  case "draw-group": {
1367
1525
  if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
1368
1526
  if (node.clip) warnUnsupported3d("draw-group:clip");
1369
- const world = projector.toWorld(node.position);
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)) });
1527
+ return /* @__PURE__ */ jsx(Group3D, { node, projector, groupOpacity });
1374
1528
  }
1375
1529
  }
1376
1530
  }
1531
+ function Group3D({
1532
+ node,
1533
+ projector,
1534
+ groupOpacity
1535
+ }) {
1536
+ const ref = useRef(null);
1537
+ const animated = isAnimatedGroup(node);
1538
+ const world = projector.toWorld(node.position);
1539
+ const inner = create3DProjector({ cellSize: projector.cellSize });
1540
+ const opacity = (node.opacity ?? 1) * groupOpacity;
1541
+ const s = node.scale ?? 1;
1542
+ const baseRotation = node.rotation ?? [0, -(node.rotate ?? 0), 0];
1543
+ const parentStore = useContext(BoneRegistryContext);
1544
+ const scopedStore = useMemo(() => node.skeleton ? new BoneStore() : null, [node.skeleton]);
1545
+ const boneStore = scopedStore ?? parentStore;
1546
+ const boneObject = useMemo(() => node.bone ? new THREE11.Bone() : null, [node.bone]);
1547
+ useEffect(() => {
1548
+ if (!node.bone || !boneStore || !boneObject) return;
1549
+ return boneStore.register(node.bone, boneObject);
1550
+ }, [node.bone, boneStore, boneObject]);
1551
+ useFrame(({ clock }) => {
1552
+ if (!animated || !ref.current) return;
1553
+ const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
1554
+ if (!state) return;
1555
+ const cell = projector.cellSize;
1556
+ ref.current.position.set(
1557
+ world[0] + state.offset[0] * cell,
1558
+ world[1] + state.offset[2] * cell,
1559
+ world[2] + state.offset[1] * cell
1560
+ );
1561
+ ref.current.rotation.set(
1562
+ baseRotation[0] + state.rotate[0],
1563
+ baseRotation[1] + state.rotate[1],
1564
+ baseRotation[2] + state.rotate[2]
1565
+ );
1566
+ ref.current.scale.setScalar(s * state.scale);
1567
+ });
1568
+ const children = /* @__PURE__ */ jsxs("group", { ref, position: world, rotation: baseRotation, scale: [s, s, s], children: [
1569
+ boneObject && /* @__PURE__ */ jsx("primitive", { object: boneObject }),
1570
+ node.items.map((item, i) => /* @__PURE__ */ jsx(Drawable3D, { node: item, projector: inner, groupOpacity: opacity }, i))
1571
+ ] });
1572
+ return scopedStore ? /* @__PURE__ */ jsx(BoneRegistryContext.Provider, { value: scopedStore, children }) : children;
1573
+ }
1377
1574
 
1378
1575
  // lib/drawable/three/game3dTheme.ts
1379
1576
  var GRID_COLORS_3D = {
@@ -1389,7 +1586,7 @@ var DEFAULT_GRID_CONFIG = {
1389
1586
  function RoomEnvironment3D() {
1390
1587
  const { gl, scene } = useThree(({ gl: gl2, scene: scene2 }) => ({ gl: gl2, scene: scene2 }));
1391
1588
  useEffect(() => {
1392
- const pmremGenerator = new THREE10.PMREMGenerator(gl);
1589
+ const pmremGenerator = new THREE11.PMREMGenerator(gl);
1393
1590
  const envTexture = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
1394
1591
  scene.environment = envTexture;
1395
1592
  return () => {
@@ -1505,6 +1702,7 @@ var Canvas3DHost = forwardRef(
1505
1702
  }),
1506
1703
  [gridBounds, cellSize]
1507
1704
  );
1705
+ const boneStore = useMemo(() => new BoneStore(), []);
1508
1706
  const drawableProjector = useMemo(
1509
1707
  () => create3DProjector({
1510
1708
  cellSize: gridConfig.cellSize,
@@ -1517,7 +1715,7 @@ var Canvas3DHost = forwardRef(
1517
1715
  getCameraPosition: () => {
1518
1716
  if (controlsRef.current) {
1519
1717
  const pos = controlsRef.current.object.position;
1520
- return new THREE10.Vector3(pos.x, pos.y, pos.z);
1718
+ return new THREE11.Vector3(pos.x, pos.y, pos.z);
1521
1719
  }
1522
1720
  return null;
1523
1721
  },
@@ -1562,6 +1760,8 @@ var Canvas3DHost = forwardRef(
1562
1760
  return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
1563
1761
  case "top-down":
1564
1762
  return { position: [cx, d * 2, cz + d * 0.35], fov: fovDeg };
1763
+ case "front":
1764
+ return { position: [cx, d * 0.32, cz + d * 1.15], fov: fovDeg };
1565
1765
  case "follow":
1566
1766
  return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
1567
1767
  case "perspective":
@@ -1628,6 +1828,7 @@ var Canvas3DHost = forwardRef(
1628
1828
  Canvas,
1629
1829
  {
1630
1830
  shadows,
1831
+ flat: lighting?.toneMapping === "none",
1631
1832
  camera: {
1632
1833
  position: cameraConfig.position,
1633
1834
  fov: cameraConfig.fov,
@@ -1686,7 +1887,7 @@ var Canvas3DHost = forwardRef(
1686
1887
  fadeStrength: 1
1687
1888
  }
1688
1889
  ),
1689
- allDrawables.length > 0 && /* @__PURE__ */ jsx("group", { children: allDrawables.map((node, i) => /* @__PURE__ */ jsx(Drawable3D, { node, projector: drawableProjector }, i)) }),
1890
+ 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
1891
  (tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxs(
1691
1892
  "mesh",
1692
1893
  {
@@ -1722,7 +1923,7 @@ var Canvas3DHost = forwardRef(
1722
1923
  dampingFactor: 0.05,
1723
1924
  enableZoom: true,
1724
1925
  enablePan: true,
1725
- touches: { ONE: THREE10.TOUCH.ROTATE, TWO: THREE10.TOUCH.DOLLY_PAN },
1926
+ touches: { ONE: THREE11.TOUCH.ROTATE, TWO: THREE11.TOUCH.DOLLY_PAN },
1726
1927
  minDistance: 2,
1727
1928
  maxDistance: 100,
1728
1929
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -1745,15 +1946,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
1745
1946
  if (initializedRef.current) return;
1746
1947
  initializedRef.current = true;
1747
1948
  if (background.startsWith("#") || background.startsWith("rgb")) {
1748
- scene.background = new THREE10.Color(background);
1949
+ scene.background = new THREE11.Color(background);
1749
1950
  } else {
1750
- const loader = new THREE10.TextureLoader();
1951
+ const loader = new THREE11.TextureLoader();
1751
1952
  loader.load(background, (texture) => {
1752
1953
  scene.background = texture;
1753
1954
  });
1754
1955
  }
1755
1956
  if (fog) {
1756
- scene.fog = new THREE10.Fog(fog.color, fog.near, fog.far);
1957
+ scene.fog = new THREE11.Fog(fog.color, fog.near, fog.far);
1757
1958
  }
1758
1959
  return () => {
1759
1960
  scene.background = null;
@@ -1776,14 +1977,14 @@ var Camera3D = forwardRef(
1776
1977
  }, ref) => {
1777
1978
  const { camera, set, viewport } = useThree();
1778
1979
  const controlsRef = useRef(null);
1779
- const initialPosition = useRef(new THREE10.Vector3(...position));
1780
- const initialTarget = useRef(new THREE10.Vector3(...target));
1980
+ const initialPosition = useRef(new THREE11.Vector3(...position));
1981
+ const initialTarget = useRef(new THREE11.Vector3(...target));
1781
1982
  useEffect(() => {
1782
1983
  let newCamera;
1783
1984
  if (mode === "isometric") {
1784
1985
  const aspect = viewport.aspect;
1785
1986
  const size = 10 / zoom;
1786
- newCamera = new THREE10.OrthographicCamera(
1987
+ newCamera = new THREE11.OrthographicCamera(
1787
1988
  -size * aspect,
1788
1989
  size * aspect,
1789
1990
  size,
@@ -1792,7 +1993,7 @@ var Camera3D = forwardRef(
1792
1993
  1e3
1793
1994
  );
1794
1995
  } else {
1795
- newCamera = new THREE10.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1996
+ newCamera = new THREE11.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1796
1997
  }
1797
1998
  newCamera.position.copy(initialPosition.current);
1798
1999
  newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
@@ -1833,8 +2034,8 @@ var Camera3D = forwardRef(
1833
2034
  }
1834
2035
  },
1835
2036
  getViewBounds: () => {
1836
- const min = new THREE10.Vector3(-10, -10, -10);
1837
- const max = new THREE10.Vector3(10, 10, 10);
2037
+ const min = new THREE11.Vector3(-10, -10, -10);
2038
+ const max = new THREE11.Vector3(10, 10, 10);
1838
2039
  return { min, max };
1839
2040
  }
1840
2041
  }));
@@ -1876,7 +2077,7 @@ var AssetLoader = class {
1876
2077
  __publicField(this, "textureCache");
1877
2078
  __publicField(this, "loadingPromises");
1878
2079
  this.objLoader = new OBJLoader();
1879
- this.textureLoader = new THREE10.TextureLoader();
2080
+ this.textureLoader = new THREE11.TextureLoader();
1880
2081
  this.modelCache = /* @__PURE__ */ new Map();
1881
2082
  this.textureCache = /* @__PURE__ */ new Map();
1882
2083
  this.loadingPromises = /* @__PURE__ */ new Map();
@@ -1950,7 +2151,7 @@ var AssetLoader = class {
1950
2151
  return this.loadingPromises.get(`texture:${url}`);
1951
2152
  }
1952
2153
  const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
1953
- texture.colorSpace = THREE10.SRGBColorSpace;
2154
+ texture.colorSpace = THREE11.SRGBColorSpace;
1954
2155
  this.textureCache.set(url, texture);
1955
2156
  this.loadingPromises.delete(`texture:${url}`);
1956
2157
  return texture;
@@ -2024,7 +2225,7 @@ var AssetLoader = class {
2024
2225
  });
2025
2226
  this.modelCache.forEach((model) => {
2026
2227
  model.scene.traverse((child) => {
2027
- if (child instanceof THREE10.Mesh) {
2228
+ if (child instanceof THREE11.Mesh) {
2028
2229
  child.geometry.dispose();
2029
2230
  if (Array.isArray(child.material)) {
2030
2231
  child.material.forEach((m) => m.dispose());
@@ -2074,21 +2275,21 @@ function useThree5(options = {}) {
2074
2275
  const [isReady, setIsReady] = useState(false);
2075
2276
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
2076
2277
  const initialCameraPosition = useMemo(
2077
- () => new THREE10.Vector3(...opts.cameraPosition),
2278
+ () => new THREE11.Vector3(...opts.cameraPosition),
2078
2279
  []
2079
2280
  );
2080
2281
  useEffect(() => {
2081
2282
  if (!containerRef.current) return;
2082
2283
  const container = containerRef.current;
2083
2284
  const { clientWidth, clientHeight } = container;
2084
- const scene = new THREE10.Scene();
2085
- scene.background = new THREE10.Color(opts.backgroundColor);
2285
+ const scene = new THREE11.Scene();
2286
+ scene.background = new THREE11.Color(opts.backgroundColor);
2086
2287
  sceneRef.current = scene;
2087
2288
  let camera;
2088
2289
  const aspect = clientWidth / clientHeight;
2089
2290
  if (opts.cameraMode === "isometric") {
2090
2291
  const size = 10;
2091
- camera = new THREE10.OrthographicCamera(
2292
+ camera = new THREE11.OrthographicCamera(
2092
2293
  -size * aspect,
2093
2294
  size * aspect,
2094
2295
  size,
@@ -2097,11 +2298,11 @@ function useThree5(options = {}) {
2097
2298
  1e3
2098
2299
  );
2099
2300
  } else {
2100
- camera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
2301
+ camera = new THREE11.PerspectiveCamera(45, aspect, 0.1, 1e3);
2101
2302
  }
2102
2303
  camera.position.copy(initialCameraPosition);
2103
2304
  cameraRef.current = camera;
2104
- const renderer = new THREE10.WebGLRenderer({
2305
+ const renderer = new THREE11.WebGLRenderer({
2105
2306
  antialias: true,
2106
2307
  alpha: true,
2107
2308
  canvas: canvasRef.current || void 0
@@ -2109,7 +2310,7 @@ function useThree5(options = {}) {
2109
2310
  renderer.setSize(clientWidth, clientHeight);
2110
2311
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
2111
2312
  renderer.shadowMap.enabled = opts.shadows;
2112
- renderer.shadowMap.type = THREE10.PCFSoftShadowMap;
2313
+ renderer.shadowMap.type = THREE11.PCFSoftShadowMap;
2113
2314
  rendererRef.current = renderer;
2114
2315
  const controls = new OrbitControls$1(camera, renderer.domElement);
2115
2316
  controls.enableDamping = true;
@@ -2118,16 +2319,16 @@ function useThree5(options = {}) {
2118
2319
  controls.maxDistance = 100;
2119
2320
  controls.maxPolarAngle = Math.PI / 2 - 0.1;
2120
2321
  controlsRef.current = controls;
2121
- const ambientLight = new THREE10.AmbientLight(16777215, 0.6);
2322
+ const ambientLight = new THREE11.AmbientLight(16777215, 0.6);
2122
2323
  scene.add(ambientLight);
2123
- const directionalLight = new THREE10.DirectionalLight(16777215, 0.8);
2324
+ const directionalLight = new THREE11.DirectionalLight(16777215, 0.8);
2124
2325
  directionalLight.position.set(10, 20, 10);
2125
2326
  directionalLight.castShadow = opts.shadows;
2126
2327
  directionalLight.shadow.mapSize.width = 2048;
2127
2328
  directionalLight.shadow.mapSize.height = 2048;
2128
2329
  scene.add(directionalLight);
2129
2330
  if (opts.showGrid) {
2130
- const gridHelper = new THREE10.GridHelper(
2331
+ const gridHelper = new THREE11.GridHelper(
2131
2332
  opts.gridSize,
2132
2333
  opts.gridSize,
2133
2334
  4473924,
@@ -2145,10 +2346,10 @@ function useThree5(options = {}) {
2145
2346
  const handleResize = () => {
2146
2347
  const { clientWidth: width, clientHeight: height } = container;
2147
2348
  setDimensions({ width, height });
2148
- if (camera instanceof THREE10.PerspectiveCamera) {
2349
+ if (camera instanceof THREE11.PerspectiveCamera) {
2149
2350
  camera.aspect = width / height;
2150
2351
  camera.updateProjectionMatrix();
2151
- } else if (camera instanceof THREE10.OrthographicCamera) {
2352
+ } else if (camera instanceof THREE11.OrthographicCamera) {
2152
2353
  const aspect2 = width / height;
2153
2354
  const size = 10;
2154
2355
  camera.left = -size * aspect2;
@@ -2179,7 +2380,7 @@ function useThree5(options = {}) {
2179
2380
  let newCamera;
2180
2381
  if (opts.cameraMode === "isometric") {
2181
2382
  const size = 10;
2182
- newCamera = new THREE10.OrthographicCamera(
2383
+ newCamera = new THREE11.OrthographicCamera(
2183
2384
  -size * aspect,
2184
2385
  size * aspect,
2185
2386
  size,
@@ -2188,7 +2389,7 @@ function useThree5(options = {}) {
2188
2389
  1e3
2189
2390
  );
2190
2391
  } else {
2191
- newCamera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
2392
+ newCamera = new THREE11.PerspectiveCamera(45, aspect, 0.1, 1e3);
2192
2393
  }
2193
2394
  newCamera.position.copy(currentPos);
2194
2395
  cameraRef.current = newCamera;
@@ -2518,8 +2719,8 @@ function useSceneGraph() {
2518
2719
  }
2519
2720
  function useRaycaster(options) {
2520
2721
  const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
2521
- const raycaster = useRef(new THREE10.Raycaster());
2522
- const mouse = useRef(new THREE10.Vector2());
2722
+ const raycaster = useRef(new THREE11.Raycaster());
2723
+ const mouse = useRef(new THREE11.Vector2());
2523
2724
  const clientToNDC = useCallback(
2524
2725
  (clientX, clientY) => {
2525
2726
  if (!canvas) {
@@ -2589,8 +2790,8 @@ function useRaycaster(options) {
2589
2790
  const ndc = clientToNDC(clientX, clientY);
2590
2791
  mouse.current.set(ndc.x, ndc.y);
2591
2792
  raycaster.current.setFromCamera(mouse.current, camera);
2592
- const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), 0);
2593
- const target = new THREE10.Vector3();
2793
+ const plane = new THREE11.Plane(new THREE11.Vector3(0, 1, 0), 0);
2794
+ const target = new THREE11.Vector3();
2594
2795
  const intersection = raycaster.current.ray.intersectPlane(plane, target);
2595
2796
  if (intersection) {
2596
2797
  const gridX = Math.round((target.x - offsetX) / cellSize);
@@ -2628,7 +2829,7 @@ function useRaycaster(options) {
2628
2829
  return {
2629
2830
  gridX: gridCoords.x,
2630
2831
  gridZ: gridCoords.z,
2631
- worldPosition: new THREE10.Vector3(
2832
+ worldPosition: new THREE11.Vector3(
2632
2833
  gridCoords.x * cellSize + offsetX,
2633
2834
  0,
2634
2835
  gridCoords.z * cellSize + offsetZ
@@ -2658,7 +2859,7 @@ var DEFAULT_CONFIG = {
2658
2859
  };
2659
2860
  function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
2660
2861
  const opts = { ...DEFAULT_CONFIG, ...config };
2661
- return new THREE10.Vector3(
2862
+ return new THREE11.Vector3(
2662
2863
  gridX * opts.cellSize + opts.offsetX,
2663
2864
  opts.elevation,
2664
2865
  gridZ * opts.cellSize + opts.offsetZ
@@ -2672,17 +2873,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
2672
2873
  };
2673
2874
  }
2674
2875
  function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
2675
- const raycaster = new THREE10.Raycaster();
2676
- const mouse = new THREE10.Vector2(mouseX, mouseY);
2876
+ const raycaster = new THREE11.Raycaster();
2877
+ const mouse = new THREE11.Vector2(mouseX, mouseY);
2677
2878
  raycaster.setFromCamera(mouse, camera);
2678
- const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), -planeY);
2679
- const target = new THREE10.Vector3();
2879
+ const plane = new THREE11.Plane(new THREE11.Vector3(0, 1, 0), -planeY);
2880
+ const target = new THREE11.Vector3();
2680
2881
  const intersection = raycaster.ray.intersectPlane(plane, target);
2681
2882
  return intersection ? target : null;
2682
2883
  }
2683
2884
  function raycastToObjects(camera, mouseX, mouseY, objects) {
2684
- const raycaster = new THREE10.Raycaster();
2685
- const mouse = new THREE10.Vector2(mouseX, mouseY);
2885
+ const raycaster = new THREE11.Raycaster();
2886
+ const mouse = new THREE11.Vector2(mouseX, mouseY);
2686
2887
  raycaster.setFromCamera(mouse, camera);
2687
2888
  const intersects = raycaster.intersectObjects(objects, true);
2688
2889
  return intersects.length > 0 ? intersects[0] : null;
@@ -2734,14 +2935,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
2734
2935
  return cells;
2735
2936
  }
2736
2937
  function createGridHighlight(color = 16776960, opacity = 0.3) {
2737
- const geometry = new THREE10.PlaneGeometry(0.95, 0.95);
2738
- const material = new THREE10.MeshBasicMaterial({
2938
+ const geometry = new THREE11.PlaneGeometry(0.95, 0.95);
2939
+ const material = new THREE11.MeshBasicMaterial({
2739
2940
  color,
2740
2941
  transparent: true,
2741
2942
  opacity,
2742
- side: THREE10.DoubleSide
2943
+ side: THREE11.DoubleSide
2743
2944
  });
2744
- const mesh = new THREE10.Mesh(geometry, material);
2945
+ const mesh = new THREE11.Mesh(geometry, material);
2745
2946
  mesh.rotation.x = -Math.PI / 2;
2746
2947
  mesh.position.y = 0.01;
2747
2948
  return mesh;
@@ -2754,31 +2955,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
2754
2955
  };
2755
2956
  }
2756
2957
  function isInFrustum(position, camera, padding = 0) {
2757
- const frustum = new THREE10.Frustum();
2758
- const projScreenMatrix = new THREE10.Matrix4();
2958
+ const frustum = new THREE11.Frustum();
2959
+ const projScreenMatrix = new THREE11.Matrix4();
2759
2960
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2760
2961
  frustum.setFromProjectionMatrix(projScreenMatrix);
2761
- const sphere = new THREE10.Sphere(position, padding);
2962
+ const sphere = new THREE11.Sphere(position, padding);
2762
2963
  return frustum.intersectsSphere(sphere);
2763
2964
  }
2764
2965
  function filterByFrustum(positions, camera, padding = 0) {
2765
- const frustum = new THREE10.Frustum();
2766
- const projScreenMatrix = new THREE10.Matrix4();
2966
+ const frustum = new THREE11.Frustum();
2967
+ const projScreenMatrix = new THREE11.Matrix4();
2767
2968
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2768
2969
  frustum.setFromProjectionMatrix(projScreenMatrix);
2769
2970
  return positions.filter((position) => {
2770
- const sphere = new THREE10.Sphere(position, padding);
2971
+ const sphere = new THREE11.Sphere(position, padding);
2771
2972
  return frustum.intersectsSphere(sphere);
2772
2973
  });
2773
2974
  }
2774
2975
  function getVisibleIndices(positions, camera, padding = 0) {
2775
- const frustum = new THREE10.Frustum();
2776
- const projScreenMatrix = new THREE10.Matrix4();
2976
+ const frustum = new THREE11.Frustum();
2977
+ const projScreenMatrix = new THREE11.Matrix4();
2777
2978
  const visible = /* @__PURE__ */ new Set();
2778
2979
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2779
2980
  frustum.setFromProjectionMatrix(projScreenMatrix);
2780
2981
  positions.forEach((position, index) => {
2781
- const sphere = new THREE10.Sphere(position, padding);
2982
+ const sphere = new THREE11.Sphere(position, padding);
2782
2983
  if (frustum.intersectsSphere(sphere)) {
2783
2984
  visible.add(index);
2784
2985
  }
@@ -2802,7 +3003,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
2802
3003
  return lodIndices;
2803
3004
  }
2804
3005
  function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
2805
- const dummy = new THREE10.Object3D();
3006
+ const dummy = new THREE11.Object3D();
2806
3007
  let visibleCount = 0;
2807
3008
  positions.forEach((position, index) => {
2808
3009
  if (visibleIndices.has(index)) {
@@ -5585,12 +5786,12 @@ function useAvl3DConfig() {
5585
5786
  }
5586
5787
  function CameraController({ targetPosition, targetLookAt, animated }) {
5587
5788
  const { camera } = useThree();
5588
- const targetPosVec = useRef(new THREE10.Vector3(...targetPosition));
5589
- const targetLookVec = useRef(new THREE10.Vector3(...targetLookAt));
5789
+ const targetPosVec = useRef(new THREE11.Vector3(...targetPosition));
5790
+ const targetLookVec = useRef(new THREE11.Vector3(...targetLookAt));
5590
5791
  const isAnimating = useRef(false);
5591
5792
  useEffect(() => {
5592
- const newTarget = new THREE10.Vector3(...targetPosition);
5593
- const newLookAt = new THREE10.Vector3(...targetLookAt);
5793
+ const newTarget = new THREE11.Vector3(...targetPosition);
5794
+ const newLookAt = new THREE11.Vector3(...targetLookAt);
5594
5795
  if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
5595
5796
  targetPosVec.current.copy(newTarget);
5596
5797
  targetLookVec.current.copy(newLookAt);
@@ -5605,9 +5806,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
5605
5806
  useFrame((_, delta) => {
5606
5807
  if (!isAnimating.current) return;
5607
5808
  const speed = 3;
5608
- camera.position.x = THREE10.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
5609
- camera.position.y = THREE10.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
5610
- camera.position.z = THREE10.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
5809
+ camera.position.x = THREE11.MathUtils.damp(camera.position.x, targetPosVec.current.x, speed, delta);
5810
+ camera.position.y = THREE11.MathUtils.damp(camera.position.y, targetPosVec.current.y, speed, delta);
5811
+ camera.position.z = THREE11.MathUtils.damp(camera.position.z, targetPosVec.current.z, speed, delta);
5611
5812
  camera.lookAt(targetLookVec.current);
5612
5813
  const dist = camera.position.distanceTo(targetPosVec.current);
5613
5814
  if (dist < 0.05) {
@@ -5622,7 +5823,7 @@ function SceneFade({ animating, children }) {
5622
5823
  useFrame((_, delta) => {
5623
5824
  if (!groupRef.current) return;
5624
5825
  const target = animating ? 0 : 1;
5625
- opacityRef.current = THREE10.MathUtils.damp(opacityRef.current, target, 5, delta);
5826
+ opacityRef.current = THREE11.MathUtils.damp(opacityRef.current, target, 5, delta);
5626
5827
  groupRef.current.visible = opacityRef.current > 0.05;
5627
5828
  const s = 0.9 + opacityRef.current * 0.1;
5628
5829
  groupRef.current.scale.setScalar(s);