@almadar/ui 5.141.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,11 +1,13 @@
1
- import React3, { forwardRef, useRef, useState, useEffect, useMemo, 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
+ import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
7
8
  import { Grid, OrbitControls, Billboard, Text, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
8
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+ import { Bloom, Vignette, EffectComposer, DepthOfField } from '@react-three/postprocessing';
9
11
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
10
12
  import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
11
13
  import { clsx } from 'clsx';
@@ -13,7 +15,6 @@ import { twMerge } from 'tailwind-merge';
13
15
  import { OrbitControls as OrbitControls$1 } from 'three/examples/jsm/controls/OrbitControls.js';
14
16
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
15
17
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
16
- import { EffectComposer, Bloom, DepthOfField, Vignette } from '@react-three/postprocessing';
17
18
  import { useTranslate } from '@almadar/ui/hooks';
18
19
 
19
20
  var __defProp = Object.defineProperty;
@@ -450,6 +451,10 @@ function Lighting3D({
450
451
  directionalIntensity = 0.8,
451
452
  directionalColor = "#ffffff",
452
453
  directionalPosition = [10, 20, 10],
454
+ hemisphereIntensity = 0.3,
455
+ hemisphereColor = "#87ceeb",
456
+ hemisphereGroundColor = "#362d1d",
457
+ points,
453
458
  shadows = true,
454
459
  shadowMapSize = 2048,
455
460
  shadowCameraSize = 20,
@@ -482,22 +487,63 @@ function Lighting3D({
482
487
  /* @__PURE__ */ jsx(
483
488
  "hemisphereLight",
484
489
  {
485
- intensity: 0.3,
486
- color: "#87ceeb",
487
- groundColor: "#362d1d"
490
+ intensity: hemisphereIntensity,
491
+ color: hemisphereColor,
492
+ groundColor: hemisphereGroundColor
488
493
  }
489
494
  ),
495
+ points?.map((p, i) => /* @__PURE__ */ jsx(
496
+ "pointLight",
497
+ {
498
+ position: p.position,
499
+ intensity: p.intensity,
500
+ color: p.color,
501
+ distance: p.distance,
502
+ decay: p.decay
503
+ },
504
+ i
505
+ )),
490
506
  showHelpers && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
491
507
  "directionalLightHelper",
492
508
  {
493
509
  args: [
494
- new THREE10.DirectionalLight(directionalColor, directionalIntensity),
510
+ new THREE11.DirectionalLight(directionalColor, directionalIntensity),
495
511
  5
496
512
  ]
497
513
  }
498
514
  ) })
499
515
  ] });
500
516
  }
517
+ function Effects3D({ post }) {
518
+ if (!post || !post.bloom && !post.vignette) return null;
519
+ const passes = [];
520
+ if (post.bloom) {
521
+ passes.push(
522
+ /* @__PURE__ */ jsx(
523
+ Bloom,
524
+ {
525
+ intensity: post.bloom.intensity ?? 1,
526
+ luminanceThreshold: post.bloom.threshold ?? 0.9,
527
+ luminanceSmoothing: post.bloom.smoothing ?? 0.3
528
+ },
529
+ "bloom"
530
+ )
531
+ );
532
+ }
533
+ if (post.vignette) {
534
+ passes.push(
535
+ /* @__PURE__ */ jsx(
536
+ Vignette,
537
+ {
538
+ offset: post.vignette.offset ?? 0.3,
539
+ darkness: post.vignette.darkness ?? 0.6
540
+ },
541
+ "vignette"
542
+ )
543
+ );
544
+ }
545
+ return /* @__PURE__ */ jsx(EffectComposer, { children: passes });
546
+ }
501
547
  function CameraController3D({
502
548
  onCameraChange
503
549
  }) {
@@ -518,8 +564,8 @@ function FollowCamera3D({
518
564
  offset
519
565
  }) {
520
566
  const { camera } = useThree();
521
- const look = useRef(new THREE10.Vector3(target[0], target[1], target[2]));
522
- 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());
523
569
  useFrame((_, delta) => {
524
570
  const t = Math.min(1, delta * 5);
525
571
  goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
@@ -540,6 +586,179 @@ function create3DProjector(opts = {}) {
540
586
  toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
541
587
  };
542
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;
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 = [
692
+ "offsetX",
693
+ "offsetY",
694
+ "rotate",
695
+ "opacity",
696
+ "radiusX",
697
+ "radiusY",
698
+ "width",
699
+ "height",
700
+ "strokeWidth",
701
+ "strokeDashOffset",
702
+ "blur"
703
+ ];
704
+ function isAnimatedShape(node) {
705
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
706
+ }
707
+ var lerp2 = (a, b, k) => a + (b - a) * k;
708
+ function applyShapeAnimation(node, timeMs) {
709
+ const anim = node.animation;
710
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
711
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
712
+ const cycle = timeMs / anim.durationMs;
713
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
714
+ const out = { ...node };
715
+ const trackValue = (key) => {
716
+ const defined = frames.filter((f) => f[key] !== void 0);
717
+ if (defined.length === 0) return void 0;
718
+ let prev;
719
+ let next;
720
+ for (const f of defined) {
721
+ if (f.at <= t) prev = f;
722
+ else if (!next) next = f;
723
+ }
724
+ if (!prev) return defined[0][key];
725
+ if (!next) return prev[key];
726
+ const span = next.at - prev.at;
727
+ const k = span > 0 ? (t - prev.at) / span : 1;
728
+ const a = prev[key];
729
+ const b = next[key];
730
+ if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
731
+ return a;
732
+ };
733
+ for (const key of NUMERIC_TRACKS2) {
734
+ const v = trackValue(key);
735
+ if (v !== void 0) out[key] = v;
736
+ }
737
+ const fill = trackValue("fill");
738
+ if (fill !== void 0) out.fill = fill;
739
+ const stroke = trackValue("stroke");
740
+ if (stroke !== void 0) out.stroke = stroke;
741
+ const shadowFrames = frames.filter((f) => f.shadow !== void 0);
742
+ if (shadowFrames.length > 0) {
743
+ const sh = trackValue("shadow");
744
+ if (sh !== void 0) {
745
+ let prevSh;
746
+ let nextSh;
747
+ for (const f of shadowFrames) {
748
+ if (f.at <= t) prevSh = f;
749
+ else if (!nextSh) nextSh = f;
750
+ }
751
+ if (prevSh?.shadow && nextSh?.shadow) {
752
+ const span = nextSh.at - prevSh.at;
753
+ const k = span > 0 ? (t - prevSh.at) / span : 1;
754
+ out.shadow = { color: prevSh.shadow.color, blur: lerp2(prevSh.shadow.blur, nextSh.shadow.blur, k) };
755
+ } else {
756
+ out.shadow = sh;
757
+ }
758
+ }
759
+ }
760
+ return out;
761
+ }
543
762
 
544
763
  // lib/atlasSlice.ts
545
764
  var atlasCache = /* @__PURE__ */ new Map();
@@ -675,12 +894,12 @@ function ModelLoader({
675
894
  if (!loadedModel) return null;
676
895
  const cloned = clone(loadedModel);
677
896
  cloned.updateMatrixWorld(true);
678
- const tintColor = tint ? new THREE10.Color(tint) : null;
897
+ const tintColor = tint ? new THREE11.Color(tint) : null;
679
898
  cloned.traverse((child) => {
680
- if (child instanceof THREE10.Mesh) {
899
+ if (child instanceof THREE11.Mesh) {
681
900
  child.castShadow = castShadow;
682
901
  child.receiveShadow = receiveShadow;
683
- if (tintColor && child.material instanceof THREE10.MeshStandardMaterial) {
902
+ if (tintColor && child.material instanceof THREE11.MeshStandardMaterial) {
684
903
  const mat = child.material.clone();
685
904
  mat.color.multiply(tintColor);
686
905
  child.material = mat;
@@ -689,7 +908,7 @@ function ModelLoader({
689
908
  });
690
909
  return cloned;
691
910
  }, [loadedModel, castShadow, receiveShadow, tint]);
692
- const mixer = useMemo(() => model ? new THREE10.AnimationMixer(model) : null, [model]);
911
+ const mixer = useMemo(() => model ? new THREE11.AnimationMixer(model) : null, [model]);
693
912
  useEffect(() => {
694
913
  if (!mixer || !animation || clips.length === 0) return;
695
914
  const wanted = animation.toLowerCase();
@@ -706,8 +925,8 @@ function ModelLoader({
706
925
  });
707
926
  const normFactor = useMemo(() => {
708
927
  if (!model) return 1;
709
- const box = new THREE10.Box3().setFromObject(model);
710
- const size = new THREE10.Vector3();
928
+ const box = new THREE11.Box3().setFromObject(model);
929
+ const size = new THREE11.Vector3();
711
930
  box.getSize(size);
712
931
  const maxDim = Math.max(size.x, size.y, size.z);
713
932
  if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
@@ -797,7 +1016,7 @@ var warnUnsupported3d = (kind) => {
797
1016
  warnedUnsupported.add(kind);
798
1017
  mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
799
1018
  };
800
- var CrossOriginTextureLoader = class extends THREE10.TextureLoader {
1019
+ var CrossOriginTextureLoader = class extends THREE11.TextureLoader {
801
1020
  constructor() {
802
1021
  super();
803
1022
  this.crossOrigin = "anonymous";
@@ -815,7 +1034,7 @@ function useBillboardTexture(url) {
815
1034
  url,
816
1035
  (texture) => {
817
1036
  if (!active) return;
818
- texture.colorSpace = THREE10.SRGBColorSpace;
1037
+ texture.colorSpace = THREE11.SRGBColorSpace;
819
1038
  setState({ texture, error: false });
820
1039
  },
821
1040
  void 0,
@@ -868,7 +1087,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
868
1087
  }, [texture, frame, node.height, node.width, anchor, cellSize]);
869
1088
  const groundGeometry = React3.useMemo(() => {
870
1089
  if (anchor !== "top-left" || !texture || !atlasReady) return null;
871
- const g = new THREE10.PlaneGeometry(size.width, size.height);
1090
+ const g = new THREE11.PlaneGeometry(size.width, size.height);
872
1091
  g.rotateX(-Math.PI / 2);
873
1092
  return g;
874
1093
  }, [anchor, texture, atlasReady, size.width, size.height]);
@@ -876,15 +1095,15 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
876
1095
  if (anchor === "top-left") {
877
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: [
878
1097
  /* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
879
- /* @__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 })
880
1099
  ] }) });
881
1100
  }
882
1101
  return /* @__PURE__ */ jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxs("mesh", { children: [
883
1102
  /* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
884
- /* @__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 })
885
1104
  ] }) });
886
1105
  }
887
- texture.magFilter = texture.minFilter = THREE10.NearestFilter;
1106
+ texture.magFilter = texture.minFilter = THREE11.NearestFilter;
888
1107
  texture.needsUpdate = true;
889
1108
  if (frame) {
890
1109
  texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
@@ -897,7 +1116,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
897
1116
  map: texture,
898
1117
  transparent: true,
899
1118
  alphaTest: 0.1,
900
- side: THREE10.DoubleSide,
1119
+ side: THREE11.DoubleSide,
901
1120
  opacity: (node.opacity ?? 1) * groupOpacity
902
1121
  }
903
1122
  ) }) });
@@ -910,7 +1129,7 @@ function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
910
1129
  map: texture,
911
1130
  transparent: true,
912
1131
  alphaTest: 0.1,
913
- side: THREE10.DoubleSide,
1132
+ side: THREE11.DoubleSide,
914
1133
  opacity: node.opacity ?? 1
915
1134
  }
916
1135
  )
@@ -941,8 +1160,27 @@ function Sprite3D({ node, projector, groupOpacity = 1 }) {
941
1160
  return /* @__PURE__ */ jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize, groupOpacity });
942
1161
  }
943
1162
  function Shape3D({ node, projector, groupOpacity = 1 }) {
944
- if (!isValidScenePos(node.position)) return null;
945
- const world = projector.toWorld(node.position);
1163
+ const groupRef = React3.useRef(null);
1164
+ const materialRef = React3.useRef(null);
1165
+ const animated = isAnimatedShape(node);
1166
+ const validPos = isValidScenePos(node.position);
1167
+ const world = validPos ? projector.toWorld(node.position) : [0, 0, 0];
1168
+ useFrame(({ clock }) => {
1169
+ if (!animated || !groupRef.current) return;
1170
+ const view = applyShapeAnimation(node, clock.elapsedTime * 1e3);
1171
+ groupRef.current.position.set(
1172
+ world[0] + (view.offsetX ?? 0) * projector.cellSize,
1173
+ world[1] + 0.02,
1174
+ world[2] + (view.offsetY ?? 0) * projector.cellSize
1175
+ );
1176
+ groupRef.current.rotation.y = -(view.rotate ?? 0);
1177
+ const mat = materialRef.current;
1178
+ if (mat) {
1179
+ mat.opacity = (view.opacity ?? node.opacity ?? 1) * groupOpacity;
1180
+ if (view.fill) mat.color.set(view.fill);
1181
+ }
1182
+ });
1183
+ if (!validPos) return null;
946
1184
  const color = node.fill ?? node.stroke ?? "#ffffff";
947
1185
  let geometry;
948
1186
  switch (node.shape) {
@@ -960,7 +1198,7 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
960
1198
  }
961
1199
  case "poly": {
962
1200
  if (!node.points || node.points.length === 0) return null;
963
- const s = new THREE10.Shape();
1201
+ const s = new THREE11.Shape();
964
1202
  node.points.forEach((p, i) => {
965
1203
  if (i === 0) s.moveTo(p.x, p.y);
966
1204
  else s.lineTo(p.x, p.y);
@@ -976,12 +1214,12 @@ function Shape3D({ node, projector, groupOpacity = 1 }) {
976
1214
  default:
977
1215
  return null;
978
1216
  }
979
- return /* @__PURE__ */ jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [world[0], world[1] + 0.02, world[2]], children: [
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: [
980
1218
  geometry,
981
- /* @__PURE__ */ jsx("meshBasicMaterial", { color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE10.DoubleSide })
982
- ] });
1219
+ /* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE11.DoubleSide })
1220
+ ] }) });
983
1221
  }
984
- function Text3D({ node, projector }) {
1222
+ function Text3D({ node, projector, groupOpacity = 1 }) {
985
1223
  if (!isValidScenePos(node.position)) return null;
986
1224
  const world = projector.toWorld(node.position);
987
1225
  return /* @__PURE__ */ jsx(Billboard, { position: [world[0], world[1] + 1.2, world[2]], children: /* @__PURE__ */ jsx(
@@ -993,68 +1231,52 @@ function Text3D({ node, projector }) {
993
1231
  anchorY: "middle",
994
1232
  outlineWidth: 0.02,
995
1233
  outlineColor: "#000000",
1234
+ fillOpacity: groupOpacity,
1235
+ outlineOpacity: groupOpacity,
996
1236
  children: node.text
997
1237
  }
998
1238
  ) });
999
1239
  }
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;
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
+ }
1034
1274
  }
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;
1275
+ geometry.setAttribute("skinIndex", new THREE11.BufferAttribute(skinIndex, 4));
1276
+ geometry.setAttribute("skinWeight", new THREE11.BufferAttribute(skinWeight, 4));
1048
1277
  }
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
- };
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 };
1058
1280
  }
1059
1281
  function meshGeometry(node, cellSize) {
1060
1282
  const seg = clampSegments(node.segments);
@@ -1082,17 +1304,20 @@ function meshGeometry(node, cellSize) {
1082
1304
  return { element: /* @__PURE__ */ jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
1083
1305
  }
1084
1306
  case "plane":
1085
- 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 };
1086
1308
  case "circle":
1087
- 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);
1088
1312
  default:
1089
1313
  return null;
1090
1314
  }
1091
1315
  }
1316
+ var warnedPolyhedronOutline = false;
1092
1317
  var SIDE_MAP = {
1093
- front: THREE10.FrontSide,
1094
- back: THREE10.BackSide,
1095
- double: THREE10.DoubleSide
1318
+ front: THREE11.FrontSide,
1319
+ back: THREE11.BackSide,
1320
+ double: THREE11.DoubleSide
1096
1321
  };
1097
1322
  function meshMaterial(mat, opacity, ref) {
1098
1323
  const m = mat ?? {};
@@ -1136,6 +1361,53 @@ function meshMaterial(mat, opacity, ref) {
1136
1361
  );
1137
1362
  }
1138
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
+ }
1139
1411
  function Mesh3D({
1140
1412
  node,
1141
1413
  projector,
@@ -1148,6 +1420,10 @@ function Mesh3D({
1148
1420
  const validPos = isValidScenePos(node.position);
1149
1421
  const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
1150
1422
  const geo = useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
1423
+ useEffect(() => {
1424
+ const g = geo?.geometry;
1425
+ return () => g?.dispose();
1426
+ }, [geo]);
1151
1427
  useFrame(({ clock }) => {
1152
1428
  if (!animated || !groupRef.current) return;
1153
1429
  const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
@@ -1160,8 +1436,13 @@ function Mesh3D({
1160
1436
  );
1161
1437
  groupRef.current.scale.setScalar(state.scale);
1162
1438
  if (meshRef.current) {
1439
+ const shapeRot = geo?.baseRotation ?? [0, 0, 0];
1163
1440
  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]);
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
+ );
1165
1446
  }
1166
1447
  const mat = materialRef.current;
1167
1448
  if (mat) {
@@ -1178,13 +1459,28 @@ function Mesh3D({
1178
1459
  if (!validPos || !geo) return null;
1179
1460
  const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
1180
1461
  const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
1181
- 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
+ ];
1182
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 } : {};
1183
1478
  return /* @__PURE__ */ jsxs("group", { ref: groupRef, position: baseWorld, children: [
1184
1479
  /* @__PURE__ */ jsxs(
1185
1480
  "mesh",
1186
1481
  {
1187
1482
  ref: meshRef,
1483
+ ...geometryProp,
1188
1484
  position: [0, lift, 0],
1189
1485
  rotation,
1190
1486
  castShadow: node.castShadow ?? true,
@@ -1195,13 +1491,13 @@ function Mesh3D({
1195
1491
  ]
1196
1492
  }
1197
1493
  ),
1198
- 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: [
1199
1495
  geo.element,
1200
1496
  /* @__PURE__ */ jsx(
1201
1497
  "meshBasicMaterial",
1202
1498
  {
1203
1499
  color: node.outline.color ?? "#101014",
1204
- side: THREE10.BackSide,
1500
+ side: THREE11.BackSide,
1205
1501
  transparent: opacity < 1,
1206
1502
  opacity
1207
1503
  }
@@ -1216,7 +1512,7 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1216
1512
  case "draw-shape":
1217
1513
  return /* @__PURE__ */ jsx(Shape3D, { node, projector, groupOpacity });
1218
1514
  case "draw-text":
1219
- return /* @__PURE__ */ jsx(Text3D, { node, projector });
1515
+ return /* @__PURE__ */ jsx(Text3D, { node, projector, groupOpacity });
1220
1516
  case "draw-mesh":
1221
1517
  return /* @__PURE__ */ jsx(Mesh3D, { node, projector, groupOpacity });
1222
1518
  case "draw-sprite-layer":
@@ -1224,18 +1520,57 @@ function Drawable3D({ node, projector, groupOpacity = 1 }) {
1224
1520
  case "draw-shape-layer":
1225
1521
  return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Shape3D, { node: item, projector, groupOpacity }, i)) });
1226
1522
  case "draw-text-layer":
1227
- return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Text3D, { node: item, projector }, i)) });
1523
+ return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Text3D, { node: item, projector, groupOpacity }, i)) });
1228
1524
  case "draw-group": {
1229
1525
  if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
1230
1526
  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)) });
1527
+ return /* @__PURE__ */ jsx(Group3D, { node, projector, groupOpacity });
1236
1528
  }
1237
1529
  }
1238
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
+ }
1239
1574
 
1240
1575
  // lib/drawable/three/game3dTheme.ts
1241
1576
  var GRID_COLORS_3D = {
@@ -1248,6 +1583,20 @@ function cn(...inputs) {
1248
1583
  }
1249
1584
  var DEFAULT_GRID_CONFIG = {
1250
1585
  cellSize: 1};
1586
+ function RoomEnvironment3D() {
1587
+ const { gl, scene } = useThree(({ gl: gl2, scene: scene2 }) => ({ gl: gl2, scene: scene2 }));
1588
+ useEffect(() => {
1589
+ const pmremGenerator = new THREE11.PMREMGenerator(gl);
1590
+ const envTexture = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
1591
+ scene.environment = envTexture;
1592
+ return () => {
1593
+ scene.environment = null;
1594
+ envTexture.dispose();
1595
+ pmremGenerator.dispose();
1596
+ };
1597
+ }, [gl, scene]);
1598
+ return null;
1599
+ }
1251
1600
  var Canvas3DHost = forwardRef(
1252
1601
  ({
1253
1602
  cameraMode = "isometric",
@@ -1273,6 +1622,8 @@ var Canvas3DHost = forwardRef(
1273
1622
  keyUpMap,
1274
1623
  pixelsPerUnit,
1275
1624
  fov,
1625
+ lighting,
1626
+ post,
1276
1627
  children,
1277
1628
  drawables
1278
1629
  }, ref) => {
@@ -1281,6 +1632,7 @@ var Canvas3DHost = forwardRef(
1281
1632
  const [internalError, setInternalError] = useState(null);
1282
1633
  const eventBus = useEventBus();
1283
1634
  const keysRef = useRef(/* @__PURE__ */ new Set());
1635
+ const allDrawables = useMemo(() => drawables ?? [], [drawables]);
1284
1636
  useEffect(() => {
1285
1637
  if (!keyMap && !keyUpMap) return;
1286
1638
  const down = (e) => {
@@ -1314,7 +1666,7 @@ var Canvas3DHost = forwardRef(
1314
1666
  unitAnimationEvent,
1315
1667
  cameraChangeEvent
1316
1668
  });
1317
- const drawnItems = useMemo(() => collectDrawnItems(drawables ?? []), [drawables]);
1669
+ const drawnItems = useMemo(() => collectDrawnItems(allDrawables), [allDrawables]);
1318
1670
  const scenePositions = useMemo(() => drawnItems.map((i) => i.pos), [drawnItems]);
1319
1671
  const hitIndex = useMemo(() => buildHitIndex(drawnItems), [drawnItems]);
1320
1672
  const gridBounds = useMemo(() => {
@@ -1350,6 +1702,7 @@ var Canvas3DHost = forwardRef(
1350
1702
  }),
1351
1703
  [gridBounds, cellSize]
1352
1704
  );
1705
+ const boneStore = useMemo(() => new BoneStore(), []);
1353
1706
  const drawableProjector = useMemo(
1354
1707
  () => create3DProjector({
1355
1708
  cellSize: gridConfig.cellSize,
@@ -1362,7 +1715,7 @@ var Canvas3DHost = forwardRef(
1362
1715
  getCameraPosition: () => {
1363
1716
  if (controlsRef.current) {
1364
1717
  const pos = controlsRef.current.object.position;
1365
- return new THREE10.Vector3(pos.x, pos.y, pos.z);
1718
+ return new THREE11.Vector3(pos.x, pos.y, pos.z);
1366
1719
  }
1367
1720
  return null;
1368
1721
  },
@@ -1407,6 +1760,8 @@ var Canvas3DHost = forwardRef(
1407
1760
  return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
1408
1761
  case "top-down":
1409
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 };
1410
1765
  case "follow":
1411
1766
  return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
1412
1767
  case "perspective":
@@ -1473,6 +1828,7 @@ var Canvas3DHost = forwardRef(
1473
1828
  Canvas,
1474
1829
  {
1475
1830
  shadows,
1831
+ flat: lighting?.toneMapping === "none",
1476
1832
  camera: {
1477
1833
  position: cameraConfig.position,
1478
1834
  fov: cameraConfig.fov,
@@ -1496,9 +1852,19 @@ var Canvas3DHost = forwardRef(
1496
1852
  shadowNormalBias: 0.04,
1497
1853
  shadowCameraSize: 5,
1498
1854
  shadowCameraNear: 0.5,
1499
- shadowCameraFar: 500
1855
+ shadowCameraFar: 500,
1856
+ ambientIntensity: lighting?.ambient?.intensity,
1857
+ ambientColor: lighting?.ambient?.color,
1858
+ directionalIntensity: lighting?.directional?.intensity,
1859
+ directionalColor: lighting?.directional?.color,
1860
+ directionalPosition: lighting?.directional?.position,
1861
+ hemisphereIntensity: lighting?.hemisphere?.intensity,
1862
+ hemisphereColor: lighting?.hemisphere?.color,
1863
+ hemisphereGroundColor: lighting?.hemisphere?.groundColor,
1864
+ points: lighting?.points
1500
1865
  }
1501
1866
  ),
1867
+ lighting?.environment === "room" && /* @__PURE__ */ jsx(RoomEnvironment3D, {}),
1502
1868
  showGrid && /* @__PURE__ */ jsx(
1503
1869
  Grid,
1504
1870
  {
@@ -1521,7 +1887,7 @@ var Canvas3DHost = forwardRef(
1521
1887
  fadeStrength: 1
1522
1888
  }
1523
1889
  ),
1524
- drawables && drawables.length > 0 && /* @__PURE__ */ jsx("group", { children: drawables.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)) }) }),
1525
1891
  (tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxs(
1526
1892
  "mesh",
1527
1893
  {
@@ -1546,7 +1912,7 @@ var Canvas3DHost = forwardRef(
1546
1912
  ]
1547
1913
  }
1548
1914
  ),
1549
- children,
1915
+ post && (post.bloom || post.vignette) ? /* @__PURE__ */ jsx(Effects3D, { post }) : null,
1550
1916
  /* @__PURE__ */ jsx(
1551
1917
  OrbitControls,
1552
1918
  {
@@ -1557,7 +1923,7 @@ var Canvas3DHost = forwardRef(
1557
1923
  dampingFactor: 0.05,
1558
1924
  enableZoom: true,
1559
1925
  enablePan: true,
1560
- touches: { ONE: THREE10.TOUCH.ROTATE, TWO: THREE10.TOUCH.DOLLY_PAN },
1926
+ touches: { ONE: THREE11.TOUCH.ROTATE, TWO: THREE11.TOUCH.DOLLY_PAN },
1561
1927
  minDistance: 2,
1562
1928
  maxDistance: 100,
1563
1929
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -1580,15 +1946,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
1580
1946
  if (initializedRef.current) return;
1581
1947
  initializedRef.current = true;
1582
1948
  if (background.startsWith("#") || background.startsWith("rgb")) {
1583
- scene.background = new THREE10.Color(background);
1949
+ scene.background = new THREE11.Color(background);
1584
1950
  } else {
1585
- const loader = new THREE10.TextureLoader();
1951
+ const loader = new THREE11.TextureLoader();
1586
1952
  loader.load(background, (texture) => {
1587
1953
  scene.background = texture;
1588
1954
  });
1589
1955
  }
1590
1956
  if (fog) {
1591
- scene.fog = new THREE10.Fog(fog.color, fog.near, fog.far);
1957
+ scene.fog = new THREE11.Fog(fog.color, fog.near, fog.far);
1592
1958
  }
1593
1959
  return () => {
1594
1960
  scene.background = null;
@@ -1611,14 +1977,14 @@ var Camera3D = forwardRef(
1611
1977
  }, ref) => {
1612
1978
  const { camera, set, viewport } = useThree();
1613
1979
  const controlsRef = useRef(null);
1614
- const initialPosition = useRef(new THREE10.Vector3(...position));
1615
- const initialTarget = useRef(new THREE10.Vector3(...target));
1980
+ const initialPosition = useRef(new THREE11.Vector3(...position));
1981
+ const initialTarget = useRef(new THREE11.Vector3(...target));
1616
1982
  useEffect(() => {
1617
1983
  let newCamera;
1618
1984
  if (mode === "isometric") {
1619
1985
  const aspect = viewport.aspect;
1620
1986
  const size = 10 / zoom;
1621
- newCamera = new THREE10.OrthographicCamera(
1987
+ newCamera = new THREE11.OrthographicCamera(
1622
1988
  -size * aspect,
1623
1989
  size * aspect,
1624
1990
  size,
@@ -1627,7 +1993,7 @@ var Camera3D = forwardRef(
1627
1993
  1e3
1628
1994
  );
1629
1995
  } else {
1630
- newCamera = new THREE10.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1996
+ newCamera = new THREE11.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
1631
1997
  }
1632
1998
  newCamera.position.copy(initialPosition.current);
1633
1999
  newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
@@ -1668,8 +2034,8 @@ var Camera3D = forwardRef(
1668
2034
  }
1669
2035
  },
1670
2036
  getViewBounds: () => {
1671
- const min = new THREE10.Vector3(-10, -10, -10);
1672
- 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);
1673
2039
  return { min, max };
1674
2040
  }
1675
2041
  }));
@@ -1711,7 +2077,7 @@ var AssetLoader = class {
1711
2077
  __publicField(this, "textureCache");
1712
2078
  __publicField(this, "loadingPromises");
1713
2079
  this.objLoader = new OBJLoader();
1714
- this.textureLoader = new THREE10.TextureLoader();
2080
+ this.textureLoader = new THREE11.TextureLoader();
1715
2081
  this.modelCache = /* @__PURE__ */ new Map();
1716
2082
  this.textureCache = /* @__PURE__ */ new Map();
1717
2083
  this.loadingPromises = /* @__PURE__ */ new Map();
@@ -1785,7 +2151,7 @@ var AssetLoader = class {
1785
2151
  return this.loadingPromises.get(`texture:${url}`);
1786
2152
  }
1787
2153
  const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
1788
- texture.colorSpace = THREE10.SRGBColorSpace;
2154
+ texture.colorSpace = THREE11.SRGBColorSpace;
1789
2155
  this.textureCache.set(url, texture);
1790
2156
  this.loadingPromises.delete(`texture:${url}`);
1791
2157
  return texture;
@@ -1859,7 +2225,7 @@ var AssetLoader = class {
1859
2225
  });
1860
2226
  this.modelCache.forEach((model) => {
1861
2227
  model.scene.traverse((child) => {
1862
- if (child instanceof THREE10.Mesh) {
2228
+ if (child instanceof THREE11.Mesh) {
1863
2229
  child.geometry.dispose();
1864
2230
  if (Array.isArray(child.material)) {
1865
2231
  child.material.forEach((m) => m.dispose());
@@ -1896,7 +2262,7 @@ var DEFAULT_OPTIONS = {
1896
2262
  gridSize: 20,
1897
2263
  assetLoader: new AssetLoader()
1898
2264
  };
1899
- function useThree4(options = {}) {
2265
+ function useThree5(options = {}) {
1900
2266
  const opts = { ...DEFAULT_OPTIONS, ...options };
1901
2267
  const containerRef = useRef(null);
1902
2268
  const canvasRef = useRef(null);
@@ -1909,21 +2275,21 @@ function useThree4(options = {}) {
1909
2275
  const [isReady, setIsReady] = useState(false);
1910
2276
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
1911
2277
  const initialCameraPosition = useMemo(
1912
- () => new THREE10.Vector3(...opts.cameraPosition),
2278
+ () => new THREE11.Vector3(...opts.cameraPosition),
1913
2279
  []
1914
2280
  );
1915
2281
  useEffect(() => {
1916
2282
  if (!containerRef.current) return;
1917
2283
  const container = containerRef.current;
1918
2284
  const { clientWidth, clientHeight } = container;
1919
- const scene = new THREE10.Scene();
1920
- scene.background = new THREE10.Color(opts.backgroundColor);
2285
+ const scene = new THREE11.Scene();
2286
+ scene.background = new THREE11.Color(opts.backgroundColor);
1921
2287
  sceneRef.current = scene;
1922
2288
  let camera;
1923
2289
  const aspect = clientWidth / clientHeight;
1924
2290
  if (opts.cameraMode === "isometric") {
1925
2291
  const size = 10;
1926
- camera = new THREE10.OrthographicCamera(
2292
+ camera = new THREE11.OrthographicCamera(
1927
2293
  -size * aspect,
1928
2294
  size * aspect,
1929
2295
  size,
@@ -1932,11 +2298,11 @@ function useThree4(options = {}) {
1932
2298
  1e3
1933
2299
  );
1934
2300
  } else {
1935
- camera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
2301
+ camera = new THREE11.PerspectiveCamera(45, aspect, 0.1, 1e3);
1936
2302
  }
1937
2303
  camera.position.copy(initialCameraPosition);
1938
2304
  cameraRef.current = camera;
1939
- const renderer = new THREE10.WebGLRenderer({
2305
+ const renderer = new THREE11.WebGLRenderer({
1940
2306
  antialias: true,
1941
2307
  alpha: true,
1942
2308
  canvas: canvasRef.current || void 0
@@ -1944,7 +2310,7 @@ function useThree4(options = {}) {
1944
2310
  renderer.setSize(clientWidth, clientHeight);
1945
2311
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
1946
2312
  renderer.shadowMap.enabled = opts.shadows;
1947
- renderer.shadowMap.type = THREE10.PCFSoftShadowMap;
2313
+ renderer.shadowMap.type = THREE11.PCFSoftShadowMap;
1948
2314
  rendererRef.current = renderer;
1949
2315
  const controls = new OrbitControls$1(camera, renderer.domElement);
1950
2316
  controls.enableDamping = true;
@@ -1953,16 +2319,16 @@ function useThree4(options = {}) {
1953
2319
  controls.maxDistance = 100;
1954
2320
  controls.maxPolarAngle = Math.PI / 2 - 0.1;
1955
2321
  controlsRef.current = controls;
1956
- const ambientLight = new THREE10.AmbientLight(16777215, 0.6);
2322
+ const ambientLight = new THREE11.AmbientLight(16777215, 0.6);
1957
2323
  scene.add(ambientLight);
1958
- const directionalLight = new THREE10.DirectionalLight(16777215, 0.8);
2324
+ const directionalLight = new THREE11.DirectionalLight(16777215, 0.8);
1959
2325
  directionalLight.position.set(10, 20, 10);
1960
2326
  directionalLight.castShadow = opts.shadows;
1961
2327
  directionalLight.shadow.mapSize.width = 2048;
1962
2328
  directionalLight.shadow.mapSize.height = 2048;
1963
2329
  scene.add(directionalLight);
1964
2330
  if (opts.showGrid) {
1965
- const gridHelper = new THREE10.GridHelper(
2331
+ const gridHelper = new THREE11.GridHelper(
1966
2332
  opts.gridSize,
1967
2333
  opts.gridSize,
1968
2334
  4473924,
@@ -1980,10 +2346,10 @@ function useThree4(options = {}) {
1980
2346
  const handleResize = () => {
1981
2347
  const { clientWidth: width, clientHeight: height } = container;
1982
2348
  setDimensions({ width, height });
1983
- if (camera instanceof THREE10.PerspectiveCamera) {
2349
+ if (camera instanceof THREE11.PerspectiveCamera) {
1984
2350
  camera.aspect = width / height;
1985
2351
  camera.updateProjectionMatrix();
1986
- } else if (camera instanceof THREE10.OrthographicCamera) {
2352
+ } else if (camera instanceof THREE11.OrthographicCamera) {
1987
2353
  const aspect2 = width / height;
1988
2354
  const size = 10;
1989
2355
  camera.left = -size * aspect2;
@@ -2014,7 +2380,7 @@ function useThree4(options = {}) {
2014
2380
  let newCamera;
2015
2381
  if (opts.cameraMode === "isometric") {
2016
2382
  const size = 10;
2017
- newCamera = new THREE10.OrthographicCamera(
2383
+ newCamera = new THREE11.OrthographicCamera(
2018
2384
  -size * aspect,
2019
2385
  size * aspect,
2020
2386
  size,
@@ -2023,7 +2389,7 @@ function useThree4(options = {}) {
2023
2389
  1e3
2024
2390
  );
2025
2391
  } else {
2026
- newCamera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
2392
+ newCamera = new THREE11.PerspectiveCamera(45, aspect, 0.1, 1e3);
2027
2393
  }
2028
2394
  newCamera.position.copy(currentPos);
2029
2395
  cameraRef.current = newCamera;
@@ -2353,8 +2719,8 @@ function useSceneGraph() {
2353
2719
  }
2354
2720
  function useRaycaster(options) {
2355
2721
  const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
2356
- const raycaster = useRef(new THREE10.Raycaster());
2357
- const mouse = useRef(new THREE10.Vector2());
2722
+ const raycaster = useRef(new THREE11.Raycaster());
2723
+ const mouse = useRef(new THREE11.Vector2());
2358
2724
  const clientToNDC = useCallback(
2359
2725
  (clientX, clientY) => {
2360
2726
  if (!canvas) {
@@ -2424,8 +2790,8 @@ function useRaycaster(options) {
2424
2790
  const ndc = clientToNDC(clientX, clientY);
2425
2791
  mouse.current.set(ndc.x, ndc.y);
2426
2792
  raycaster.current.setFromCamera(mouse.current, camera);
2427
- const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), 0);
2428
- const target = new THREE10.Vector3();
2793
+ const plane = new THREE11.Plane(new THREE11.Vector3(0, 1, 0), 0);
2794
+ const target = new THREE11.Vector3();
2429
2795
  const intersection = raycaster.current.ray.intersectPlane(plane, target);
2430
2796
  if (intersection) {
2431
2797
  const gridX = Math.round((target.x - offsetX) / cellSize);
@@ -2463,7 +2829,7 @@ function useRaycaster(options) {
2463
2829
  return {
2464
2830
  gridX: gridCoords.x,
2465
2831
  gridZ: gridCoords.z,
2466
- worldPosition: new THREE10.Vector3(
2832
+ worldPosition: new THREE11.Vector3(
2467
2833
  gridCoords.x * cellSize + offsetX,
2468
2834
  0,
2469
2835
  gridCoords.z * cellSize + offsetZ
@@ -2493,7 +2859,7 @@ var DEFAULT_CONFIG = {
2493
2859
  };
2494
2860
  function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
2495
2861
  const opts = { ...DEFAULT_CONFIG, ...config };
2496
- return new THREE10.Vector3(
2862
+ return new THREE11.Vector3(
2497
2863
  gridX * opts.cellSize + opts.offsetX,
2498
2864
  opts.elevation,
2499
2865
  gridZ * opts.cellSize + opts.offsetZ
@@ -2507,17 +2873,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
2507
2873
  };
2508
2874
  }
2509
2875
  function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
2510
- const raycaster = new THREE10.Raycaster();
2511
- const mouse = new THREE10.Vector2(mouseX, mouseY);
2876
+ const raycaster = new THREE11.Raycaster();
2877
+ const mouse = new THREE11.Vector2(mouseX, mouseY);
2512
2878
  raycaster.setFromCamera(mouse, camera);
2513
- const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), -planeY);
2514
- const target = new THREE10.Vector3();
2879
+ const plane = new THREE11.Plane(new THREE11.Vector3(0, 1, 0), -planeY);
2880
+ const target = new THREE11.Vector3();
2515
2881
  const intersection = raycaster.ray.intersectPlane(plane, target);
2516
2882
  return intersection ? target : null;
2517
2883
  }
2518
2884
  function raycastToObjects(camera, mouseX, mouseY, objects) {
2519
- const raycaster = new THREE10.Raycaster();
2520
- const mouse = new THREE10.Vector2(mouseX, mouseY);
2885
+ const raycaster = new THREE11.Raycaster();
2886
+ const mouse = new THREE11.Vector2(mouseX, mouseY);
2521
2887
  raycaster.setFromCamera(mouse, camera);
2522
2888
  const intersects = raycaster.intersectObjects(objects, true);
2523
2889
  return intersects.length > 0 ? intersects[0] : null;
@@ -2569,14 +2935,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
2569
2935
  return cells;
2570
2936
  }
2571
2937
  function createGridHighlight(color = 16776960, opacity = 0.3) {
2572
- const geometry = new THREE10.PlaneGeometry(0.95, 0.95);
2573
- const material = new THREE10.MeshBasicMaterial({
2938
+ const geometry = new THREE11.PlaneGeometry(0.95, 0.95);
2939
+ const material = new THREE11.MeshBasicMaterial({
2574
2940
  color,
2575
2941
  transparent: true,
2576
2942
  opacity,
2577
- side: THREE10.DoubleSide
2943
+ side: THREE11.DoubleSide
2578
2944
  });
2579
- const mesh = new THREE10.Mesh(geometry, material);
2945
+ const mesh = new THREE11.Mesh(geometry, material);
2580
2946
  mesh.rotation.x = -Math.PI / 2;
2581
2947
  mesh.position.y = 0.01;
2582
2948
  return mesh;
@@ -2589,31 +2955,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
2589
2955
  };
2590
2956
  }
2591
2957
  function isInFrustum(position, camera, padding = 0) {
2592
- const frustum = new THREE10.Frustum();
2593
- const projScreenMatrix = new THREE10.Matrix4();
2958
+ const frustum = new THREE11.Frustum();
2959
+ const projScreenMatrix = new THREE11.Matrix4();
2594
2960
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2595
2961
  frustum.setFromProjectionMatrix(projScreenMatrix);
2596
- const sphere = new THREE10.Sphere(position, padding);
2962
+ const sphere = new THREE11.Sphere(position, padding);
2597
2963
  return frustum.intersectsSphere(sphere);
2598
2964
  }
2599
2965
  function filterByFrustum(positions, camera, padding = 0) {
2600
- const frustum = new THREE10.Frustum();
2601
- const projScreenMatrix = new THREE10.Matrix4();
2966
+ const frustum = new THREE11.Frustum();
2967
+ const projScreenMatrix = new THREE11.Matrix4();
2602
2968
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2603
2969
  frustum.setFromProjectionMatrix(projScreenMatrix);
2604
2970
  return positions.filter((position) => {
2605
- const sphere = new THREE10.Sphere(position, padding);
2971
+ const sphere = new THREE11.Sphere(position, padding);
2606
2972
  return frustum.intersectsSphere(sphere);
2607
2973
  });
2608
2974
  }
2609
2975
  function getVisibleIndices(positions, camera, padding = 0) {
2610
- const frustum = new THREE10.Frustum();
2611
- const projScreenMatrix = new THREE10.Matrix4();
2976
+ const frustum = new THREE11.Frustum();
2977
+ const projScreenMatrix = new THREE11.Matrix4();
2612
2978
  const visible = /* @__PURE__ */ new Set();
2613
2979
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
2614
2980
  frustum.setFromProjectionMatrix(projScreenMatrix);
2615
2981
  positions.forEach((position, index) => {
2616
- const sphere = new THREE10.Sphere(position, padding);
2982
+ const sphere = new THREE11.Sphere(position, padding);
2617
2983
  if (frustum.intersectsSphere(sphere)) {
2618
2984
  visible.add(index);
2619
2985
  }
@@ -2637,7 +3003,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
2637
3003
  return lodIndices;
2638
3004
  }
2639
3005
  function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
2640
- const dummy = new THREE10.Object3D();
3006
+ const dummy = new THREE11.Object3D();
2641
3007
  let visibleCount = 0;
2642
3008
  positions.forEach((position, index) => {
2643
3009
  if (visibleIndices.has(index)) {
@@ -5420,12 +5786,12 @@ function useAvl3DConfig() {
5420
5786
  }
5421
5787
  function CameraController({ targetPosition, targetLookAt, animated }) {
5422
5788
  const { camera } = useThree();
5423
- const targetPosVec = useRef(new THREE10.Vector3(...targetPosition));
5424
- const targetLookVec = useRef(new THREE10.Vector3(...targetLookAt));
5789
+ const targetPosVec = useRef(new THREE11.Vector3(...targetPosition));
5790
+ const targetLookVec = useRef(new THREE11.Vector3(...targetLookAt));
5425
5791
  const isAnimating = useRef(false);
5426
5792
  useEffect(() => {
5427
- const newTarget = new THREE10.Vector3(...targetPosition);
5428
- const newLookAt = new THREE10.Vector3(...targetLookAt);
5793
+ const newTarget = new THREE11.Vector3(...targetPosition);
5794
+ const newLookAt = new THREE11.Vector3(...targetLookAt);
5429
5795
  if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
5430
5796
  targetPosVec.current.copy(newTarget);
5431
5797
  targetLookVec.current.copy(newLookAt);
@@ -5440,9 +5806,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
5440
5806
  useFrame((_, delta) => {
5441
5807
  if (!isAnimating.current) return;
5442
5808
  const speed = 3;
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);
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);
5446
5812
  camera.lookAt(targetLookVec.current);
5447
5813
  const dist = camera.position.distanceTo(targetPosVec.current);
5448
5814
  if (dist < 0.05) {
@@ -5457,7 +5823,7 @@ function SceneFade({ animating, children }) {
5457
5823
  useFrame((_, delta) => {
5458
5824
  if (!groupRef.current) return;
5459
5825
  const target = animating ? 0 : 1;
5460
- opacityRef.current = THREE10.MathUtils.damp(opacityRef.current, target, 5, delta);
5826
+ opacityRef.current = THREE11.MathUtils.damp(opacityRef.current, target, 5, delta);
5461
5827
  groupRef.current.visible = opacityRef.current > 0.05;
5462
5828
  const s = 0.9 + opacityRef.current * 0.1;
5463
5829
  groupRef.current.scale.setScalar(s);
@@ -5710,4 +6076,4 @@ var Avl3DViewer = ({
5710
6076
  };
5711
6077
  Avl3DViewer.displayName = "Avl3DViewer";
5712
6078
 
5713
- export { AVL_3D_COLORS, AssetLoader, Avl3DApplicationScene, Avl3DContext, Avl3DEffects, Avl3DOrbitalScene, Avl3DTraitScene, Avl3DTransitionScene, Avl3DViewer, CAMERA_POSITIONS, Camera3D, Canvas3DErrorBoundary, Canvas3DHost, Canvas3DLoadingState, Canvas3DHost as GameCanvas3D, Lighting3D, ModelLoader, Scene3D, SpatialHashGrid, arcCurve3D, assetLoader, calculateLODLevel, createGridHighlight, cullInstancedMesh, fibonacciSpherePositions, filterByFrustum, getCellsInRadius, getNeighbors, getVisibleIndices, goldenSpiralPositions, gridDistance, gridManhattanDistance, gridToWorld, isInBounds, isInFrustum, normalizeMouseCoordinates, orbitRingPositions, raycastToObjects, raycastToPlane, selfLoopCurve3D, treeLayout3D, updateInstanceLOD, useAssetLoader, useAvl3DConfig, useGameCanvas3DEvents, useRaycaster, useSceneGraph, useThree4 as useThree, worldToGrid };
6079
+ export { AVL_3D_COLORS, AssetLoader, Avl3DApplicationScene, Avl3DContext, Avl3DEffects, Avl3DOrbitalScene, Avl3DTraitScene, Avl3DTransitionScene, Avl3DViewer, CAMERA_POSITIONS, Camera3D, Canvas3DErrorBoundary, Canvas3DHost, Canvas3DLoadingState, Canvas3DHost as GameCanvas3D, Lighting3D, ModelLoader, Scene3D, SpatialHashGrid, arcCurve3D, assetLoader, calculateLODLevel, createGridHighlight, cullInstancedMesh, fibonacciSpherePositions, filterByFrustum, getCellsInRadius, getNeighbors, getVisibleIndices, goldenSpiralPositions, gridDistance, gridManhattanDistance, gridToWorld, isInBounds, isInFrustum, normalizeMouseCoordinates, orbitRingPositions, raycastToObjects, raycastToPlane, selfLoopCurve3D, treeLayout3D, updateInstanceLOD, useAssetLoader, useAvl3DConfig, useGameCanvas3DEvents, useRaycaster, useSceneGraph, useThree5 as useThree, worldToGrid };