@almadar/ui 5.140.0 → 5.142.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +1380 -1226
- package/dist/avl/index.js +409 -255
- package/dist/{avl-schema-parser-B8Onmfsu.d.cts → avl-schema-parser-DAs12kxP.d.ts} +229 -2
- package/dist/{avl-schema-parser-B8Onmfsu.d.ts → avl-schema-parser-wOkfbwAh.d.cts} +229 -2
- package/dist/{cn-Do5Ra3l3.d.ts → cn-BDwC4QpH.d.ts} +1 -1
- package/dist/{cn-DkGYzPOg.d.cts → cn-rwc3svaX.d.cts} +1 -1
- package/dist/components/index.cjs +1364 -1210
- package/dist/components/index.d.cts +13 -6
- package/dist/components/index.d.ts +13 -6
- package/dist/components/index.js +400 -246
- package/dist/lib/drawable/three/index.cjs +542 -154
- package/dist/lib/drawable/three/index.d.cts +14 -171
- package/dist/lib/drawable/three/index.d.ts +14 -171
- package/dist/lib/drawable/three/index.js +518 -130
- package/dist/lib/index.d.cts +2 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/{paintDispatch-B5pPeSEp.d.cts → paintDispatch-BQn5Lyx1.d.cts} +160 -3
- package/dist/{paintDispatch-B5pPeSEp.d.ts → paintDispatch-BQn5Lyx1.d.ts} +160 -3
- package/dist/providers/index.cjs +1263 -1109
- package/dist/providers/index.js +384 -230
- package/dist/runtime/index.cjs +1241 -1087
- package/dist/runtime/index.js +389 -235
- package/package.json +4 -4
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import React3, { forwardRef, useRef, useState,
|
|
1
|
+
import React3, { forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle, useCallback, createContext, useContext, Component, useReducer } from 'react';
|
|
2
2
|
import { EventBusContext, useTraitScopeChain } from '@almadar/ui/providers';
|
|
3
3
|
import { createLogger } from '@almadar/logger';
|
|
4
4
|
import { Canvas, useThree, useFrame } from '@react-three/fiber';
|
|
5
|
-
import * as
|
|
5
|
+
import * as THREE10 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;
|
|
@@ -165,6 +166,7 @@ function collectDrawnItems(nodes) {
|
|
|
165
166
|
case "draw-shape":
|
|
166
167
|
case "draw-text":
|
|
167
168
|
case "draw-group":
|
|
169
|
+
case "draw-mesh":
|
|
168
170
|
if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id });
|
|
169
171
|
break;
|
|
170
172
|
case "draw-sprite-layer":
|
|
@@ -449,6 +451,10 @@ function Lighting3D({
|
|
|
449
451
|
directionalIntensity = 0.8,
|
|
450
452
|
directionalColor = "#ffffff",
|
|
451
453
|
directionalPosition = [10, 20, 10],
|
|
454
|
+
hemisphereIntensity = 0.3,
|
|
455
|
+
hemisphereColor = "#87ceeb",
|
|
456
|
+
hemisphereGroundColor = "#362d1d",
|
|
457
|
+
points,
|
|
452
458
|
shadows = true,
|
|
453
459
|
shadowMapSize = 2048,
|
|
454
460
|
shadowCameraSize = 20,
|
|
@@ -481,22 +487,63 @@ function Lighting3D({
|
|
|
481
487
|
/* @__PURE__ */ jsx(
|
|
482
488
|
"hemisphereLight",
|
|
483
489
|
{
|
|
484
|
-
intensity:
|
|
485
|
-
color:
|
|
486
|
-
groundColor:
|
|
490
|
+
intensity: hemisphereIntensity,
|
|
491
|
+
color: hemisphereColor,
|
|
492
|
+
groundColor: hemisphereGroundColor
|
|
487
493
|
}
|
|
488
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
|
+
)),
|
|
489
506
|
showHelpers && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
|
|
490
507
|
"directionalLightHelper",
|
|
491
508
|
{
|
|
492
509
|
args: [
|
|
493
|
-
new
|
|
510
|
+
new THREE10.DirectionalLight(directionalColor, directionalIntensity),
|
|
494
511
|
5
|
|
495
512
|
]
|
|
496
513
|
}
|
|
497
514
|
) })
|
|
498
515
|
] });
|
|
499
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
|
+
}
|
|
500
547
|
function CameraController3D({
|
|
501
548
|
onCameraChange
|
|
502
549
|
}) {
|
|
@@ -517,8 +564,8 @@ function FollowCamera3D({
|
|
|
517
564
|
offset
|
|
518
565
|
}) {
|
|
519
566
|
const { camera } = useThree();
|
|
520
|
-
const look = useRef(new
|
|
521
|
-
const goal = useRef(new
|
|
567
|
+
const look = useRef(new THREE10.Vector3(target[0], target[1], target[2]));
|
|
568
|
+
const goal = useRef(new THREE10.Vector3());
|
|
522
569
|
useFrame((_, delta) => {
|
|
523
570
|
const t = Math.min(1, delta * 5);
|
|
524
571
|
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
@@ -529,6 +576,88 @@ function FollowCamera3D({
|
|
|
529
576
|
return null;
|
|
530
577
|
}
|
|
531
578
|
|
|
579
|
+
// lib/drawable/projector3d.ts
|
|
580
|
+
function create3DProjector(opts = {}) {
|
|
581
|
+
const cellSize = opts.cellSize ?? 1;
|
|
582
|
+
const offsetX = opts.offsetX ?? 0;
|
|
583
|
+
const offsetZ = opts.offsetZ ?? 0;
|
|
584
|
+
return {
|
|
585
|
+
cellSize,
|
|
586
|
+
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
var NUMERIC_TRACKS = [
|
|
590
|
+
"offsetX",
|
|
591
|
+
"offsetY",
|
|
592
|
+
"rotate",
|
|
593
|
+
"opacity",
|
|
594
|
+
"radiusX",
|
|
595
|
+
"radiusY",
|
|
596
|
+
"width",
|
|
597
|
+
"height",
|
|
598
|
+
"strokeWidth",
|
|
599
|
+
"strokeDashOffset",
|
|
600
|
+
"blur"
|
|
601
|
+
];
|
|
602
|
+
function isAnimatedShape(node) {
|
|
603
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
604
|
+
}
|
|
605
|
+
var lerp = (a, b, k) => a + (b - a) * k;
|
|
606
|
+
function applyShapeAnimation(node, timeMs) {
|
|
607
|
+
const anim = node.animation;
|
|
608
|
+
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
|
|
609
|
+
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
610
|
+
const cycle = timeMs / anim.durationMs;
|
|
611
|
+
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
612
|
+
const out = { ...node };
|
|
613
|
+
const trackValue = (key) => {
|
|
614
|
+
const defined = frames.filter((f) => f[key] !== void 0);
|
|
615
|
+
if (defined.length === 0) return void 0;
|
|
616
|
+
let prev;
|
|
617
|
+
let next;
|
|
618
|
+
for (const f of defined) {
|
|
619
|
+
if (f.at <= t) prev = f;
|
|
620
|
+
else if (!next) next = f;
|
|
621
|
+
}
|
|
622
|
+
if (!prev) return defined[0][key];
|
|
623
|
+
if (!next) return prev[key];
|
|
624
|
+
const span = next.at - prev.at;
|
|
625
|
+
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
626
|
+
const a = prev[key];
|
|
627
|
+
const b = next[key];
|
|
628
|
+
if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
|
|
629
|
+
return a;
|
|
630
|
+
};
|
|
631
|
+
for (const key of NUMERIC_TRACKS) {
|
|
632
|
+
const v = trackValue(key);
|
|
633
|
+
if (v !== void 0) out[key] = v;
|
|
634
|
+
}
|
|
635
|
+
const fill = trackValue("fill");
|
|
636
|
+
if (fill !== void 0) out.fill = fill;
|
|
637
|
+
const stroke = trackValue("stroke");
|
|
638
|
+
if (stroke !== void 0) out.stroke = stroke;
|
|
639
|
+
const shadowFrames = frames.filter((f) => f.shadow !== void 0);
|
|
640
|
+
if (shadowFrames.length > 0) {
|
|
641
|
+
const sh = trackValue("shadow");
|
|
642
|
+
if (sh !== void 0) {
|
|
643
|
+
let prevSh;
|
|
644
|
+
let nextSh;
|
|
645
|
+
for (const f of shadowFrames) {
|
|
646
|
+
if (f.at <= t) prevSh = f;
|
|
647
|
+
else if (!nextSh) nextSh = f;
|
|
648
|
+
}
|
|
649
|
+
if (prevSh?.shadow && nextSh?.shadow) {
|
|
650
|
+
const span = nextSh.at - prevSh.at;
|
|
651
|
+
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) };
|
|
653
|
+
} else {
|
|
654
|
+
out.shadow = sh;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return out;
|
|
659
|
+
}
|
|
660
|
+
|
|
532
661
|
// lib/atlasSlice.ts
|
|
533
662
|
var atlasCache = /* @__PURE__ */ new Map();
|
|
534
663
|
function isTilesheet(a) {
|
|
@@ -663,12 +792,12 @@ function ModelLoader({
|
|
|
663
792
|
if (!loadedModel) return null;
|
|
664
793
|
const cloned = clone(loadedModel);
|
|
665
794
|
cloned.updateMatrixWorld(true);
|
|
666
|
-
const tintColor = tint ? new
|
|
795
|
+
const tintColor = tint ? new THREE10.Color(tint) : null;
|
|
667
796
|
cloned.traverse((child) => {
|
|
668
|
-
if (child instanceof
|
|
797
|
+
if (child instanceof THREE10.Mesh) {
|
|
669
798
|
child.castShadow = castShadow;
|
|
670
799
|
child.receiveShadow = receiveShadow;
|
|
671
|
-
if (tintColor && child.material instanceof
|
|
800
|
+
if (tintColor && child.material instanceof THREE10.MeshStandardMaterial) {
|
|
672
801
|
const mat = child.material.clone();
|
|
673
802
|
mat.color.multiply(tintColor);
|
|
674
803
|
child.material = mat;
|
|
@@ -677,7 +806,7 @@ function ModelLoader({
|
|
|
677
806
|
});
|
|
678
807
|
return cloned;
|
|
679
808
|
}, [loadedModel, castShadow, receiveShadow, tint]);
|
|
680
|
-
const mixer = useMemo(() => model ? new
|
|
809
|
+
const mixer = useMemo(() => model ? new THREE10.AnimationMixer(model) : null, [model]);
|
|
681
810
|
useEffect(() => {
|
|
682
811
|
if (!mixer || !animation || clips.length === 0) return;
|
|
683
812
|
const wanted = animation.toLowerCase();
|
|
@@ -694,8 +823,8 @@ function ModelLoader({
|
|
|
694
823
|
});
|
|
695
824
|
const normFactor = useMemo(() => {
|
|
696
825
|
if (!model) return 1;
|
|
697
|
-
const box = new
|
|
698
|
-
const size = new
|
|
826
|
+
const box = new THREE10.Box3().setFromObject(model);
|
|
827
|
+
const size = new THREE10.Vector3();
|
|
699
828
|
box.getSize(size);
|
|
700
829
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
701
830
|
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
@@ -785,7 +914,7 @@ var warnUnsupported3d = (kind) => {
|
|
|
785
914
|
warnedUnsupported.add(kind);
|
|
786
915
|
mesh3dLog.warn("unsupported drawable kind on the 3D backend \u2014 skipped", { kind });
|
|
787
916
|
};
|
|
788
|
-
var CrossOriginTextureLoader = class extends
|
|
917
|
+
var CrossOriginTextureLoader = class extends THREE10.TextureLoader {
|
|
789
918
|
constructor() {
|
|
790
919
|
super();
|
|
791
920
|
this.crossOrigin = "anonymous";
|
|
@@ -803,7 +932,7 @@ function useBillboardTexture(url) {
|
|
|
803
932
|
url,
|
|
804
933
|
(texture) => {
|
|
805
934
|
if (!active) return;
|
|
806
|
-
texture.colorSpace =
|
|
935
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
807
936
|
setState({ texture, error: false });
|
|
808
937
|
},
|
|
809
938
|
void 0,
|
|
@@ -835,7 +964,7 @@ function useAtlasFrame(asset) {
|
|
|
835
964
|
return { frame: { x: r.sx, y: r.sy, w: r.sw, h: r.sh }, ready: true };
|
|
836
965
|
}, [asset, tick]);
|
|
837
966
|
}
|
|
838
|
-
function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
967
|
+
function SpriteBillboard({ node, world, cellSize = 1, groupOpacity = 1 }) {
|
|
839
968
|
const { texture, error: textureError } = useBillboardTexture(node.asset.url);
|
|
840
969
|
const { frame: atlasFrame, ready: atlasReady } = useAtlasFrame(node.asset);
|
|
841
970
|
const frame = node.frame ?? atlasFrame;
|
|
@@ -856,7 +985,7 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
856
985
|
}, [texture, frame, node.height, node.width, anchor, cellSize]);
|
|
857
986
|
const groundGeometry = React3.useMemo(() => {
|
|
858
987
|
if (anchor !== "top-left" || !texture || !atlasReady) return null;
|
|
859
|
-
const g = new
|
|
988
|
+
const g = new THREE10.PlaneGeometry(size.width, size.height);
|
|
860
989
|
g.rotateX(-Math.PI / 2);
|
|
861
990
|
return g;
|
|
862
991
|
}, [anchor, texture, atlasReady, size.width, size.height]);
|
|
@@ -864,15 +993,15 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
864
993
|
if (anchor === "top-left") {
|
|
865
994
|
return /* @__PURE__ */ jsx("group", { position: [world[0] + size.width / 2, 0.02, world[2] + size.height / 2], children: /* @__PURE__ */ jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [
|
|
866
995
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
867
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
996
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
|
|
868
997
|
] }) });
|
|
869
998
|
}
|
|
870
999
|
return /* @__PURE__ */ jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxs("mesh", { children: [
|
|
871
1000
|
/* @__PURE__ */ jsx("planeGeometry", { args: [size.width, size.height] }),
|
|
872
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side:
|
|
1001
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE10.DoubleSide })
|
|
873
1002
|
] }) });
|
|
874
1003
|
}
|
|
875
|
-
texture.magFilter = texture.minFilter =
|
|
1004
|
+
texture.magFilter = texture.minFilter = THREE10.NearestFilter;
|
|
876
1005
|
texture.needsUpdate = true;
|
|
877
1006
|
if (frame) {
|
|
878
1007
|
texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
|
|
@@ -885,8 +1014,8 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
885
1014
|
map: texture,
|
|
886
1015
|
transparent: true,
|
|
887
1016
|
alphaTest: 0.1,
|
|
888
|
-
side:
|
|
889
|
-
opacity: node.opacity ?? 1
|
|
1017
|
+
side: THREE10.DoubleSide,
|
|
1018
|
+
opacity: (node.opacity ?? 1) * groupOpacity
|
|
890
1019
|
}
|
|
891
1020
|
) }) });
|
|
892
1021
|
}
|
|
@@ -898,13 +1027,13 @@ function SpriteBillboard({ node, world, cellSize = 1 }) {
|
|
|
898
1027
|
map: texture,
|
|
899
1028
|
transparent: true,
|
|
900
1029
|
alphaTest: 0.1,
|
|
901
|
-
side:
|
|
1030
|
+
side: THREE10.DoubleSide,
|
|
902
1031
|
opacity: node.opacity ?? 1
|
|
903
1032
|
}
|
|
904
1033
|
)
|
|
905
1034
|
] }) });
|
|
906
1035
|
}
|
|
907
|
-
function Sprite3D({ node, projector }) {
|
|
1036
|
+
function Sprite3D({ node, projector, groupOpacity = 1 }) {
|
|
908
1037
|
const asset = node.asset;
|
|
909
1038
|
if (!asset?.url || !isValidScenePos(node.position)) return null;
|
|
910
1039
|
if (asset.dimension === "3d") {
|
|
@@ -918,7 +1047,7 @@ function Sprite3D({ node, projector }) {
|
|
|
918
1047
|
{
|
|
919
1048
|
url: asset.url,
|
|
920
1049
|
scale,
|
|
921
|
-
rotation: [0, node.rotation ?? 0, 0],
|
|
1050
|
+
rotation: [0, (node.rotation ?? 0) * 180 / Math.PI, 0],
|
|
922
1051
|
animation: node.animation,
|
|
923
1052
|
fallbackGeometry: "box",
|
|
924
1053
|
castShadow: true,
|
|
@@ -926,11 +1055,30 @@ function Sprite3D({ node, projector }) {
|
|
|
926
1055
|
}
|
|
927
1056
|
) });
|
|
928
1057
|
}
|
|
929
|
-
return /* @__PURE__ */ jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize });
|
|
1058
|
+
return /* @__PURE__ */ jsx(SpriteBillboard, { node, world: projector.toWorld(node.position), cellSize: projector.cellSize, groupOpacity });
|
|
930
1059
|
}
|
|
931
|
-
function Shape3D({ node, projector }) {
|
|
932
|
-
|
|
933
|
-
const
|
|
1060
|
+
function Shape3D({ node, projector, groupOpacity = 1 }) {
|
|
1061
|
+
const groupRef = React3.useRef(null);
|
|
1062
|
+
const materialRef = React3.useRef(null);
|
|
1063
|
+
const animated = isAnimatedShape(node);
|
|
1064
|
+
const validPos = isValidScenePos(node.position);
|
|
1065
|
+
const world = validPos ? projector.toWorld(node.position) : [0, 0, 0];
|
|
1066
|
+
useFrame(({ clock }) => {
|
|
1067
|
+
if (!animated || !groupRef.current) return;
|
|
1068
|
+
const view = applyShapeAnimation(node, clock.elapsedTime * 1e3);
|
|
1069
|
+
groupRef.current.position.set(
|
|
1070
|
+
world[0] + (view.offsetX ?? 0) * projector.cellSize,
|
|
1071
|
+
world[1] + 0.02,
|
|
1072
|
+
world[2] + (view.offsetY ?? 0) * projector.cellSize
|
|
1073
|
+
);
|
|
1074
|
+
groupRef.current.rotation.y = -(view.rotate ?? 0);
|
|
1075
|
+
const mat = materialRef.current;
|
|
1076
|
+
if (mat) {
|
|
1077
|
+
mat.opacity = (view.opacity ?? node.opacity ?? 1) * groupOpacity;
|
|
1078
|
+
if (view.fill) mat.color.set(view.fill);
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
if (!validPos) return null;
|
|
934
1082
|
const color = node.fill ?? node.stroke ?? "#ffffff";
|
|
935
1083
|
let geometry;
|
|
936
1084
|
switch (node.shape) {
|
|
@@ -948,7 +1096,7 @@ function Shape3D({ node, projector }) {
|
|
|
948
1096
|
}
|
|
949
1097
|
case "poly": {
|
|
950
1098
|
if (!node.points || node.points.length === 0) return null;
|
|
951
|
-
const s = new
|
|
1099
|
+
const s = new THREE10.Shape();
|
|
952
1100
|
node.points.forEach((p, i) => {
|
|
953
1101
|
if (i === 0) s.moveTo(p.x, p.y);
|
|
954
1102
|
else s.lineTo(p.x, p.y);
|
|
@@ -964,12 +1112,12 @@ function Shape3D({ node, projector }) {
|
|
|
964
1112
|
default:
|
|
965
1113
|
return null;
|
|
966
1114
|
}
|
|
967
|
-
return /* @__PURE__ */
|
|
1115
|
+
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: [
|
|
968
1116
|
geometry,
|
|
969
|
-
/* @__PURE__ */ jsx("meshBasicMaterial", { color, transparent: true, opacity: node.opacity ?? 1, side:
|
|
970
|
-
] });
|
|
1117
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { ref: materialRef, color, transparent: true, opacity: (node.opacity ?? 1) * groupOpacity, side: THREE10.DoubleSide })
|
|
1118
|
+
] }) });
|
|
971
1119
|
}
|
|
972
|
-
function Text3D({ node, projector }) {
|
|
1120
|
+
function Text3D({ node, projector, groupOpacity = 1 }) {
|
|
973
1121
|
if (!isValidScenePos(node.position)) return null;
|
|
974
1122
|
const world = projector.toWorld(node.position);
|
|
975
1123
|
return /* @__PURE__ */ jsx(Billboard, { position: [world[0], world[1] + 1.2, world[2]], children: /* @__PURE__ */ jsx(
|
|
@@ -981,41 +1129,252 @@ function Text3D({ node, projector }) {
|
|
|
981
1129
|
anchorY: "middle",
|
|
982
1130
|
outlineWidth: 0.02,
|
|
983
1131
|
outlineColor: "#000000",
|
|
1132
|
+
fillOpacity: groupOpacity,
|
|
1133
|
+
outlineOpacity: groupOpacity,
|
|
984
1134
|
children: node.text
|
|
985
1135
|
}
|
|
986
1136
|
) });
|
|
987
1137
|
}
|
|
988
|
-
|
|
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;
|
|
1172
|
+
}
|
|
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;
|
|
1186
|
+
}
|
|
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
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
function meshGeometry(node, cellSize) {
|
|
1198
|
+
const seg = clampSegments(node.segments);
|
|
1199
|
+
const r = (node.radius ?? 0.4) * cellSize;
|
|
1200
|
+
const w = (node.width ?? 1) * cellSize;
|
|
1201
|
+
const h = (node.height ?? 1) * cellSize;
|
|
1202
|
+
const d = (node.depth ?? node.width ?? 1) * cellSize;
|
|
1203
|
+
switch (node.shape) {
|
|
1204
|
+
case "box":
|
|
1205
|
+
return { element: /* @__PURE__ */ jsx("boxGeometry", { args: [w, h, d] }), lift: h / 2, size: Math.max(w, h, d) };
|
|
1206
|
+
case "sphere":
|
|
1207
|
+
return { element: /* @__PURE__ */ jsx("sphereGeometry", { args: [r, seg, seg] }), lift: r, size: r * 2 };
|
|
1208
|
+
case "capsule":
|
|
1209
|
+
return { element: /* @__PURE__ */ jsx("capsuleGeometry", { args: [r, h, Math.max(2, Math.round(seg / 2)), seg] }), lift: h / 2 + r, size: h + r * 2 };
|
|
1210
|
+
case "cylinder":
|
|
1211
|
+
return {
|
|
1212
|
+
element: /* @__PURE__ */ jsx("cylinderGeometry", { args: [(node.radiusTop ?? node.radius ?? 0.4) * cellSize, (node.radiusBottom ?? node.radius ?? 0.4) * cellSize, h, seg] }),
|
|
1213
|
+
lift: h / 2,
|
|
1214
|
+
size: Math.max(h, r * 2)
|
|
1215
|
+
};
|
|
1216
|
+
case "cone":
|
|
1217
|
+
return { element: /* @__PURE__ */ jsx("coneGeometry", { args: [r, h, seg] }), lift: h / 2, size: Math.max(h, r * 2) };
|
|
1218
|
+
case "torus": {
|
|
1219
|
+
const tube = (node.tube ?? (node.radius ?? 0.4) / 3) * cellSize;
|
|
1220
|
+
return { element: /* @__PURE__ */ jsx("torusGeometry", { args: [r, tube, Math.max(3, Math.round(seg / 2)), seg] }), lift: r + tube, size: (r + tube) * 2 };
|
|
1221
|
+
}
|
|
1222
|
+
case "plane":
|
|
1223
|
+
return { element: /* @__PURE__ */ jsx("planeGeometry", { args: [w, d] }), lift: 0, size: Math.max(w, d) };
|
|
1224
|
+
case "circle":
|
|
1225
|
+
return { element: /* @__PURE__ */ jsx("circleGeometry", { args: [r, seg] }), lift: 0, size: r * 2 };
|
|
1226
|
+
default:
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
var SIDE_MAP = {
|
|
1231
|
+
front: THREE10.FrontSide,
|
|
1232
|
+
back: THREE10.BackSide,
|
|
1233
|
+
double: THREE10.DoubleSide
|
|
1234
|
+
};
|
|
1235
|
+
function meshMaterial(mat, opacity, ref) {
|
|
1236
|
+
const m = mat ?? {};
|
|
1237
|
+
const common = {
|
|
1238
|
+
ref,
|
|
1239
|
+
color: m.color ?? "#ffffff",
|
|
1240
|
+
transparent: opacity < 1,
|
|
1241
|
+
opacity,
|
|
1242
|
+
side: SIDE_MAP[m.side ?? "front"]
|
|
1243
|
+
};
|
|
1244
|
+
const emissive = m.emissive ? { emissive: m.emissive, emissiveIntensity: m.emissiveIntensity ?? 1 } : {};
|
|
1245
|
+
switch (m.kind ?? "standard") {
|
|
1246
|
+
case "basic":
|
|
1247
|
+
return /* @__PURE__ */ jsx("meshBasicMaterial", { ...common });
|
|
1248
|
+
case "toon":
|
|
1249
|
+
return /* @__PURE__ */ jsx("meshToonMaterial", { ...common, ...emissive });
|
|
1250
|
+
case "physical":
|
|
1251
|
+
return /* @__PURE__ */ jsx(
|
|
1252
|
+
"meshPhysicalMaterial",
|
|
1253
|
+
{
|
|
1254
|
+
...common,
|
|
1255
|
+
...emissive,
|
|
1256
|
+
metalness: m.metalness ?? 0,
|
|
1257
|
+
roughness: m.roughness ?? 0.5,
|
|
1258
|
+
transmission: m.transmission ?? 0,
|
|
1259
|
+
ior: m.ior ?? 1.5,
|
|
1260
|
+
flatShading: m.flatShading ?? false
|
|
1261
|
+
}
|
|
1262
|
+
);
|
|
1263
|
+
case "standard":
|
|
1264
|
+
default:
|
|
1265
|
+
return /* @__PURE__ */ jsx(
|
|
1266
|
+
"meshStandardMaterial",
|
|
1267
|
+
{
|
|
1268
|
+
...common,
|
|
1269
|
+
...emissive,
|
|
1270
|
+
metalness: m.metalness ?? 0,
|
|
1271
|
+
roughness: m.roughness ?? 0.5,
|
|
1272
|
+
flatShading: m.flatShading ?? false
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function Mesh3D({
|
|
1278
|
+
node,
|
|
1279
|
+
projector,
|
|
1280
|
+
groupOpacity = 1
|
|
1281
|
+
}) {
|
|
1282
|
+
const groupRef = useRef(null);
|
|
1283
|
+
const meshRef = useRef(null);
|
|
1284
|
+
const materialRef = useRef(null);
|
|
1285
|
+
const animated = isAnimatedMesh(node);
|
|
1286
|
+
const validPos = isValidScenePos(node.position);
|
|
1287
|
+
const baseWorld = validPos ? projector.toWorld(node.position) : [0, 0, 0];
|
|
1288
|
+
const geo = useMemo(() => meshGeometry(node, projector.cellSize), [node, projector.cellSize]);
|
|
1289
|
+
useFrame(({ clock }) => {
|
|
1290
|
+
if (!animated || !groupRef.current) return;
|
|
1291
|
+
const state = applyMeshAnimation(node, clock.elapsedTime * 1e3);
|
|
1292
|
+
if (!state) return;
|
|
1293
|
+
const [wx, wy, wz] = baseWorld;
|
|
1294
|
+
groupRef.current.position.set(
|
|
1295
|
+
wx + state.offset[0] * projector.cellSize,
|
|
1296
|
+
wy + state.offset[2] * projector.cellSize,
|
|
1297
|
+
wz + state.offset[1] * projector.cellSize
|
|
1298
|
+
);
|
|
1299
|
+
groupRef.current.scale.setScalar(state.scale);
|
|
1300
|
+
if (meshRef.current) {
|
|
1301
|
+
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]);
|
|
1303
|
+
}
|
|
1304
|
+
const mat = materialRef.current;
|
|
1305
|
+
if (mat) {
|
|
1306
|
+
const effective = (state.opacity ?? node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
|
|
1307
|
+
mat.opacity = effective;
|
|
1308
|
+
mat.transparent = effective < 1;
|
|
1309
|
+
if (state.color && mat.color) mat.color.set(state.color);
|
|
1310
|
+
if (state.emissive && mat.emissive) mat.emissive.set(state.emissive);
|
|
1311
|
+
if (state.emissiveIntensity !== void 0 && mat.emissiveIntensity !== void 0) {
|
|
1312
|
+
mat.emissiveIntensity = state.emissiveIntensity;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
if (!validPos || !geo) return null;
|
|
1317
|
+
const lift = (node.pivot ?? "bottom") === "bottom" ? geo.lift : 0;
|
|
1318
|
+
const opacity = (node.opacity ?? 1) * (node.material?.opacity ?? 1) * groupOpacity;
|
|
1319
|
+
const rotation = node.rotation ?? [0, 0, 0];
|
|
1320
|
+
const outlineScale = geo.size > 0 ? 1 + (node.outline?.width ?? 0.05) * projector.cellSize / geo.size : 1;
|
|
1321
|
+
return /* @__PURE__ */ jsxs("group", { ref: groupRef, position: baseWorld, children: [
|
|
1322
|
+
/* @__PURE__ */ jsxs(
|
|
1323
|
+
"mesh",
|
|
1324
|
+
{
|
|
1325
|
+
ref: meshRef,
|
|
1326
|
+
position: [0, lift, 0],
|
|
1327
|
+
rotation,
|
|
1328
|
+
castShadow: node.castShadow ?? true,
|
|
1329
|
+
receiveShadow: node.receiveShadow ?? true,
|
|
1330
|
+
children: [
|
|
1331
|
+
geo.element,
|
|
1332
|
+
meshMaterial(node.material, opacity, materialRef)
|
|
1333
|
+
]
|
|
1334
|
+
}
|
|
1335
|
+
),
|
|
1336
|
+
node.outline && /* @__PURE__ */ jsxs("mesh", { position: [0, lift, 0], rotation, scale: outlineScale, children: [
|
|
1337
|
+
geo.element,
|
|
1338
|
+
/* @__PURE__ */ jsx(
|
|
1339
|
+
"meshBasicMaterial",
|
|
1340
|
+
{
|
|
1341
|
+
color: node.outline.color ?? "#101014",
|
|
1342
|
+
side: THREE10.BackSide,
|
|
1343
|
+
transparent: opacity < 1,
|
|
1344
|
+
opacity
|
|
1345
|
+
}
|
|
1346
|
+
)
|
|
1347
|
+
] })
|
|
1348
|
+
] });
|
|
1349
|
+
}
|
|
1350
|
+
function Drawable3D({ node, projector, groupOpacity = 1 }) {
|
|
989
1351
|
switch (node.type) {
|
|
990
1352
|
case "draw-sprite":
|
|
991
|
-
return /* @__PURE__ */ jsx(Sprite3D, { node, projector });
|
|
1353
|
+
return /* @__PURE__ */ jsx(Sprite3D, { node, projector, groupOpacity });
|
|
992
1354
|
case "draw-shape":
|
|
993
|
-
return /* @__PURE__ */ jsx(Shape3D, { node, projector });
|
|
1355
|
+
return /* @__PURE__ */ jsx(Shape3D, { node, projector, groupOpacity });
|
|
994
1356
|
case "draw-text":
|
|
995
|
-
return /* @__PURE__ */ jsx(Text3D, { node, projector });
|
|
1357
|
+
return /* @__PURE__ */ jsx(Text3D, { node, projector, groupOpacity });
|
|
1358
|
+
case "draw-mesh":
|
|
1359
|
+
return /* @__PURE__ */ jsx(Mesh3D, { node, projector, groupOpacity });
|
|
996
1360
|
case "draw-sprite-layer":
|
|
997
|
-
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Sprite3D, { node: item, projector }, i)) });
|
|
1361
|
+
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Sprite3D, { node: item, projector, groupOpacity }, i)) });
|
|
998
1362
|
case "draw-shape-layer":
|
|
999
|
-
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Shape3D, { node: item, projector }, i)) });
|
|
1363
|
+
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Shape3D, { node: item, projector, groupOpacity }, i)) });
|
|
1000
1364
|
case "draw-text-layer":
|
|
1001
|
-
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Text3D, { node: item, projector }, i)) });
|
|
1002
|
-
case "draw-group":
|
|
1003
|
-
|
|
1004
|
-
|
|
1365
|
+
return /* @__PURE__ */ jsx(Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsx(Text3D, { node: item, projector, groupOpacity }, i)) });
|
|
1366
|
+
case "draw-group": {
|
|
1367
|
+
if (!isValidScenePos(node.position) || !Array.isArray(node.items)) return null;
|
|
1368
|
+
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)) });
|
|
1374
|
+
}
|
|
1005
1375
|
}
|
|
1006
1376
|
}
|
|
1007
1377
|
|
|
1008
|
-
// lib/drawable/projector3d.ts
|
|
1009
|
-
function create3DProjector(opts = {}) {
|
|
1010
|
-
const cellSize = opts.cellSize ?? 1;
|
|
1011
|
-
const offsetX = opts.offsetX ?? 0;
|
|
1012
|
-
const offsetZ = opts.offsetZ ?? 0;
|
|
1013
|
-
return {
|
|
1014
|
-
cellSize,
|
|
1015
|
-
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
1378
|
// lib/drawable/three/game3dTheme.ts
|
|
1020
1379
|
var GRID_COLORS_3D = {
|
|
1021
1380
|
cell: "#444444",
|
|
@@ -1027,6 +1386,20 @@ function cn(...inputs) {
|
|
|
1027
1386
|
}
|
|
1028
1387
|
var DEFAULT_GRID_CONFIG = {
|
|
1029
1388
|
cellSize: 1};
|
|
1389
|
+
function RoomEnvironment3D() {
|
|
1390
|
+
const { gl, scene } = useThree(({ gl: gl2, scene: scene2 }) => ({ gl: gl2, scene: scene2 }));
|
|
1391
|
+
useEffect(() => {
|
|
1392
|
+
const pmremGenerator = new THREE10.PMREMGenerator(gl);
|
|
1393
|
+
const envTexture = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
|
|
1394
|
+
scene.environment = envTexture;
|
|
1395
|
+
return () => {
|
|
1396
|
+
scene.environment = null;
|
|
1397
|
+
envTexture.dispose();
|
|
1398
|
+
pmremGenerator.dispose();
|
|
1399
|
+
};
|
|
1400
|
+
}, [gl, scene]);
|
|
1401
|
+
return null;
|
|
1402
|
+
}
|
|
1030
1403
|
var Canvas3DHost = forwardRef(
|
|
1031
1404
|
({
|
|
1032
1405
|
cameraMode = "isometric",
|
|
@@ -1051,6 +1424,9 @@ var Canvas3DHost = forwardRef(
|
|
|
1051
1424
|
keyMap,
|
|
1052
1425
|
keyUpMap,
|
|
1053
1426
|
pixelsPerUnit,
|
|
1427
|
+
fov,
|
|
1428
|
+
lighting,
|
|
1429
|
+
post,
|
|
1054
1430
|
children,
|
|
1055
1431
|
drawables
|
|
1056
1432
|
}, ref) => {
|
|
@@ -1059,6 +1435,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1059
1435
|
const [internalError, setInternalError] = useState(null);
|
|
1060
1436
|
const eventBus = useEventBus();
|
|
1061
1437
|
const keysRef = useRef(/* @__PURE__ */ new Set());
|
|
1438
|
+
const allDrawables = useMemo(() => drawables ?? [], [drawables]);
|
|
1062
1439
|
useEffect(() => {
|
|
1063
1440
|
if (!keyMap && !keyUpMap) return;
|
|
1064
1441
|
const down = (e) => {
|
|
@@ -1092,7 +1469,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1092
1469
|
unitAnimationEvent,
|
|
1093
1470
|
cameraChangeEvent
|
|
1094
1471
|
});
|
|
1095
|
-
const drawnItems = useMemo(() => collectDrawnItems(
|
|
1472
|
+
const drawnItems = useMemo(() => collectDrawnItems(allDrawables), [allDrawables]);
|
|
1096
1473
|
const scenePositions = useMemo(() => drawnItems.map((i) => i.pos), [drawnItems]);
|
|
1097
1474
|
const hitIndex = useMemo(() => buildHitIndex(drawnItems), [drawnItems]);
|
|
1098
1475
|
const gridBounds = useMemo(() => {
|
|
@@ -1140,7 +1517,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1140
1517
|
getCameraPosition: () => {
|
|
1141
1518
|
if (controlsRef.current) {
|
|
1142
1519
|
const pos = controlsRef.current.object.position;
|
|
1143
|
-
return new
|
|
1520
|
+
return new THREE10.Vector3(pos.x, pos.y, pos.z);
|
|
1144
1521
|
}
|
|
1145
1522
|
return null;
|
|
1146
1523
|
},
|
|
@@ -1179,18 +1556,19 @@ var Canvas3DHost = forwardRef(
|
|
|
1179
1556
|
const cx = cameraTarget[0];
|
|
1180
1557
|
const cz = cameraTarget[2];
|
|
1181
1558
|
const d = size * 1;
|
|
1559
|
+
const fovDeg = fov ?? 45;
|
|
1182
1560
|
switch (cameraMode) {
|
|
1183
1561
|
case "isometric":
|
|
1184
|
-
return { position: [cx + d, d * 0.8, cz + d], fov:
|
|
1562
|
+
return { position: [cx + d, d * 0.8, cz + d], fov: fovDeg };
|
|
1185
1563
|
case "top-down":
|
|
1186
|
-
return { position: [cx, d * 2, cz + d * 0.35], fov:
|
|
1564
|
+
return { position: [cx, d * 2, cz + d * 0.35], fov: fovDeg };
|
|
1187
1565
|
case "follow":
|
|
1188
|
-
return { position: [cx, d * 0.5, cz + d], fov:
|
|
1566
|
+
return { position: [cx, d * 0.5, cz + d], fov: fovDeg };
|
|
1189
1567
|
case "perspective":
|
|
1190
1568
|
default:
|
|
1191
|
-
return { position: [cx + d, d, cz + d], fov:
|
|
1569
|
+
return { position: [cx + d, d, cz + d], fov: fovDeg };
|
|
1192
1570
|
}
|
|
1193
|
-
}, [cameraMode, gridBounds, cellSize, cameraTarget]);
|
|
1571
|
+
}, [cameraMode, gridBounds, cellSize, cameraTarget, fov]);
|
|
1194
1572
|
const followWorld = useMemo(() => {
|
|
1195
1573
|
if (followTarget) return drawableProjector.toWorld(followTarget);
|
|
1196
1574
|
return cameraTarget;
|
|
@@ -1273,9 +1651,19 @@ var Canvas3DHost = forwardRef(
|
|
|
1273
1651
|
shadowNormalBias: 0.04,
|
|
1274
1652
|
shadowCameraSize: 5,
|
|
1275
1653
|
shadowCameraNear: 0.5,
|
|
1276
|
-
shadowCameraFar: 500
|
|
1654
|
+
shadowCameraFar: 500,
|
|
1655
|
+
ambientIntensity: lighting?.ambient?.intensity,
|
|
1656
|
+
ambientColor: lighting?.ambient?.color,
|
|
1657
|
+
directionalIntensity: lighting?.directional?.intensity,
|
|
1658
|
+
directionalColor: lighting?.directional?.color,
|
|
1659
|
+
directionalPosition: lighting?.directional?.position,
|
|
1660
|
+
hemisphereIntensity: lighting?.hemisphere?.intensity,
|
|
1661
|
+
hemisphereColor: lighting?.hemisphere?.color,
|
|
1662
|
+
hemisphereGroundColor: lighting?.hemisphere?.groundColor,
|
|
1663
|
+
points: lighting?.points
|
|
1277
1664
|
}
|
|
1278
1665
|
),
|
|
1666
|
+
lighting?.environment === "room" && /* @__PURE__ */ jsx(RoomEnvironment3D, {}),
|
|
1279
1667
|
showGrid && /* @__PURE__ */ jsx(
|
|
1280
1668
|
Grid,
|
|
1281
1669
|
{
|
|
@@ -1298,7 +1686,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1298
1686
|
fadeStrength: 1
|
|
1299
1687
|
}
|
|
1300
1688
|
),
|
|
1301
|
-
|
|
1689
|
+
allDrawables.length > 0 && /* @__PURE__ */ jsx("group", { children: allDrawables.map((node, i) => /* @__PURE__ */ jsx(Drawable3D, { node, projector: drawableProjector }, i)) }),
|
|
1302
1690
|
(tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxs(
|
|
1303
1691
|
"mesh",
|
|
1304
1692
|
{
|
|
@@ -1323,7 +1711,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1323
1711
|
]
|
|
1324
1712
|
}
|
|
1325
1713
|
),
|
|
1326
|
-
|
|
1714
|
+
post && (post.bloom || post.vignette) ? /* @__PURE__ */ jsx(Effects3D, { post }) : null,
|
|
1327
1715
|
/* @__PURE__ */ jsx(
|
|
1328
1716
|
OrbitControls,
|
|
1329
1717
|
{
|
|
@@ -1334,7 +1722,7 @@ var Canvas3DHost = forwardRef(
|
|
|
1334
1722
|
dampingFactor: 0.05,
|
|
1335
1723
|
enableZoom: true,
|
|
1336
1724
|
enablePan: true,
|
|
1337
|
-
touches: { ONE:
|
|
1725
|
+
touches: { ONE: THREE10.TOUCH.ROTATE, TWO: THREE10.TOUCH.DOLLY_PAN },
|
|
1338
1726
|
minDistance: 2,
|
|
1339
1727
|
maxDistance: 100,
|
|
1340
1728
|
maxPolarAngle: Math.PI / 2 - 0.1
|
|
@@ -1357,15 +1745,15 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
|
1357
1745
|
if (initializedRef.current) return;
|
|
1358
1746
|
initializedRef.current = true;
|
|
1359
1747
|
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
1360
|
-
scene.background = new
|
|
1748
|
+
scene.background = new THREE10.Color(background);
|
|
1361
1749
|
} else {
|
|
1362
|
-
const loader = new
|
|
1750
|
+
const loader = new THREE10.TextureLoader();
|
|
1363
1751
|
loader.load(background, (texture) => {
|
|
1364
1752
|
scene.background = texture;
|
|
1365
1753
|
});
|
|
1366
1754
|
}
|
|
1367
1755
|
if (fog) {
|
|
1368
|
-
scene.fog = new
|
|
1756
|
+
scene.fog = new THREE10.Fog(fog.color, fog.near, fog.far);
|
|
1369
1757
|
}
|
|
1370
1758
|
return () => {
|
|
1371
1759
|
scene.background = null;
|
|
@@ -1388,14 +1776,14 @@ var Camera3D = forwardRef(
|
|
|
1388
1776
|
}, ref) => {
|
|
1389
1777
|
const { camera, set, viewport } = useThree();
|
|
1390
1778
|
const controlsRef = useRef(null);
|
|
1391
|
-
const initialPosition = useRef(new
|
|
1392
|
-
const initialTarget = useRef(new
|
|
1779
|
+
const initialPosition = useRef(new THREE10.Vector3(...position));
|
|
1780
|
+
const initialTarget = useRef(new THREE10.Vector3(...target));
|
|
1393
1781
|
useEffect(() => {
|
|
1394
1782
|
let newCamera;
|
|
1395
1783
|
if (mode === "isometric") {
|
|
1396
1784
|
const aspect = viewport.aspect;
|
|
1397
1785
|
const size = 10 / zoom;
|
|
1398
|
-
newCamera = new
|
|
1786
|
+
newCamera = new THREE10.OrthographicCamera(
|
|
1399
1787
|
-size * aspect,
|
|
1400
1788
|
size * aspect,
|
|
1401
1789
|
size,
|
|
@@ -1404,7 +1792,7 @@ var Camera3D = forwardRef(
|
|
|
1404
1792
|
1e3
|
|
1405
1793
|
);
|
|
1406
1794
|
} else {
|
|
1407
|
-
newCamera = new
|
|
1795
|
+
newCamera = new THREE10.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
|
|
1408
1796
|
}
|
|
1409
1797
|
newCamera.position.copy(initialPosition.current);
|
|
1410
1798
|
newCamera.lookAt(initialTarget.current.x, initialTarget.current.y, initialTarget.current.z);
|
|
@@ -1445,8 +1833,8 @@ var Camera3D = forwardRef(
|
|
|
1445
1833
|
}
|
|
1446
1834
|
},
|
|
1447
1835
|
getViewBounds: () => {
|
|
1448
|
-
const min = new
|
|
1449
|
-
const max = new
|
|
1836
|
+
const min = new THREE10.Vector3(-10, -10, -10);
|
|
1837
|
+
const max = new THREE10.Vector3(10, 10, 10);
|
|
1450
1838
|
return { min, max };
|
|
1451
1839
|
}
|
|
1452
1840
|
}));
|
|
@@ -1488,7 +1876,7 @@ var AssetLoader = class {
|
|
|
1488
1876
|
__publicField(this, "textureCache");
|
|
1489
1877
|
__publicField(this, "loadingPromises");
|
|
1490
1878
|
this.objLoader = new OBJLoader();
|
|
1491
|
-
this.textureLoader = new
|
|
1879
|
+
this.textureLoader = new THREE10.TextureLoader();
|
|
1492
1880
|
this.modelCache = /* @__PURE__ */ new Map();
|
|
1493
1881
|
this.textureCache = /* @__PURE__ */ new Map();
|
|
1494
1882
|
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
@@ -1562,7 +1950,7 @@ var AssetLoader = class {
|
|
|
1562
1950
|
return this.loadingPromises.get(`texture:${url}`);
|
|
1563
1951
|
}
|
|
1564
1952
|
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
1565
|
-
texture.colorSpace =
|
|
1953
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
1566
1954
|
this.textureCache.set(url, texture);
|
|
1567
1955
|
this.loadingPromises.delete(`texture:${url}`);
|
|
1568
1956
|
return texture;
|
|
@@ -1636,7 +2024,7 @@ var AssetLoader = class {
|
|
|
1636
2024
|
});
|
|
1637
2025
|
this.modelCache.forEach((model) => {
|
|
1638
2026
|
model.scene.traverse((child) => {
|
|
1639
|
-
if (child instanceof
|
|
2027
|
+
if (child instanceof THREE10.Mesh) {
|
|
1640
2028
|
child.geometry.dispose();
|
|
1641
2029
|
if (Array.isArray(child.material)) {
|
|
1642
2030
|
child.material.forEach((m) => m.dispose());
|
|
@@ -1673,7 +2061,7 @@ var DEFAULT_OPTIONS = {
|
|
|
1673
2061
|
gridSize: 20,
|
|
1674
2062
|
assetLoader: new AssetLoader()
|
|
1675
2063
|
};
|
|
1676
|
-
function
|
|
2064
|
+
function useThree5(options = {}) {
|
|
1677
2065
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1678
2066
|
const containerRef = useRef(null);
|
|
1679
2067
|
const canvasRef = useRef(null);
|
|
@@ -1686,21 +2074,21 @@ function useThree4(options = {}) {
|
|
|
1686
2074
|
const [isReady, setIsReady] = useState(false);
|
|
1687
2075
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
1688
2076
|
const initialCameraPosition = useMemo(
|
|
1689
|
-
() => new
|
|
2077
|
+
() => new THREE10.Vector3(...opts.cameraPosition),
|
|
1690
2078
|
[]
|
|
1691
2079
|
);
|
|
1692
2080
|
useEffect(() => {
|
|
1693
2081
|
if (!containerRef.current) return;
|
|
1694
2082
|
const container = containerRef.current;
|
|
1695
2083
|
const { clientWidth, clientHeight } = container;
|
|
1696
|
-
const scene = new
|
|
1697
|
-
scene.background = new
|
|
2084
|
+
const scene = new THREE10.Scene();
|
|
2085
|
+
scene.background = new THREE10.Color(opts.backgroundColor);
|
|
1698
2086
|
sceneRef.current = scene;
|
|
1699
2087
|
let camera;
|
|
1700
2088
|
const aspect = clientWidth / clientHeight;
|
|
1701
2089
|
if (opts.cameraMode === "isometric") {
|
|
1702
2090
|
const size = 10;
|
|
1703
|
-
camera = new
|
|
2091
|
+
camera = new THREE10.OrthographicCamera(
|
|
1704
2092
|
-size * aspect,
|
|
1705
2093
|
size * aspect,
|
|
1706
2094
|
size,
|
|
@@ -1709,11 +2097,11 @@ function useThree4(options = {}) {
|
|
|
1709
2097
|
1e3
|
|
1710
2098
|
);
|
|
1711
2099
|
} else {
|
|
1712
|
-
camera = new
|
|
2100
|
+
camera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
1713
2101
|
}
|
|
1714
2102
|
camera.position.copy(initialCameraPosition);
|
|
1715
2103
|
cameraRef.current = camera;
|
|
1716
|
-
const renderer = new
|
|
2104
|
+
const renderer = new THREE10.WebGLRenderer({
|
|
1717
2105
|
antialias: true,
|
|
1718
2106
|
alpha: true,
|
|
1719
2107
|
canvas: canvasRef.current || void 0
|
|
@@ -1721,7 +2109,7 @@ function useThree4(options = {}) {
|
|
|
1721
2109
|
renderer.setSize(clientWidth, clientHeight);
|
|
1722
2110
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
1723
2111
|
renderer.shadowMap.enabled = opts.shadows;
|
|
1724
|
-
renderer.shadowMap.type =
|
|
2112
|
+
renderer.shadowMap.type = THREE10.PCFSoftShadowMap;
|
|
1725
2113
|
rendererRef.current = renderer;
|
|
1726
2114
|
const controls = new OrbitControls$1(camera, renderer.domElement);
|
|
1727
2115
|
controls.enableDamping = true;
|
|
@@ -1730,16 +2118,16 @@ function useThree4(options = {}) {
|
|
|
1730
2118
|
controls.maxDistance = 100;
|
|
1731
2119
|
controls.maxPolarAngle = Math.PI / 2 - 0.1;
|
|
1732
2120
|
controlsRef.current = controls;
|
|
1733
|
-
const ambientLight = new
|
|
2121
|
+
const ambientLight = new THREE10.AmbientLight(16777215, 0.6);
|
|
1734
2122
|
scene.add(ambientLight);
|
|
1735
|
-
const directionalLight = new
|
|
2123
|
+
const directionalLight = new THREE10.DirectionalLight(16777215, 0.8);
|
|
1736
2124
|
directionalLight.position.set(10, 20, 10);
|
|
1737
2125
|
directionalLight.castShadow = opts.shadows;
|
|
1738
2126
|
directionalLight.shadow.mapSize.width = 2048;
|
|
1739
2127
|
directionalLight.shadow.mapSize.height = 2048;
|
|
1740
2128
|
scene.add(directionalLight);
|
|
1741
2129
|
if (opts.showGrid) {
|
|
1742
|
-
const gridHelper = new
|
|
2130
|
+
const gridHelper = new THREE10.GridHelper(
|
|
1743
2131
|
opts.gridSize,
|
|
1744
2132
|
opts.gridSize,
|
|
1745
2133
|
4473924,
|
|
@@ -1757,10 +2145,10 @@ function useThree4(options = {}) {
|
|
|
1757
2145
|
const handleResize = () => {
|
|
1758
2146
|
const { clientWidth: width, clientHeight: height } = container;
|
|
1759
2147
|
setDimensions({ width, height });
|
|
1760
|
-
if (camera instanceof
|
|
2148
|
+
if (camera instanceof THREE10.PerspectiveCamera) {
|
|
1761
2149
|
camera.aspect = width / height;
|
|
1762
2150
|
camera.updateProjectionMatrix();
|
|
1763
|
-
} else if (camera instanceof
|
|
2151
|
+
} else if (camera instanceof THREE10.OrthographicCamera) {
|
|
1764
2152
|
const aspect2 = width / height;
|
|
1765
2153
|
const size = 10;
|
|
1766
2154
|
camera.left = -size * aspect2;
|
|
@@ -1791,7 +2179,7 @@ function useThree4(options = {}) {
|
|
|
1791
2179
|
let newCamera;
|
|
1792
2180
|
if (opts.cameraMode === "isometric") {
|
|
1793
2181
|
const size = 10;
|
|
1794
|
-
newCamera = new
|
|
2182
|
+
newCamera = new THREE10.OrthographicCamera(
|
|
1795
2183
|
-size * aspect,
|
|
1796
2184
|
size * aspect,
|
|
1797
2185
|
size,
|
|
@@ -1800,7 +2188,7 @@ function useThree4(options = {}) {
|
|
|
1800
2188
|
1e3
|
|
1801
2189
|
);
|
|
1802
2190
|
} else {
|
|
1803
|
-
newCamera = new
|
|
2191
|
+
newCamera = new THREE10.PerspectiveCamera(45, aspect, 0.1, 1e3);
|
|
1804
2192
|
}
|
|
1805
2193
|
newCamera.position.copy(currentPos);
|
|
1806
2194
|
cameraRef.current = newCamera;
|
|
@@ -2130,8 +2518,8 @@ function useSceneGraph() {
|
|
|
2130
2518
|
}
|
|
2131
2519
|
function useRaycaster(options) {
|
|
2132
2520
|
const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
|
|
2133
|
-
const raycaster = useRef(new
|
|
2134
|
-
const mouse = useRef(new
|
|
2521
|
+
const raycaster = useRef(new THREE10.Raycaster());
|
|
2522
|
+
const mouse = useRef(new THREE10.Vector2());
|
|
2135
2523
|
const clientToNDC = useCallback(
|
|
2136
2524
|
(clientX, clientY) => {
|
|
2137
2525
|
if (!canvas) {
|
|
@@ -2201,8 +2589,8 @@ function useRaycaster(options) {
|
|
|
2201
2589
|
const ndc = clientToNDC(clientX, clientY);
|
|
2202
2590
|
mouse.current.set(ndc.x, ndc.y);
|
|
2203
2591
|
raycaster.current.setFromCamera(mouse.current, camera);
|
|
2204
|
-
const plane = new
|
|
2205
|
-
const target = new
|
|
2592
|
+
const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), 0);
|
|
2593
|
+
const target = new THREE10.Vector3();
|
|
2206
2594
|
const intersection = raycaster.current.ray.intersectPlane(plane, target);
|
|
2207
2595
|
if (intersection) {
|
|
2208
2596
|
const gridX = Math.round((target.x - offsetX) / cellSize);
|
|
@@ -2240,7 +2628,7 @@ function useRaycaster(options) {
|
|
|
2240
2628
|
return {
|
|
2241
2629
|
gridX: gridCoords.x,
|
|
2242
2630
|
gridZ: gridCoords.z,
|
|
2243
|
-
worldPosition: new
|
|
2631
|
+
worldPosition: new THREE10.Vector3(
|
|
2244
2632
|
gridCoords.x * cellSize + offsetX,
|
|
2245
2633
|
0,
|
|
2246
2634
|
gridCoords.z * cellSize + offsetZ
|
|
@@ -2270,7 +2658,7 @@ var DEFAULT_CONFIG = {
|
|
|
2270
2658
|
};
|
|
2271
2659
|
function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
|
|
2272
2660
|
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
2273
|
-
return new
|
|
2661
|
+
return new THREE10.Vector3(
|
|
2274
2662
|
gridX * opts.cellSize + opts.offsetX,
|
|
2275
2663
|
opts.elevation,
|
|
2276
2664
|
gridZ * opts.cellSize + opts.offsetZ
|
|
@@ -2284,17 +2672,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
|
|
|
2284
2672
|
};
|
|
2285
2673
|
}
|
|
2286
2674
|
function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
|
|
2287
|
-
const raycaster = new
|
|
2288
|
-
const mouse = new
|
|
2675
|
+
const raycaster = new THREE10.Raycaster();
|
|
2676
|
+
const mouse = new THREE10.Vector2(mouseX, mouseY);
|
|
2289
2677
|
raycaster.setFromCamera(mouse, camera);
|
|
2290
|
-
const plane = new
|
|
2291
|
-
const target = new
|
|
2678
|
+
const plane = new THREE10.Plane(new THREE10.Vector3(0, 1, 0), -planeY);
|
|
2679
|
+
const target = new THREE10.Vector3();
|
|
2292
2680
|
const intersection = raycaster.ray.intersectPlane(plane, target);
|
|
2293
2681
|
return intersection ? target : null;
|
|
2294
2682
|
}
|
|
2295
2683
|
function raycastToObjects(camera, mouseX, mouseY, objects) {
|
|
2296
|
-
const raycaster = new
|
|
2297
|
-
const mouse = new
|
|
2684
|
+
const raycaster = new THREE10.Raycaster();
|
|
2685
|
+
const mouse = new THREE10.Vector2(mouseX, mouseY);
|
|
2298
2686
|
raycaster.setFromCamera(mouse, camera);
|
|
2299
2687
|
const intersects = raycaster.intersectObjects(objects, true);
|
|
2300
2688
|
return intersects.length > 0 ? intersects[0] : null;
|
|
@@ -2346,14 +2734,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
|
|
|
2346
2734
|
return cells;
|
|
2347
2735
|
}
|
|
2348
2736
|
function createGridHighlight(color = 16776960, opacity = 0.3) {
|
|
2349
|
-
const geometry = new
|
|
2350
|
-
const material = new
|
|
2737
|
+
const geometry = new THREE10.PlaneGeometry(0.95, 0.95);
|
|
2738
|
+
const material = new THREE10.MeshBasicMaterial({
|
|
2351
2739
|
color,
|
|
2352
2740
|
transparent: true,
|
|
2353
2741
|
opacity,
|
|
2354
|
-
side:
|
|
2742
|
+
side: THREE10.DoubleSide
|
|
2355
2743
|
});
|
|
2356
|
-
const mesh = new
|
|
2744
|
+
const mesh = new THREE10.Mesh(geometry, material);
|
|
2357
2745
|
mesh.rotation.x = -Math.PI / 2;
|
|
2358
2746
|
mesh.position.y = 0.01;
|
|
2359
2747
|
return mesh;
|
|
@@ -2366,31 +2754,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
|
|
|
2366
2754
|
};
|
|
2367
2755
|
}
|
|
2368
2756
|
function isInFrustum(position, camera, padding = 0) {
|
|
2369
|
-
const frustum = new
|
|
2370
|
-
const projScreenMatrix = new
|
|
2757
|
+
const frustum = new THREE10.Frustum();
|
|
2758
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2371
2759
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2372
2760
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2373
|
-
const sphere = new
|
|
2761
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2374
2762
|
return frustum.intersectsSphere(sphere);
|
|
2375
2763
|
}
|
|
2376
2764
|
function filterByFrustum(positions, camera, padding = 0) {
|
|
2377
|
-
const frustum = new
|
|
2378
|
-
const projScreenMatrix = new
|
|
2765
|
+
const frustum = new THREE10.Frustum();
|
|
2766
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2379
2767
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2380
2768
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2381
2769
|
return positions.filter((position) => {
|
|
2382
|
-
const sphere = new
|
|
2770
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2383
2771
|
return frustum.intersectsSphere(sphere);
|
|
2384
2772
|
});
|
|
2385
2773
|
}
|
|
2386
2774
|
function getVisibleIndices(positions, camera, padding = 0) {
|
|
2387
|
-
const frustum = new
|
|
2388
|
-
const projScreenMatrix = new
|
|
2775
|
+
const frustum = new THREE10.Frustum();
|
|
2776
|
+
const projScreenMatrix = new THREE10.Matrix4();
|
|
2389
2777
|
const visible = /* @__PURE__ */ new Set();
|
|
2390
2778
|
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
2391
2779
|
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
2392
2780
|
positions.forEach((position, index) => {
|
|
2393
|
-
const sphere = new
|
|
2781
|
+
const sphere = new THREE10.Sphere(position, padding);
|
|
2394
2782
|
if (frustum.intersectsSphere(sphere)) {
|
|
2395
2783
|
visible.add(index);
|
|
2396
2784
|
}
|
|
@@ -2414,7 +2802,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
|
|
|
2414
2802
|
return lodIndices;
|
|
2415
2803
|
}
|
|
2416
2804
|
function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
|
|
2417
|
-
const dummy = new
|
|
2805
|
+
const dummy = new THREE10.Object3D();
|
|
2418
2806
|
let visibleCount = 0;
|
|
2419
2807
|
positions.forEach((position, index) => {
|
|
2420
2808
|
if (visibleIndices.has(index)) {
|
|
@@ -5197,12 +5585,12 @@ function useAvl3DConfig() {
|
|
|
5197
5585
|
}
|
|
5198
5586
|
function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
5199
5587
|
const { camera } = useThree();
|
|
5200
|
-
const targetPosVec = useRef(new
|
|
5201
|
-
const targetLookVec = useRef(new
|
|
5588
|
+
const targetPosVec = useRef(new THREE10.Vector3(...targetPosition));
|
|
5589
|
+
const targetLookVec = useRef(new THREE10.Vector3(...targetLookAt));
|
|
5202
5590
|
const isAnimating = useRef(false);
|
|
5203
5591
|
useEffect(() => {
|
|
5204
|
-
const newTarget = new
|
|
5205
|
-
const newLookAt = new
|
|
5592
|
+
const newTarget = new THREE10.Vector3(...targetPosition);
|
|
5593
|
+
const newLookAt = new THREE10.Vector3(...targetLookAt);
|
|
5206
5594
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
5207
5595
|
targetPosVec.current.copy(newTarget);
|
|
5208
5596
|
targetLookVec.current.copy(newLookAt);
|
|
@@ -5217,9 +5605,9 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
|
5217
5605
|
useFrame((_, delta) => {
|
|
5218
5606
|
if (!isAnimating.current) return;
|
|
5219
5607
|
const speed = 3;
|
|
5220
|
-
camera.position.x =
|
|
5221
|
-
camera.position.y =
|
|
5222
|
-
camera.position.z =
|
|
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);
|
|
5223
5611
|
camera.lookAt(targetLookVec.current);
|
|
5224
5612
|
const dist = camera.position.distanceTo(targetPosVec.current);
|
|
5225
5613
|
if (dist < 0.05) {
|
|
@@ -5234,7 +5622,7 @@ function SceneFade({ animating, children }) {
|
|
|
5234
5622
|
useFrame((_, delta) => {
|
|
5235
5623
|
if (!groupRef.current) return;
|
|
5236
5624
|
const target = animating ? 0 : 1;
|
|
5237
|
-
opacityRef.current =
|
|
5625
|
+
opacityRef.current = THREE10.MathUtils.damp(opacityRef.current, target, 5, delta);
|
|
5238
5626
|
groupRef.current.visible = opacityRef.current > 0.05;
|
|
5239
5627
|
const s = 0.9 + opacityRef.current * 0.1;
|
|
5240
5628
|
groupRef.current.scale.setScalar(s);
|
|
@@ -5487,4 +5875,4 @@ var Avl3DViewer = ({
|
|
|
5487
5875
|
};
|
|
5488
5876
|
Avl3DViewer.displayName = "Avl3DViewer";
|
|
5489
5877
|
|
|
5490
|
-
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,
|
|
5878
|
+
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 };
|