@almadar/ui 2.15.8 → 2.15.10

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.
Files changed (125) hide show
  1. package/dist/components/atoms/ContentSection.d.ts +16 -0
  2. package/dist/components/atoms/SectionHeader.d.ts +14 -0
  3. package/dist/components/atoms/StatCard.d.ts +13 -0
  4. package/dist/components/atoms/index.d.ts +3 -0
  5. package/dist/components/atoms/svg/SvgBranch.d.ts +12 -0
  6. package/dist/components/atoms/svg/SvgConnection.d.ts +13 -0
  7. package/dist/components/atoms/svg/SvgFlow.d.ts +10 -0
  8. package/dist/components/atoms/svg/SvgGrid.d.ts +14 -0
  9. package/dist/components/atoms/svg/SvgLobe.d.ts +13 -0
  10. package/dist/components/atoms/svg/SvgMesh.d.ts +12 -0
  11. package/dist/components/atoms/svg/SvgMorph.d.ts +11 -0
  12. package/dist/components/atoms/svg/SvgNode.d.ts +12 -0
  13. package/dist/components/atoms/svg/SvgPulse.d.ts +12 -0
  14. package/dist/components/atoms/svg/SvgRing.d.ts +13 -0
  15. package/dist/components/atoms/svg/SvgShield.d.ts +11 -0
  16. package/dist/components/atoms/svg/SvgStack.d.ts +13 -0
  17. package/dist/components/atoms/svg/index.d.ts +12 -0
  18. package/dist/{chunk-ACUO2BBW.js → components/index.cjs} +24764 -16677
  19. package/dist/components/index.js +37757 -889
  20. package/dist/components/molecules/AnimatedCounter.d.ts +18 -0
  21. package/dist/components/molecules/ArticleSection.d.ts +18 -0
  22. package/dist/components/molecules/CTABanner.d.ts +31 -0
  23. package/dist/components/molecules/CaseStudyCard.d.ts +24 -0
  24. package/dist/components/molecules/CodeExample.d.ts +23 -0
  25. package/dist/components/molecules/CommunityLinks.d.ts +25 -0
  26. package/dist/components/molecules/DocBreadcrumb.d.ts +20 -0
  27. package/dist/components/molecules/DocCodeBlock.d.ts +13 -0
  28. package/dist/components/molecules/DocPagination.d.ts +14 -0
  29. package/dist/components/molecules/DocSearch.d.ts +15 -0
  30. package/dist/components/molecules/DocSidebar.d.ts +24 -0
  31. package/dist/components/molecules/DocTOC.d.ts +24 -0
  32. package/dist/components/molecules/FeatureCard.d.ts +26 -0
  33. package/dist/components/molecules/FeatureGrid.d.ts +19 -0
  34. package/dist/components/molecules/GradientDivider.d.ts +14 -0
  35. package/dist/components/molecules/HeroSection.d.ts +36 -0
  36. package/dist/components/molecules/InstallBox.d.ts +16 -0
  37. package/dist/components/molecules/MarketingFooter.d.ts +27 -0
  38. package/dist/components/molecules/PricingCard.d.ts +21 -0
  39. package/dist/components/molecules/PricingGrid.d.ts +13 -0
  40. package/dist/components/molecules/PullQuote.d.ts +14 -0
  41. package/dist/components/molecules/ServiceCatalog.d.ts +19 -0
  42. package/dist/components/molecules/ShowcaseCard.d.ts +20 -0
  43. package/dist/components/molecules/SocialProof.d.ts +25 -0
  44. package/dist/components/molecules/SplitSection.d.ts +21 -0
  45. package/dist/components/molecules/StatsGrid.d.ts +17 -0
  46. package/dist/components/molecules/StepFlow.d.ts +20 -0
  47. package/dist/components/molecules/TagCloud.d.ts +18 -0
  48. package/dist/components/molecules/TeamCard.d.ts +18 -0
  49. package/dist/components/molecules/index.d.ts +19 -0
  50. package/dist/components/molecules/svg/AIGenerates.d.ts +7 -0
  51. package/dist/components/molecules/svg/ClosedCircuit.d.ts +7 -0
  52. package/dist/components/molecules/svg/CommunityOwnership.d.ts +7 -0
  53. package/dist/components/molecules/svg/CompileAnywhere.d.ts +7 -0
  54. package/dist/components/molecules/svg/ComposableModels.d.ts +7 -0
  55. package/dist/components/molecules/svg/DescribeProveDeploy.d.ts +7 -0
  56. package/dist/components/molecules/svg/DomainGrid.d.ts +7 -0
  57. package/dist/components/molecules/svg/EventBus.d.ts +7 -0
  58. package/dist/components/molecules/svg/OrbitalUnit.d.ts +7 -0
  59. package/dist/components/molecules/svg/PlanVerifyRemember.d.ts +7 -0
  60. package/dist/components/molecules/svg/ProveCorrect.d.ts +7 -0
  61. package/dist/components/molecules/svg/ServiceLayers.d.ts +7 -0
  62. package/dist/components/molecules/svg/SharedReality.d.ts +7 -0
  63. package/dist/components/molecules/svg/StandardLibrary.d.ts +7 -0
  64. package/dist/components/molecules/svg/StateMachine.d.ts +7 -0
  65. package/dist/components/molecules/svg/WorldModel.d.ts +7 -0
  66. package/dist/components/molecules/svg/index.d.ts +16 -0
  67. package/dist/components/organisms/CaseStudyOrganism.d.ts +19 -0
  68. package/dist/components/organisms/FeatureGridOrganism.d.ts +20 -0
  69. package/dist/components/organisms/HeroOrganism.d.ts +18 -0
  70. package/dist/components/organisms/PricingOrganism.d.ts +19 -0
  71. package/dist/components/organisms/ShowcaseOrganism.d.ts +20 -0
  72. package/dist/components/organisms/StatsOrganism.d.ts +17 -0
  73. package/dist/components/organisms/StepFlowOrganism.d.ts +20 -0
  74. package/dist/components/organisms/TeamOrganism.d.ts +18 -0
  75. package/dist/components/organisms/game/three/index.cjs +2525 -0
  76. package/dist/components/organisms/game/three/index.js +1795 -50
  77. package/dist/components/organisms/index.d.ts +9 -0
  78. package/dist/components/organisms/marketing-types.d.ts +87 -0
  79. package/dist/components/templates/AboutPageTemplate.d.ts +26 -0
  80. package/dist/components/templates/FeatureDetailPageTemplate.d.ts +27 -0
  81. package/dist/components/templates/LandingPageTemplate.d.ts +31 -0
  82. package/dist/components/templates/PricingPageTemplate.d.ts +26 -0
  83. package/dist/components/templates/index.d.ts +4 -0
  84. package/dist/context/index.cjs +550 -0
  85. package/dist/context/index.js +420 -6
  86. package/dist/docs/index.cjs +4015 -0
  87. package/dist/docs/index.d.cts +412 -0
  88. package/dist/docs/index.d.ts +29 -0
  89. package/dist/docs/index.js +3977 -0
  90. package/dist/hooks/index.cjs +2606 -0
  91. package/dist/hooks/index.js +2535 -8
  92. package/dist/illustrations/index.cjs +3004 -0
  93. package/dist/illustrations/index.d.cts +261 -0
  94. package/dist/illustrations/index.d.ts +35 -0
  95. package/dist/illustrations/index.js +2971 -0
  96. package/dist/{chunk-XL7WB2O5.js → lib/index.cjs} +454 -274
  97. package/dist/lib/index.js +1407 -3
  98. package/dist/locales/index.cjs +340 -0
  99. package/dist/locales/index.js +105 -2
  100. package/dist/marketing/index.cjs +4683 -0
  101. package/dist/marketing/index.d.cts +831 -0
  102. package/dist/marketing/index.d.ts +62 -0
  103. package/dist/marketing/index.js +4626 -0
  104. package/dist/providers/index.cjs +4811 -0
  105. package/dist/providers/index.js +4765 -11
  106. package/dist/{chunk-K2D5D3WK.js → renderer/index.cjs} +101 -42
  107. package/dist/renderer/index.js +1036 -2
  108. package/dist/runtime/index.cjs +4400 -0
  109. package/dist/runtime/index.js +3615 -19
  110. package/dist/{chunk-N7MVUW4R.js → stores/index.cjs} +24 -1
  111. package/dist/stores/index.js +194 -2
  112. package/dist/tsup.config.d.ts +2 -1
  113. package/package.json +27 -12
  114. package/tailwind-preset.cjs +9 -0
  115. package/themes/index.css +22 -20
  116. package/dist/chunk-3HJHHULT.js +0 -93
  117. package/dist/chunk-3JGAROCW.js +0 -149
  118. package/dist/chunk-4N3BAPDB.js +0 -1667
  119. package/dist/chunk-CDIOHSKG.js +0 -661
  120. package/dist/chunk-DKQN5FVU.js +0 -279
  121. package/dist/chunk-JJHCOO34.js +0 -375
  122. package/dist/chunk-PKBMQBKP.js +0 -5
  123. package/dist/chunk-QIABKRCN.js +0 -107
  124. package/dist/chunk-SD3KVCY6.js +0 -1465
  125. package/dist/chunk-YXZM3WCF.js +0 -222
@@ -1,11 +1,753 @@
1
- import { AssetLoader } from '../../../../chunk-4N3BAPDB.js';
2
- export { AssetLoader, Camera3D, Canvas3DErrorBoundary, Canvas3DLoadingState, FeatureRenderer, FeatureRenderer3D, Lighting3D, ModelLoader, PhysicsObject3D, Scene3D, TileRenderer, UnitRenderer, assetLoader, preloadFeatures, useAssetLoader, useGameCanvas3DEvents, usePhysics3DController } from '../../../../chunk-4N3BAPDB.js';
3
- import '../../../../chunk-YXZM3WCF.js';
4
- import { __publicField } from '../../../../chunk-PKBMQBKP.js';
5
- import { useRef, useState, useMemo, useEffect, useCallback } from 'react';
6
- import * as THREE from 'three';
7
- import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
1
+ import React8, { forwardRef, useRef, useEffect, useImperativeHandle, createContext, Component, useMemo, useState, useCallback, useContext } from 'react';
2
+ import { useThree, useFrame } from '@react-three/fiber';
3
+ import * as THREE6 from 'three';
4
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
+ import { OrbitControls } from '@react-three/drei';
6
+ import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
7
+ import { OrbitControls as OrbitControls$1 } from 'three/examples/jsm/controls/OrbitControls.js';
8
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
9
+ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
8
10
 
11
+ var __defProp = Object.defineProperty;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
14
+ function Scene3D({ background = "#1a1a2e", fog, children }) {
15
+ const { scene } = useThree();
16
+ const initializedRef = useRef(false);
17
+ useEffect(() => {
18
+ if (initializedRef.current) return;
19
+ initializedRef.current = true;
20
+ if (background.startsWith("#") || background.startsWith("rgb")) {
21
+ scene.background = new THREE6.Color(background);
22
+ } else {
23
+ const loader = new THREE6.TextureLoader();
24
+ loader.load(background, (texture) => {
25
+ scene.background = texture;
26
+ });
27
+ }
28
+ if (fog) {
29
+ scene.fog = new THREE6.Fog(fog.color, fog.near, fog.far);
30
+ }
31
+ return () => {
32
+ scene.background = null;
33
+ scene.fog = null;
34
+ };
35
+ }, [scene, background, fog]);
36
+ return /* @__PURE__ */ jsx(Fragment, { children });
37
+ }
38
+ var Camera3D = forwardRef(
39
+ ({
40
+ mode = "isometric",
41
+ position = [10, 10, 10],
42
+ target = [0, 0, 0],
43
+ zoom = 1,
44
+ fov = 45,
45
+ enableOrbit = true,
46
+ minDistance = 2,
47
+ maxDistance = 100,
48
+ onChange
49
+ }, ref) => {
50
+ const { camera, set, viewport } = useThree();
51
+ const controlsRef = useRef(null);
52
+ const initialPosition = useRef(new THREE6.Vector3(...position));
53
+ const initialTarget = useRef(new THREE6.Vector3(...target));
54
+ useEffect(() => {
55
+ let newCamera;
56
+ if (mode === "isometric") {
57
+ const aspect = viewport.aspect;
58
+ const size = 10 / zoom;
59
+ newCamera = new THREE6.OrthographicCamera(
60
+ -size * aspect,
61
+ size * aspect,
62
+ size,
63
+ -size,
64
+ 0.1,
65
+ 1e3
66
+ );
67
+ } else {
68
+ newCamera = new THREE6.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
69
+ }
70
+ newCamera.position.copy(initialPosition.current);
71
+ newCamera.lookAt(initialTarget.current);
72
+ set({ camera: newCamera });
73
+ if (mode === "top-down") {
74
+ newCamera.position.set(0, 20 / zoom, 0);
75
+ newCamera.lookAt(0, 0, 0);
76
+ }
77
+ return () => {
78
+ };
79
+ }, [mode, fov, zoom, viewport.aspect, set]);
80
+ useFrame(() => {
81
+ if (onChange) {
82
+ onChange(camera);
83
+ }
84
+ });
85
+ useImperativeHandle(ref, () => ({
86
+ getCamera: () => camera,
87
+ setPosition: (x, y, z) => {
88
+ camera.position.set(x, y, z);
89
+ if (controlsRef.current) {
90
+ controlsRef.current.update();
91
+ }
92
+ },
93
+ lookAt: (x, y, z) => {
94
+ camera.lookAt(x, y, z);
95
+ if (controlsRef.current) {
96
+ controlsRef.current.target.set(x, y, z);
97
+ controlsRef.current.update();
98
+ }
99
+ },
100
+ reset: () => {
101
+ camera.position.copy(initialPosition.current);
102
+ camera.lookAt(initialTarget.current);
103
+ if (controlsRef.current) {
104
+ controlsRef.current.target.copy(initialTarget.current);
105
+ controlsRef.current.update();
106
+ }
107
+ },
108
+ getViewBounds: () => {
109
+ const min = new THREE6.Vector3(-10, -10, -10);
110
+ const max = new THREE6.Vector3(10, 10, 10);
111
+ return { min, max };
112
+ }
113
+ }));
114
+ const maxPolarAngle = mode === "top-down" ? 0.1 : Math.PI / 2 - 0.1;
115
+ return /* @__PURE__ */ jsx(
116
+ OrbitControls,
117
+ {
118
+ ref: controlsRef,
119
+ camera,
120
+ enabled: enableOrbit,
121
+ target: initialTarget.current,
122
+ minDistance,
123
+ maxDistance,
124
+ maxPolarAngle,
125
+ enableDamping: true,
126
+ dampingFactor: 0.05
127
+ }
128
+ );
129
+ }
130
+ );
131
+ Camera3D.displayName = "Camera3D";
132
+ function Lighting3D({
133
+ ambientIntensity = 0.6,
134
+ ambientColor = "#ffffff",
135
+ directionalIntensity = 0.8,
136
+ directionalColor = "#ffffff",
137
+ directionalPosition = [10, 20, 10],
138
+ shadows = true,
139
+ shadowMapSize = 2048,
140
+ shadowCameraSize = 20,
141
+ showHelpers = false
142
+ }) {
143
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
144
+ /* @__PURE__ */ jsx("ambientLight", { intensity: ambientIntensity, color: ambientColor }),
145
+ /* @__PURE__ */ jsx(
146
+ "directionalLight",
147
+ {
148
+ position: directionalPosition,
149
+ intensity: directionalIntensity,
150
+ color: directionalColor,
151
+ castShadow: shadows,
152
+ "shadow-mapSize": [shadowMapSize, shadowMapSize],
153
+ "shadow-camera-left": -shadowCameraSize,
154
+ "shadow-camera-right": shadowCameraSize,
155
+ "shadow-camera-top": shadowCameraSize,
156
+ "shadow-camera-bottom": -shadowCameraSize,
157
+ "shadow-camera-near": 0.1,
158
+ "shadow-camera-far": 100,
159
+ "shadow-bias": -1e-3
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsx(
163
+ "hemisphereLight",
164
+ {
165
+ intensity: 0.3,
166
+ color: "#87ceeb",
167
+ groundColor: "#362d1d"
168
+ }
169
+ ),
170
+ showHelpers && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
171
+ "directionalLightHelper",
172
+ {
173
+ args: [
174
+ new THREE6.DirectionalLight(directionalColor, directionalIntensity),
175
+ 5
176
+ ]
177
+ }
178
+ ) })
179
+ ] });
180
+ }
181
+ function Canvas3DLoadingState({
182
+ progress = 0,
183
+ loaded = 0,
184
+ total = 0,
185
+ message = "Loading 3D Scene...",
186
+ details,
187
+ showSpinner = true,
188
+ className
189
+ }) {
190
+ const clampedProgress = Math.max(0, Math.min(100, progress));
191
+ const hasProgress = total > 0;
192
+ return /* @__PURE__ */ jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
193
+ /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__content", children: [
194
+ showSpinner && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__spinner", children: [
195
+ /* @__PURE__ */ jsx("div", { className: "spinner__ring" }),
196
+ /* @__PURE__ */ jsx("div", { className: "spinner__ring spinner__ring--secondary" })
197
+ ] }),
198
+ /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__message", children: message }),
199
+ details && /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__details", children: details }),
200
+ hasProgress && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__progress", children: [
201
+ /* @__PURE__ */ jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsx(
202
+ "div",
203
+ {
204
+ className: "progress__fill",
205
+ style: { width: `${clampedProgress}%` }
206
+ }
207
+ ) }),
208
+ /* @__PURE__ */ jsxs("div", { className: "progress__text", children: [
209
+ /* @__PURE__ */ jsxs("span", { className: "progress__percentage", children: [
210
+ clampedProgress,
211
+ "%"
212
+ ] }),
213
+ /* @__PURE__ */ jsxs("span", { className: "progress__count", children: [
214
+ "(",
215
+ loaded,
216
+ "/",
217
+ total,
218
+ ")"
219
+ ] })
220
+ ] })
221
+ ] })
222
+ ] }),
223
+ /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsx("div", { className: "bg__grid" }) })
224
+ ] });
225
+ }
226
+ var Canvas3DErrorBoundary = class extends Component {
227
+ constructor(props) {
228
+ super(props);
229
+ __publicField(this, "handleReset", () => {
230
+ this.setState({
231
+ hasError: false,
232
+ error: null,
233
+ errorInfo: null
234
+ });
235
+ this.props.onReset?.();
236
+ });
237
+ this.state = {
238
+ hasError: false,
239
+ error: null,
240
+ errorInfo: null
241
+ };
242
+ }
243
+ static getDerivedStateFromError(error) {
244
+ return {
245
+ hasError: true,
246
+ error,
247
+ errorInfo: null
248
+ };
249
+ }
250
+ componentDidCatch(error, errorInfo) {
251
+ this.setState({ errorInfo });
252
+ this.props.onError?.(error, errorInfo);
253
+ console.error("[Canvas3DErrorBoundary] Error caught:", error);
254
+ console.error("[Canvas3DErrorBoundary] Component stack:", errorInfo.componentStack);
255
+ }
256
+ render() {
257
+ if (this.state.hasError) {
258
+ if (this.props.fallback) {
259
+ return this.props.fallback;
260
+ }
261
+ return /* @__PURE__ */ jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__content", children: [
262
+ /* @__PURE__ */ jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
263
+ /* @__PURE__ */ jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
264
+ /* @__PURE__ */ jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
265
+ this.state.error && /* @__PURE__ */ jsxs("details", { className: "canvas-3d-error__details", children: [
266
+ /* @__PURE__ */ jsx("summary", { children: "Error Details" }),
267
+ /* @__PURE__ */ jsxs("pre", { className: "error__stack", children: [
268
+ this.state.error.message,
269
+ "\n",
270
+ this.state.error.stack
271
+ ] }),
272
+ this.state.errorInfo && /* @__PURE__ */ jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
273
+ ] }),
274
+ /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__actions", children: [
275
+ /* @__PURE__ */ jsx(
276
+ "button",
277
+ {
278
+ className: "error__button error__button--primary",
279
+ onClick: this.handleReset,
280
+ children: "Try Again"
281
+ }
282
+ ),
283
+ /* @__PURE__ */ jsx(
284
+ "button",
285
+ {
286
+ className: "error__button error__button--secondary",
287
+ onClick: () => window.location.reload(),
288
+ children: "Reload Page"
289
+ }
290
+ )
291
+ ] })
292
+ ] }) });
293
+ }
294
+ return this.props.children;
295
+ }
296
+ };
297
+ function detectAssetRoot(modelUrl) {
298
+ const idx = modelUrl.indexOf("/3d/");
299
+ if (idx !== -1) {
300
+ return modelUrl.substring(0, idx + 4);
301
+ }
302
+ return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
303
+ }
304
+ function useGLTFModel(url, resourceBasePath) {
305
+ const [state, setState] = useState({
306
+ model: null,
307
+ isLoading: false,
308
+ error: null
309
+ });
310
+ useEffect(() => {
311
+ if (!url) {
312
+ setState({ model: null, isLoading: false, error: null });
313
+ return;
314
+ }
315
+ console.log("[ModelLoader] Loading:", url);
316
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
317
+ const assetRoot = resourceBasePath || detectAssetRoot(url);
318
+ const loader = new GLTFLoader$1();
319
+ loader.setResourcePath(assetRoot);
320
+ loader.load(
321
+ url,
322
+ (gltf) => {
323
+ console.log("[ModelLoader] Loaded:", url);
324
+ setState({
325
+ model: gltf.scene,
326
+ isLoading: false,
327
+ error: null
328
+ });
329
+ },
330
+ void 0,
331
+ (err) => {
332
+ const errorMsg = err instanceof Error ? err.message : String(err);
333
+ console.warn("[ModelLoader] Failed:", url, errorMsg);
334
+ setState({
335
+ model: null,
336
+ isLoading: false,
337
+ error: err instanceof Error ? err : new Error(String(err))
338
+ });
339
+ }
340
+ );
341
+ }, [url, resourceBasePath]);
342
+ return state;
343
+ }
344
+ function ModelLoader({
345
+ url,
346
+ position = [0, 0, 0],
347
+ scale = 1,
348
+ rotation = [0, 0, 0],
349
+ isSelected = false,
350
+ isHovered = false,
351
+ onClick,
352
+ onHover,
353
+ fallbackGeometry = "box",
354
+ castShadow = true,
355
+ receiveShadow = true,
356
+ resourceBasePath
357
+ }) {
358
+ const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
359
+ const model = useMemo(() => {
360
+ if (!loadedModel) return null;
361
+ const cloned = loadedModel.clone();
362
+ cloned.traverse((child) => {
363
+ if (child instanceof THREE6.Mesh) {
364
+ child.castShadow = castShadow;
365
+ child.receiveShadow = receiveShadow;
366
+ }
367
+ });
368
+ return cloned;
369
+ }, [loadedModel, castShadow, receiveShadow]);
370
+ const scaleArray = useMemo(() => {
371
+ if (typeof scale === "number") {
372
+ return [scale, scale, scale];
373
+ }
374
+ return scale;
375
+ }, [scale]);
376
+ const rotationRad = useMemo(() => {
377
+ return [
378
+ rotation[0] * Math.PI / 180,
379
+ rotation[1] * Math.PI / 180,
380
+ rotation[2] * Math.PI / 180
381
+ ];
382
+ }, [rotation]);
383
+ if (isLoading) {
384
+ return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
385
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
386
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
387
+ ] }) });
388
+ }
389
+ if (error || !model) {
390
+ if (fallbackGeometry === "none") {
391
+ return /* @__PURE__ */ jsx("group", { position });
392
+ }
393
+ const fallbackProps = {
394
+ onClick,
395
+ onPointerOver: () => onHover?.(true),
396
+ onPointerOut: () => onHover?.(false)
397
+ };
398
+ return /* @__PURE__ */ jsxs("group", { position, children: [
399
+ (isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
400
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
401
+ /* @__PURE__ */ jsx(
402
+ "meshBasicMaterial",
403
+ {
404
+ color: isSelected ? 16755200 : 16777215,
405
+ transparent: true,
406
+ opacity: 0.5
407
+ }
408
+ )
409
+ ] }),
410
+ fallbackGeometry === "box" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
411
+ /* @__PURE__ */ jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
412
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
413
+ ] }),
414
+ fallbackGeometry === "sphere" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
415
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [0.4, 16, 16] }),
416
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
417
+ ] }),
418
+ fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
419
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
420
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
421
+ ] })
422
+ ] });
423
+ }
424
+ return /* @__PURE__ */ jsxs(
425
+ "group",
426
+ {
427
+ position,
428
+ rotation: rotationRad,
429
+ onClick,
430
+ onPointerOver: () => onHover?.(true),
431
+ onPointerOut: () => onHover?.(false),
432
+ children: [
433
+ (isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
434
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
435
+ /* @__PURE__ */ jsx(
436
+ "meshBasicMaterial",
437
+ {
438
+ color: isSelected ? 16755200 : 16777215,
439
+ transparent: true,
440
+ opacity: 0.5
441
+ }
442
+ )
443
+ ] }),
444
+ /* @__PURE__ */ jsx("primitive", { object: model, scale: scaleArray })
445
+ ]
446
+ }
447
+ );
448
+ }
449
+ function PhysicsObject3D({
450
+ entityId,
451
+ modelUrl,
452
+ initialPosition = [0, 0, 0],
453
+ initialVelocity = [0, 0, 0],
454
+ mass = 1,
455
+ gravity = 9.8,
456
+ groundY = 0,
457
+ scale = 1,
458
+ onPhysicsUpdate,
459
+ onGroundHit,
460
+ onCollision
461
+ }) {
462
+ const groupRef = useRef(null);
463
+ const physicsStateRef = useRef({
464
+ id: entityId,
465
+ x: initialPosition[0],
466
+ y: initialPosition[1],
467
+ z: initialPosition[2],
468
+ vx: initialVelocity[0],
469
+ vy: initialVelocity[1],
470
+ vz: initialVelocity[2],
471
+ rx: 0,
472
+ ry: 0,
473
+ rz: 0,
474
+ isGrounded: false,
475
+ gravity,
476
+ friction: 0.8,
477
+ mass,
478
+ state: "Active"
479
+ });
480
+ const groundHitRef = useRef(false);
481
+ useEffect(() => {
482
+ if (groupRef.current) {
483
+ groupRef.current.position.set(
484
+ initialPosition[0],
485
+ initialPosition[1],
486
+ initialPosition[2]
487
+ );
488
+ }
489
+ }, []);
490
+ useFrame((state, delta) => {
491
+ const physics = physicsStateRef.current;
492
+ if (physics.state !== "Active") return;
493
+ const dt = Math.min(delta, 0.1);
494
+ if (!physics.isGrounded) {
495
+ physics.vy -= physics.gravity * dt;
496
+ }
497
+ physics.x += physics.vx * dt;
498
+ physics.y += physics.vy * dt;
499
+ physics.z += physics.vz * dt;
500
+ const airResistance = Math.pow(0.99, dt * 60);
501
+ physics.vx *= airResistance;
502
+ physics.vz *= airResistance;
503
+ if (physics.y <= groundY) {
504
+ physics.y = groundY;
505
+ if (!physics.isGrounded) {
506
+ physics.isGrounded = true;
507
+ groundHitRef.current = true;
508
+ physics.vx *= physics.friction;
509
+ physics.vz *= physics.friction;
510
+ onGroundHit?.();
511
+ }
512
+ physics.vy = 0;
513
+ } else {
514
+ physics.isGrounded = false;
515
+ }
516
+ if (groupRef.current) {
517
+ groupRef.current.position.set(physics.x, physics.y, physics.z);
518
+ if (!physics.isGrounded) {
519
+ physics.rx += physics.vz * dt * 0.5;
520
+ physics.rz -= physics.vx * dt * 0.5;
521
+ groupRef.current.rotation.set(physics.rx, physics.ry, physics.rz);
522
+ }
523
+ }
524
+ onPhysicsUpdate?.({ ...physics });
525
+ });
526
+ const scaleArray = typeof scale === "number" ? [scale, scale, scale] : scale;
527
+ return /* @__PURE__ */ jsx("group", { ref: groupRef, scale: scaleArray, children: /* @__PURE__ */ jsx(
528
+ ModelLoader,
529
+ {
530
+ url: modelUrl,
531
+ fallbackGeometry: "box"
532
+ }
533
+ ) });
534
+ }
535
+ function usePhysics3DController(entityId) {
536
+ const applyForce = (fx, fy, fz) => {
537
+ console.log(`Apply force to ${entityId}:`, { fx, fy, fz });
538
+ };
539
+ const setVelocity = (vx, vy, vz) => {
540
+ console.log(`Set velocity for ${entityId}:`, { vx, vy, vz });
541
+ };
542
+ const setPosition = (x, y, z) => {
543
+ console.log(`Set position for ${entityId}:`, { x, y, z });
544
+ };
545
+ const jump = (force = 10) => {
546
+ applyForce(0, force, 0);
547
+ };
548
+ return {
549
+ applyForce,
550
+ setVelocity,
551
+ setPosition,
552
+ jump
553
+ };
554
+ }
555
+ function detectAssetRoot2(modelUrl) {
556
+ const idx = modelUrl.indexOf("/3d/");
557
+ if (idx !== -1) {
558
+ return modelUrl.substring(0, idx + 4);
559
+ }
560
+ return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
561
+ }
562
+ function createGLTFLoaderForUrl(url) {
563
+ const loader = new GLTFLoader();
564
+ loader.setResourcePath(detectAssetRoot2(url));
565
+ return loader;
566
+ }
567
+ var AssetLoader = class {
568
+ constructor() {
569
+ __publicField(this, "objLoader");
570
+ __publicField(this, "textureLoader");
571
+ __publicField(this, "modelCache");
572
+ __publicField(this, "textureCache");
573
+ __publicField(this, "loadingPromises");
574
+ this.objLoader = new OBJLoader();
575
+ this.textureLoader = new THREE6.TextureLoader();
576
+ this.modelCache = /* @__PURE__ */ new Map();
577
+ this.textureCache = /* @__PURE__ */ new Map();
578
+ this.loadingPromises = /* @__PURE__ */ new Map();
579
+ }
580
+ /**
581
+ * Load a GLB/GLTF model
582
+ * @param url - URL to the .glb or .gltf file
583
+ * @returns Promise with loaded model scene and animations
584
+ */
585
+ async loadModel(url) {
586
+ if (this.modelCache.has(url)) {
587
+ return this.modelCache.get(url);
588
+ }
589
+ if (this.loadingPromises.has(url)) {
590
+ return this.loadingPromises.get(url);
591
+ }
592
+ const loader = createGLTFLoaderForUrl(url);
593
+ const loadPromise = loader.loadAsync(url).then((gltf) => {
594
+ const result = {
595
+ scene: gltf.scene,
596
+ animations: gltf.animations || []
597
+ };
598
+ this.modelCache.set(url, result);
599
+ this.loadingPromises.delete(url);
600
+ return result;
601
+ }).catch((error) => {
602
+ this.loadingPromises.delete(url);
603
+ throw new Error(`Failed to load model ${url}: ${error.message}`);
604
+ });
605
+ this.loadingPromises.set(url, loadPromise);
606
+ return loadPromise;
607
+ }
608
+ /**
609
+ * Load an OBJ model (fallback for non-GLB assets)
610
+ * @param url - URL to the .obj file
611
+ * @returns Promise with loaded object group
612
+ */
613
+ async loadOBJ(url) {
614
+ if (this.modelCache.has(url)) {
615
+ return this.modelCache.get(url).scene;
616
+ }
617
+ if (this.loadingPromises.has(url)) {
618
+ const result = await this.loadingPromises.get(url);
619
+ return result.scene;
620
+ }
621
+ const loadPromise = this.objLoader.loadAsync(url).then((group) => {
622
+ const result = {
623
+ scene: group,
624
+ animations: []
625
+ };
626
+ this.modelCache.set(url, result);
627
+ this.loadingPromises.delete(url);
628
+ return result;
629
+ }).catch((error) => {
630
+ this.loadingPromises.delete(url);
631
+ throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
632
+ });
633
+ this.loadingPromises.set(url, loadPromise);
634
+ return (await loadPromise).scene;
635
+ }
636
+ /**
637
+ * Load a texture
638
+ * @param url - URL to the texture image
639
+ * @returns Promise with loaded texture
640
+ */
641
+ async loadTexture(url) {
642
+ if (this.textureCache.has(url)) {
643
+ return this.textureCache.get(url);
644
+ }
645
+ if (this.loadingPromises.has(`texture:${url}`)) {
646
+ return this.loadingPromises.get(`texture:${url}`);
647
+ }
648
+ const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
649
+ texture.colorSpace = THREE6.SRGBColorSpace;
650
+ this.textureCache.set(url, texture);
651
+ this.loadingPromises.delete(`texture:${url}`);
652
+ return texture;
653
+ }).catch((error) => {
654
+ this.loadingPromises.delete(`texture:${url}`);
655
+ throw new Error(`Failed to load texture ${url}: ${error.message}`);
656
+ });
657
+ this.loadingPromises.set(`texture:${url}`, loadPromise);
658
+ return loadPromise;
659
+ }
660
+ /**
661
+ * Preload multiple assets
662
+ * @param urls - Array of asset URLs to preload
663
+ * @returns Promise that resolves when all assets are loaded
664
+ */
665
+ async preload(urls) {
666
+ const promises = urls.map((url) => {
667
+ if (url.endsWith(".glb") || url.endsWith(".gltf")) {
668
+ return this.loadModel(url).catch(() => null);
669
+ } else if (url.endsWith(".obj")) {
670
+ return this.loadOBJ(url).catch(() => null);
671
+ } else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
672
+ return this.loadTexture(url).catch(() => null);
673
+ }
674
+ return Promise.resolve(null);
675
+ });
676
+ await Promise.all(promises);
677
+ }
678
+ /**
679
+ * Check if a model is cached
680
+ * @param url - Model URL
681
+ */
682
+ hasModel(url) {
683
+ return this.modelCache.has(url);
684
+ }
685
+ /**
686
+ * Check if a texture is cached
687
+ * @param url - Texture URL
688
+ */
689
+ hasTexture(url) {
690
+ return this.textureCache.has(url);
691
+ }
692
+ /**
693
+ * Get cached model (throws if not cached)
694
+ * @param url - Model URL
695
+ */
696
+ getModel(url) {
697
+ const model = this.modelCache.get(url);
698
+ if (!model) {
699
+ throw new Error(`Model ${url} not in cache`);
700
+ }
701
+ return model;
702
+ }
703
+ /**
704
+ * Get cached texture (throws if not cached)
705
+ * @param url - Texture URL
706
+ */
707
+ getTexture(url) {
708
+ const texture = this.textureCache.get(url);
709
+ if (!texture) {
710
+ throw new Error(`Texture ${url} not in cache`);
711
+ }
712
+ return texture;
713
+ }
714
+ /**
715
+ * Clear all caches
716
+ */
717
+ clearCache() {
718
+ this.textureCache.forEach((texture) => {
719
+ texture.dispose();
720
+ });
721
+ this.modelCache.forEach((model) => {
722
+ model.scene.traverse((child) => {
723
+ if (child instanceof THREE6.Mesh) {
724
+ child.geometry.dispose();
725
+ if (Array.isArray(child.material)) {
726
+ child.material.forEach((m) => m.dispose());
727
+ } else {
728
+ child.material.dispose();
729
+ }
730
+ }
731
+ });
732
+ });
733
+ this.modelCache.clear();
734
+ this.textureCache.clear();
735
+ this.loadingPromises.clear();
736
+ }
737
+ /**
738
+ * Get cache statistics
739
+ */
740
+ getStats() {
741
+ return {
742
+ models: this.modelCache.size,
743
+ textures: this.textureCache.size,
744
+ loading: this.loadingPromises.size
745
+ };
746
+ }
747
+ };
748
+ var assetLoader = new AssetLoader();
749
+
750
+ // components/organisms/game/three/hooks/useThree.ts
9
751
  var DEFAULT_OPTIONS = {
10
752
  cameraMode: "isometric",
11
753
  cameraPosition: [10, 10, 10],
@@ -15,7 +757,7 @@ var DEFAULT_OPTIONS = {
15
757
  gridSize: 20,
16
758
  assetLoader: new AssetLoader()
17
759
  };
18
- function useThree(options = {}) {
760
+ function useThree3(options = {}) {
19
761
  const opts = { ...DEFAULT_OPTIONS, ...options };
20
762
  const containerRef = useRef(null);
21
763
  const canvasRef = useRef(null);
@@ -28,21 +770,21 @@ function useThree(options = {}) {
28
770
  const [isReady, setIsReady] = useState(false);
29
771
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
30
772
  const initialCameraPosition = useMemo(
31
- () => new THREE.Vector3(...opts.cameraPosition),
773
+ () => new THREE6.Vector3(...opts.cameraPosition),
32
774
  []
33
775
  );
34
776
  useEffect(() => {
35
777
  if (!containerRef.current) return;
36
778
  const container = containerRef.current;
37
779
  const { clientWidth, clientHeight } = container;
38
- const scene = new THREE.Scene();
39
- scene.background = new THREE.Color(opts.backgroundColor);
780
+ const scene = new THREE6.Scene();
781
+ scene.background = new THREE6.Color(opts.backgroundColor);
40
782
  sceneRef.current = scene;
41
783
  let camera;
42
784
  const aspect = clientWidth / clientHeight;
43
785
  if (opts.cameraMode === "isometric") {
44
786
  const size = 10;
45
- camera = new THREE.OrthographicCamera(
787
+ camera = new THREE6.OrthographicCamera(
46
788
  -size * aspect,
47
789
  size * aspect,
48
790
  size,
@@ -51,11 +793,11 @@ function useThree(options = {}) {
51
793
  1e3
52
794
  );
53
795
  } else {
54
- camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1e3);
796
+ camera = new THREE6.PerspectiveCamera(45, aspect, 0.1, 1e3);
55
797
  }
56
798
  camera.position.copy(initialCameraPosition);
57
799
  cameraRef.current = camera;
58
- const renderer = new THREE.WebGLRenderer({
800
+ const renderer = new THREE6.WebGLRenderer({
59
801
  antialias: true,
60
802
  alpha: true,
61
803
  canvas: canvasRef.current || void 0
@@ -63,25 +805,25 @@ function useThree(options = {}) {
63
805
  renderer.setSize(clientWidth, clientHeight);
64
806
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
65
807
  renderer.shadowMap.enabled = opts.shadows;
66
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
808
+ renderer.shadowMap.type = THREE6.PCFSoftShadowMap;
67
809
  rendererRef.current = renderer;
68
- const controls = new OrbitControls(camera, renderer.domElement);
810
+ const controls = new OrbitControls$1(camera, renderer.domElement);
69
811
  controls.enableDamping = true;
70
812
  controls.dampingFactor = 0.05;
71
813
  controls.minDistance = 2;
72
814
  controls.maxDistance = 100;
73
815
  controls.maxPolarAngle = Math.PI / 2 - 0.1;
74
816
  controlsRef.current = controls;
75
- const ambientLight = new THREE.AmbientLight(16777215, 0.6);
817
+ const ambientLight = new THREE6.AmbientLight(16777215, 0.6);
76
818
  scene.add(ambientLight);
77
- const directionalLight = new THREE.DirectionalLight(16777215, 0.8);
819
+ const directionalLight = new THREE6.DirectionalLight(16777215, 0.8);
78
820
  directionalLight.position.set(10, 20, 10);
79
821
  directionalLight.castShadow = opts.shadows;
80
822
  directionalLight.shadow.mapSize.width = 2048;
81
823
  directionalLight.shadow.mapSize.height = 2048;
82
824
  scene.add(directionalLight);
83
825
  if (opts.showGrid) {
84
- const gridHelper = new THREE.GridHelper(
826
+ const gridHelper = new THREE6.GridHelper(
85
827
  opts.gridSize,
86
828
  opts.gridSize,
87
829
  4473924,
@@ -99,10 +841,10 @@ function useThree(options = {}) {
99
841
  const handleResize = () => {
100
842
  const { clientWidth: width, clientHeight: height } = container;
101
843
  setDimensions({ width, height });
102
- if (camera instanceof THREE.PerspectiveCamera) {
844
+ if (camera instanceof THREE6.PerspectiveCamera) {
103
845
  camera.aspect = width / height;
104
846
  camera.updateProjectionMatrix();
105
- } else if (camera instanceof THREE.OrthographicCamera) {
847
+ } else if (camera instanceof THREE6.OrthographicCamera) {
106
848
  const aspect2 = width / height;
107
849
  const size = 10;
108
850
  camera.left = -size * aspect2;
@@ -133,7 +875,7 @@ function useThree(options = {}) {
133
875
  let newCamera;
134
876
  if (opts.cameraMode === "isometric") {
135
877
  const size = 10;
136
- newCamera = new THREE.OrthographicCamera(
878
+ newCamera = new THREE6.OrthographicCamera(
137
879
  -size * aspect,
138
880
  size * aspect,
139
881
  size,
@@ -142,7 +884,7 @@ function useThree(options = {}) {
142
884
  1e3
143
885
  );
144
886
  } else {
145
- newCamera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1e3);
887
+ newCamera = new THREE6.PerspectiveCamera(45, aspect, 0.1, 1e3);
146
888
  }
147
889
  newCamera.position.copy(currentPos);
148
890
  cameraRef.current = newCamera;
@@ -211,6 +953,180 @@ function useThree(options = {}) {
211
953
  fitView
212
954
  };
213
955
  }
956
+ function useAssetLoader(options = {}) {
957
+ const { preloadUrls = [], loader: customLoader } = options;
958
+ const loaderRef = useRef(customLoader || new AssetLoader());
959
+ const [state, setState] = useState({
960
+ isLoading: false,
961
+ progress: 0,
962
+ loaded: 0,
963
+ total: 0,
964
+ errors: []
965
+ });
966
+ useEffect(() => {
967
+ if (preloadUrls.length > 0) {
968
+ preload(preloadUrls);
969
+ }
970
+ }, []);
971
+ const updateProgress = useCallback((loaded, total) => {
972
+ setState((prev) => ({
973
+ ...prev,
974
+ loaded,
975
+ total,
976
+ progress: total > 0 ? Math.round(loaded / total * 100) : 0
977
+ }));
978
+ }, []);
979
+ const loadModel = useCallback(
980
+ async (url) => {
981
+ setState((prev) => ({ ...prev, isLoading: true }));
982
+ try {
983
+ const model = await loaderRef.current.loadModel(url);
984
+ setState((prev) => ({
985
+ ...prev,
986
+ isLoading: false,
987
+ loaded: prev.loaded + 1
988
+ }));
989
+ return model;
990
+ } catch (error) {
991
+ const errorMsg = error instanceof Error ? error.message : String(error);
992
+ setState((prev) => ({
993
+ ...prev,
994
+ isLoading: false,
995
+ errors: [...prev.errors, errorMsg]
996
+ }));
997
+ throw error;
998
+ }
999
+ },
1000
+ []
1001
+ );
1002
+ const loadOBJ = useCallback(
1003
+ async (url) => {
1004
+ setState((prev) => ({ ...prev, isLoading: true }));
1005
+ try {
1006
+ const model = await loaderRef.current.loadOBJ(url);
1007
+ setState((prev) => ({
1008
+ ...prev,
1009
+ isLoading: false,
1010
+ loaded: prev.loaded + 1
1011
+ }));
1012
+ return model;
1013
+ } catch (error) {
1014
+ const errorMsg = error instanceof Error ? error.message : String(error);
1015
+ setState((prev) => ({
1016
+ ...prev,
1017
+ isLoading: false,
1018
+ errors: [...prev.errors, errorMsg]
1019
+ }));
1020
+ throw error;
1021
+ }
1022
+ },
1023
+ []
1024
+ );
1025
+ const loadTexture = useCallback(
1026
+ async (url) => {
1027
+ setState((prev) => ({ ...prev, isLoading: true }));
1028
+ try {
1029
+ const texture = await loaderRef.current.loadTexture(url);
1030
+ setState((prev) => ({
1031
+ ...prev,
1032
+ isLoading: false,
1033
+ loaded: prev.loaded + 1
1034
+ }));
1035
+ return texture;
1036
+ } catch (error) {
1037
+ const errorMsg = error instanceof Error ? error.message : String(error);
1038
+ setState((prev) => ({
1039
+ ...prev,
1040
+ isLoading: false,
1041
+ errors: [...prev.errors, errorMsg]
1042
+ }));
1043
+ throw error;
1044
+ }
1045
+ },
1046
+ []
1047
+ );
1048
+ const preload = useCallback(
1049
+ async (urls) => {
1050
+ setState((prev) => ({
1051
+ ...prev,
1052
+ isLoading: true,
1053
+ total: urls.length,
1054
+ loaded: 0,
1055
+ errors: []
1056
+ }));
1057
+ let completed = 0;
1058
+ const errors = [];
1059
+ await Promise.all(
1060
+ urls.map(async (url) => {
1061
+ try {
1062
+ if (url.endsWith(".glb") || url.endsWith(".gltf")) {
1063
+ await loaderRef.current.loadModel(url);
1064
+ } else if (url.endsWith(".obj")) {
1065
+ await loaderRef.current.loadOBJ(url);
1066
+ } else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
1067
+ await loaderRef.current.loadTexture(url);
1068
+ }
1069
+ completed++;
1070
+ updateProgress(completed, urls.length);
1071
+ } catch (error) {
1072
+ const errorMsg = error instanceof Error ? error.message : String(error);
1073
+ errors.push(`${url}: ${errorMsg}`);
1074
+ completed++;
1075
+ updateProgress(completed, urls.length);
1076
+ }
1077
+ })
1078
+ );
1079
+ setState((prev) => ({
1080
+ ...prev,
1081
+ isLoading: false,
1082
+ errors
1083
+ }));
1084
+ },
1085
+ [updateProgress]
1086
+ );
1087
+ const hasModel = useCallback((url) => {
1088
+ return loaderRef.current.hasModel(url);
1089
+ }, []);
1090
+ const hasTexture = useCallback((url) => {
1091
+ return loaderRef.current.hasTexture(url);
1092
+ }, []);
1093
+ const getModel = useCallback((url) => {
1094
+ try {
1095
+ return loaderRef.current.getModel(url);
1096
+ } catch {
1097
+ return void 0;
1098
+ }
1099
+ }, []);
1100
+ const getTexture = useCallback((url) => {
1101
+ try {
1102
+ return loaderRef.current.getTexture(url);
1103
+ } catch {
1104
+ return void 0;
1105
+ }
1106
+ }, []);
1107
+ const clearCache = useCallback(() => {
1108
+ loaderRef.current.clearCache();
1109
+ setState({
1110
+ isLoading: false,
1111
+ progress: 0,
1112
+ loaded: 0,
1113
+ total: 0,
1114
+ errors: []
1115
+ });
1116
+ }, []);
1117
+ return {
1118
+ ...state,
1119
+ loadModel,
1120
+ loadOBJ,
1121
+ loadTexture,
1122
+ preload,
1123
+ hasModel,
1124
+ hasTexture,
1125
+ getModel,
1126
+ getTexture,
1127
+ clearCache
1128
+ };
1129
+ }
214
1130
  function useSceneGraph() {
215
1131
  const nodesRef = useRef(/* @__PURE__ */ new Map());
216
1132
  const addNode = useCallback((node) => {
@@ -298,8 +1214,8 @@ function useSceneGraph() {
298
1214
  }
299
1215
  function useRaycaster(options) {
300
1216
  const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
301
- const raycaster = useRef(new THREE.Raycaster());
302
- const mouse = useRef(new THREE.Vector2());
1217
+ const raycaster = useRef(new THREE6.Raycaster());
1218
+ const mouse = useRef(new THREE6.Vector2());
303
1219
  const clientToNDC = useCallback(
304
1220
  (clientX, clientY) => {
305
1221
  if (!canvas) {
@@ -369,8 +1285,8 @@ function useRaycaster(options) {
369
1285
  const ndc = clientToNDC(clientX, clientY);
370
1286
  mouse.current.set(ndc.x, ndc.y);
371
1287
  raycaster.current.setFromCamera(mouse.current, camera);
372
- const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
373
- const target = new THREE.Vector3();
1288
+ const plane = new THREE6.Plane(new THREE6.Vector3(0, 1, 0), 0);
1289
+ const target = new THREE6.Vector3();
374
1290
  const intersection = raycaster.current.ray.intersectPlane(plane, target);
375
1291
  if (intersection) {
376
1292
  const gridX = Math.round((target.x - offsetX) / cellSize);
@@ -408,7 +1324,7 @@ function useRaycaster(options) {
408
1324
  return {
409
1325
  gridX: gridCoords.x,
410
1326
  gridZ: gridCoords.z,
411
- worldPosition: new THREE.Vector3(
1327
+ worldPosition: new THREE6.Vector3(
412
1328
  gridCoords.x * cellSize + offsetX,
413
1329
  0,
414
1330
  gridCoords.z * cellSize + offsetZ
@@ -430,6 +1346,835 @@ function useRaycaster(options) {
430
1346
  isWithinCanvas
431
1347
  };
432
1348
  }
1349
+ var EventBusContext = createContext(null);
1350
+
1351
+ // hooks/useEventBus.ts
1352
+ function getGlobalEventBus() {
1353
+ if (typeof window !== "undefined") {
1354
+ return window.__kflowEventBus ?? null;
1355
+ }
1356
+ return null;
1357
+ }
1358
+ var fallbackListeners = /* @__PURE__ */ new Map();
1359
+ var fallbackAnyListeners = /* @__PURE__ */ new Set();
1360
+ var fallbackEventBus = {
1361
+ emit: (type, payload) => {
1362
+ const event = {
1363
+ type,
1364
+ payload,
1365
+ timestamp: Date.now()
1366
+ };
1367
+ const handlers = fallbackListeners.get(type);
1368
+ if (handlers) {
1369
+ handlers.forEach((handler) => {
1370
+ try {
1371
+ handler(event);
1372
+ } catch (error) {
1373
+ console.error(`[EventBus] Error in listener for '${type}':`, error);
1374
+ }
1375
+ });
1376
+ }
1377
+ fallbackAnyListeners.forEach((handler) => {
1378
+ try {
1379
+ handler(event);
1380
+ } catch (error) {
1381
+ console.error(`[EventBus] Error in onAny listener for '${type}':`, error);
1382
+ }
1383
+ });
1384
+ },
1385
+ on: (type, listener) => {
1386
+ if (!fallbackListeners.has(type)) {
1387
+ fallbackListeners.set(type, /* @__PURE__ */ new Set());
1388
+ }
1389
+ fallbackListeners.get(type).add(listener);
1390
+ return () => {
1391
+ const handlers = fallbackListeners.get(type);
1392
+ if (handlers) {
1393
+ handlers.delete(listener);
1394
+ if (handlers.size === 0) {
1395
+ fallbackListeners.delete(type);
1396
+ }
1397
+ }
1398
+ };
1399
+ },
1400
+ once: (type, listener) => {
1401
+ const wrappedListener = (event) => {
1402
+ fallbackListeners.get(type)?.delete(wrappedListener);
1403
+ listener(event);
1404
+ };
1405
+ return fallbackEventBus.on(type, wrappedListener);
1406
+ },
1407
+ hasListeners: (type) => {
1408
+ const handlers = fallbackListeners.get(type);
1409
+ return handlers !== void 0 && handlers.size > 0;
1410
+ },
1411
+ onAny: (listener) => {
1412
+ fallbackAnyListeners.add(listener);
1413
+ return () => {
1414
+ fallbackAnyListeners.delete(listener);
1415
+ };
1416
+ }
1417
+ };
1418
+ function useEventBus() {
1419
+ const context = useContext(EventBusContext);
1420
+ return context ?? getGlobalEventBus() ?? fallbackEventBus;
1421
+ }
1422
+ function useEmitEvent() {
1423
+ const eventBus = useEventBus();
1424
+ return useCallback(
1425
+ (type, payload) => {
1426
+ eventBus.emit(type, payload);
1427
+ },
1428
+ [eventBus]
1429
+ );
1430
+ }
1431
+
1432
+ // components/organisms/game/three/hooks/useGameCanvas3DEvents.ts
1433
+ function useGameCanvas3DEvents(options) {
1434
+ const {
1435
+ tileClickEvent,
1436
+ unitClickEvent,
1437
+ featureClickEvent,
1438
+ canvasClickEvent,
1439
+ tileHoverEvent,
1440
+ tileLeaveEvent,
1441
+ unitAnimationEvent,
1442
+ cameraChangeEvent,
1443
+ onTileClick,
1444
+ onUnitClick,
1445
+ onFeatureClick,
1446
+ onCanvasClick,
1447
+ onTileHover,
1448
+ onUnitAnimation
1449
+ } = options;
1450
+ const emit = useEmitEvent();
1451
+ const optionsRef = useRef(options);
1452
+ optionsRef.current = options;
1453
+ const handleTileClick = useCallback(
1454
+ (tile, event) => {
1455
+ if (tileClickEvent) {
1456
+ emit(tileClickEvent, {
1457
+ tileId: tile.id,
1458
+ x: tile.x,
1459
+ z: tile.z ?? tile.y ?? 0,
1460
+ type: tile.type,
1461
+ terrain: tile.terrain,
1462
+ elevation: tile.elevation
1463
+ });
1464
+ }
1465
+ optionsRef.current.onTileClick?.(tile, event);
1466
+ },
1467
+ [tileClickEvent, emit]
1468
+ );
1469
+ const handleUnitClick = useCallback(
1470
+ (unit, event) => {
1471
+ if (unitClickEvent) {
1472
+ emit(unitClickEvent, {
1473
+ unitId: unit.id,
1474
+ x: unit.x,
1475
+ z: unit.z ?? unit.y ?? 0,
1476
+ unitType: unit.unitType,
1477
+ name: unit.name,
1478
+ team: unit.team,
1479
+ faction: unit.faction,
1480
+ health: unit.health,
1481
+ maxHealth: unit.maxHealth
1482
+ });
1483
+ }
1484
+ optionsRef.current.onUnitClick?.(unit, event);
1485
+ },
1486
+ [unitClickEvent, emit]
1487
+ );
1488
+ const handleFeatureClick = useCallback(
1489
+ (feature, event) => {
1490
+ if (featureClickEvent) {
1491
+ emit(featureClickEvent, {
1492
+ featureId: feature.id,
1493
+ x: feature.x,
1494
+ z: feature.z ?? feature.y ?? 0,
1495
+ type: feature.type,
1496
+ elevation: feature.elevation
1497
+ });
1498
+ }
1499
+ optionsRef.current.onFeatureClick?.(feature, event);
1500
+ },
1501
+ [featureClickEvent, emit]
1502
+ );
1503
+ const handleCanvasClick = useCallback(
1504
+ (event) => {
1505
+ if (canvasClickEvent) {
1506
+ emit(canvasClickEvent, {
1507
+ clientX: event.clientX,
1508
+ clientY: event.clientY,
1509
+ button: event.button
1510
+ });
1511
+ }
1512
+ optionsRef.current.onCanvasClick?.(event);
1513
+ },
1514
+ [canvasClickEvent, emit]
1515
+ );
1516
+ const handleTileHover = useCallback(
1517
+ (tile, event) => {
1518
+ if (tile) {
1519
+ if (tileHoverEvent) {
1520
+ emit(tileHoverEvent, {
1521
+ tileId: tile.id,
1522
+ x: tile.x,
1523
+ z: tile.z ?? tile.y ?? 0,
1524
+ type: tile.type
1525
+ });
1526
+ }
1527
+ } else {
1528
+ if (tileLeaveEvent) {
1529
+ emit(tileLeaveEvent, {});
1530
+ }
1531
+ }
1532
+ optionsRef.current.onTileHover?.(tile, event);
1533
+ },
1534
+ [tileHoverEvent, tileLeaveEvent, emit]
1535
+ );
1536
+ const handleUnitAnimation = useCallback(
1537
+ (unitId, state) => {
1538
+ if (unitAnimationEvent) {
1539
+ emit(unitAnimationEvent, {
1540
+ unitId,
1541
+ state,
1542
+ timestamp: Date.now()
1543
+ });
1544
+ }
1545
+ optionsRef.current.onUnitAnimation?.(unitId, state);
1546
+ },
1547
+ [unitAnimationEvent, emit]
1548
+ );
1549
+ const handleCameraChange = useCallback(
1550
+ (position) => {
1551
+ if (cameraChangeEvent) {
1552
+ emit(cameraChangeEvent, {
1553
+ position,
1554
+ timestamp: Date.now()
1555
+ });
1556
+ }
1557
+ },
1558
+ [cameraChangeEvent, emit]
1559
+ );
1560
+ return {
1561
+ handleTileClick,
1562
+ handleUnitClick,
1563
+ handleFeatureClick,
1564
+ handleCanvasClick,
1565
+ handleTileHover,
1566
+ handleUnitAnimation,
1567
+ handleCameraChange
1568
+ };
1569
+ }
1570
+ var DEFAULT_TERRAIN_COLORS = {
1571
+ grass: "#44aa44",
1572
+ dirt: "#8b7355",
1573
+ sand: "#ddcc88",
1574
+ water: "#4488cc",
1575
+ rock: "#888888",
1576
+ snow: "#eeeeee",
1577
+ forest: "#228b22",
1578
+ desert: "#d4a574",
1579
+ mountain: "#696969",
1580
+ swamp: "#556b2f"
1581
+ };
1582
+ function TileRenderer({
1583
+ tiles,
1584
+ cellSize = 1,
1585
+ offsetX = 0,
1586
+ offsetZ = 0,
1587
+ useInstancing = true,
1588
+ terrainColors = DEFAULT_TERRAIN_COLORS,
1589
+ onTileClick,
1590
+ onTileHover,
1591
+ selectedTileIds = [],
1592
+ validMoves = [],
1593
+ attackTargets = []
1594
+ }) {
1595
+ const meshRef = useRef(null);
1596
+ const geometry = useMemo(() => {
1597
+ return new THREE6.BoxGeometry(cellSize * 0.95, 0.2, cellSize * 0.95);
1598
+ }, [cellSize]);
1599
+ const material = useMemo(() => {
1600
+ return new THREE6.MeshStandardMaterial({
1601
+ roughness: 0.8,
1602
+ metalness: 0.1
1603
+ });
1604
+ }, []);
1605
+ const { positions, colors, tileMap } = useMemo(() => {
1606
+ const pos = [];
1607
+ const cols = [];
1608
+ const map = /* @__PURE__ */ new Map();
1609
+ tiles.forEach((tile) => {
1610
+ const x = (tile.x - offsetX) * cellSize;
1611
+ const z = ((tile.z ?? tile.y ?? 0) - offsetZ) * cellSize;
1612
+ const y = (tile.elevation ?? 0) * 0.1;
1613
+ pos.push(new THREE6.Vector3(x, y, z));
1614
+ const colorHex = terrainColors[tile.type || ""] || terrainColors[tile.terrain || ""] || "#808080";
1615
+ const color = new THREE6.Color(colorHex);
1616
+ const isValidMove = validMoves.some(
1617
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
1618
+ );
1619
+ const isAttackTarget = attackTargets.some(
1620
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
1621
+ );
1622
+ const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
1623
+ if (isSelected) {
1624
+ color.addScalar(0.3);
1625
+ } else if (isAttackTarget) {
1626
+ color.setHex(16729156);
1627
+ } else if (isValidMove) {
1628
+ color.setHex(4521796);
1629
+ }
1630
+ cols.push(color);
1631
+ map.set(`${tile.x},${tile.z ?? tile.y ?? 0}`, tile);
1632
+ });
1633
+ return { positions: pos, colors: cols, tileMap: map };
1634
+ }, [tiles, cellSize, offsetX, offsetZ, terrainColors, selectedTileIds, validMoves, attackTargets]);
1635
+ useEffect(() => {
1636
+ if (!meshRef.current || !useInstancing) return;
1637
+ const mesh = meshRef.current;
1638
+ mesh.count = positions.length;
1639
+ const dummy = new THREE6.Object3D();
1640
+ positions.forEach((pos, i) => {
1641
+ dummy.position.copy(pos);
1642
+ dummy.updateMatrix();
1643
+ mesh.setMatrixAt(i, dummy.matrix);
1644
+ if (mesh.setColorAt) {
1645
+ mesh.setColorAt(i, colors[i]);
1646
+ }
1647
+ });
1648
+ mesh.instanceMatrix.needsUpdate = true;
1649
+ if (mesh.instanceColor) {
1650
+ mesh.instanceColor.needsUpdate = true;
1651
+ }
1652
+ }, [positions, colors, useInstancing]);
1653
+ const handlePointerMove = (e) => {
1654
+ if (!onTileHover) return;
1655
+ const instanceId = e.instanceId;
1656
+ if (instanceId !== void 0) {
1657
+ const pos = positions[instanceId];
1658
+ if (pos) {
1659
+ const gridX = Math.round(pos.x / cellSize + offsetX);
1660
+ const gridZ = Math.round(pos.z / cellSize + offsetZ);
1661
+ const tile = tileMap.get(`${gridX},${gridZ}`);
1662
+ if (tile) {
1663
+ onTileHover(tile);
1664
+ }
1665
+ }
1666
+ }
1667
+ };
1668
+ const handleClick = (e) => {
1669
+ if (!onTileClick) return;
1670
+ const instanceId = e.instanceId;
1671
+ if (instanceId !== void 0) {
1672
+ const pos = positions[instanceId];
1673
+ if (pos) {
1674
+ const gridX = Math.round(pos.x / cellSize + offsetX);
1675
+ const gridZ = Math.round(pos.z / cellSize + offsetZ);
1676
+ const tile = tileMap.get(`${gridX},${gridZ}`);
1677
+ if (tile) {
1678
+ onTileClick(tile);
1679
+ }
1680
+ }
1681
+ }
1682
+ };
1683
+ const renderIndividualTiles = () => {
1684
+ return tiles.map((tile) => {
1685
+ const x = (tile.x - offsetX) * cellSize;
1686
+ const z = ((tile.z ?? tile.y ?? 0) - offsetZ) * cellSize;
1687
+ const y = (tile.elevation ?? 0) * 0.1;
1688
+ const colorHex = terrainColors[tile.type || ""] || terrainColors[tile.terrain || ""] || "#808080";
1689
+ const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
1690
+ const isValidMove = validMoves.some(
1691
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
1692
+ );
1693
+ const isAttackTarget = attackTargets.some(
1694
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
1695
+ );
1696
+ let emissive = "#000000";
1697
+ if (isSelected) emissive = "#444444";
1698
+ else if (isAttackTarget) emissive = "#440000";
1699
+ else if (isValidMove) emissive = "#004400";
1700
+ return /* @__PURE__ */ jsxs(
1701
+ "mesh",
1702
+ {
1703
+ position: [x, y, z],
1704
+ userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
1705
+ onClick: () => onTileClick?.(tile),
1706
+ onPointerEnter: () => onTileHover?.(tile),
1707
+ onPointerLeave: () => onTileHover?.(null),
1708
+ children: [
1709
+ /* @__PURE__ */ jsx("boxGeometry", { args: [cellSize * 0.95, 0.2, cellSize * 0.95] }),
1710
+ /* @__PURE__ */ jsx(
1711
+ "meshStandardMaterial",
1712
+ {
1713
+ color: colorHex,
1714
+ emissive,
1715
+ roughness: 0.8,
1716
+ metalness: 0.1
1717
+ }
1718
+ )
1719
+ ]
1720
+ },
1721
+ tile.id ?? `tile-${tile.x}-${tile.y}`
1722
+ );
1723
+ });
1724
+ };
1725
+ if (useInstancing && tiles.length > 0) {
1726
+ return /* @__PURE__ */ jsx(
1727
+ "instancedMesh",
1728
+ {
1729
+ ref: meshRef,
1730
+ args: [geometry, material, tiles.length],
1731
+ onPointerMove: handlePointerMove,
1732
+ onClick: handleClick
1733
+ }
1734
+ );
1735
+ }
1736
+ return /* @__PURE__ */ jsx("group", { children: renderIndividualTiles() });
1737
+ }
1738
+ function UnitVisual({ unit, position, isSelected, onClick }) {
1739
+ const groupRef = useRef(null);
1740
+ const [animationState, setAnimationState] = useState("idle");
1741
+ const [isHovered, setIsHovered] = useState(false);
1742
+ const teamColor = useMemo(() => {
1743
+ if (unit.faction === "player" || unit.team === "player") return 4491519;
1744
+ if (unit.faction === "enemy" || unit.team === "enemy") return 16729156;
1745
+ if (unit.faction === "neutral" || unit.team === "neutral") return 16777028;
1746
+ return 8947848;
1747
+ }, [unit.faction, unit.team]);
1748
+ useFrame((state) => {
1749
+ if (groupRef.current && animationState === "idle") {
1750
+ const y = position[1] + Math.sin(state.clock.elapsedTime * 2 + position[0]) * 0.05;
1751
+ groupRef.current.position.y = y;
1752
+ }
1753
+ });
1754
+ const healthPercent = useMemo(() => {
1755
+ if (unit.health === void 0 || unit.maxHealth === void 0) return 1;
1756
+ return Math.max(0, Math.min(1, unit.health / unit.maxHealth));
1757
+ }, [unit.health, unit.maxHealth]);
1758
+ const healthColor = useMemo(() => {
1759
+ if (healthPercent > 0.5) return "#44aa44";
1760
+ if (healthPercent > 0.25) return "#aaaa44";
1761
+ return "#ff4444";
1762
+ }, [healthPercent]);
1763
+ return /* @__PURE__ */ jsxs(
1764
+ "group",
1765
+ {
1766
+ ref: groupRef,
1767
+ position,
1768
+ onClick,
1769
+ onPointerEnter: () => setIsHovered(true),
1770
+ onPointerLeave: () => setIsHovered(false),
1771
+ userData: { type: "unit", unitId: unit.id },
1772
+ children: [
1773
+ isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
1774
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
1775
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
1776
+ ] }),
1777
+ isHovered && !isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
1778
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
1779
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffffff", transparent: true, opacity: 0.5 })
1780
+ ] }),
1781
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.1, 0], children: [
1782
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.25, 0.25, 0.1, 8] }),
1783
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: teamColor })
1784
+ ] }),
1785
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.5, 0], children: [
1786
+ /* @__PURE__ */ jsx("capsuleGeometry", { args: [0.15, 0.5, 4, 8] }),
1787
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: teamColor })
1788
+ ] }),
1789
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
1790
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
1791
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: teamColor })
1792
+ ] }),
1793
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 1.3, 0], children: [
1794
+ /* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.06] }),
1795
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#333333" })
1796
+ ] }),
1797
+ /* @__PURE__ */ jsxs("mesh", { position: [-0.25 + 0.25 * healthPercent, 1.3, 0.01], children: [
1798
+ /* @__PURE__ */ jsx("planeGeometry", { args: [0.5 * healthPercent, 0.04] }),
1799
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: healthColor })
1800
+ ] }),
1801
+ unit.name && /* @__PURE__ */ jsxs("mesh", { position: [0, 1.5, 0], children: [
1802
+ /* @__PURE__ */ jsx("planeGeometry", { args: [0.4, 0.1] }),
1803
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.5 })
1804
+ ] })
1805
+ ]
1806
+ }
1807
+ );
1808
+ }
1809
+ function UnitRenderer({
1810
+ units,
1811
+ cellSize = 1,
1812
+ offsetX = 0,
1813
+ offsetZ = 0,
1814
+ selectedUnitId,
1815
+ onUnitClick,
1816
+ onAnimationStateChange,
1817
+ animationSpeed = 1
1818
+ }) {
1819
+ const handleUnitClick = React8.useCallback(
1820
+ (unit) => {
1821
+ onUnitClick?.(unit);
1822
+ },
1823
+ [onUnitClick]
1824
+ );
1825
+ return /* @__PURE__ */ jsx("group", { children: units.map((unit) => {
1826
+ const unitX = unit.x ?? unit.position?.x ?? 0;
1827
+ const unitY = unit.z ?? unit.y ?? unit.position?.y ?? 0;
1828
+ const x = (unitX - offsetX) * cellSize;
1829
+ const z = (unitY - offsetZ) * cellSize;
1830
+ const y = (unit.elevation ?? 0) * 0.1 + 0.5;
1831
+ return /* @__PURE__ */ jsx(
1832
+ UnitVisual,
1833
+ {
1834
+ unit,
1835
+ position: [x, y, z],
1836
+ isSelected: selectedUnitId === unit.id,
1837
+ onClick: () => handleUnitClick(unit)
1838
+ },
1839
+ unit.id
1840
+ );
1841
+ }) });
1842
+ }
1843
+ var DEFAULT_FEATURE_CONFIGS = {
1844
+ tree: { color: 2263842, height: 1.5, scale: 1, geometry: "tree" },
1845
+ rock: { color: 8421504, height: 0.5, scale: 0.8, geometry: "rock" },
1846
+ bush: { color: 3329330, height: 0.4, scale: 0.6, geometry: "bush" },
1847
+ house: { color: 9127187, height: 1.2, scale: 1.2, geometry: "house" },
1848
+ tower: { color: 6908265, height: 2.5, scale: 1, geometry: "tower" },
1849
+ wall: { color: 8421504, height: 1, scale: 1, geometry: "wall" },
1850
+ mountain: { color: 5597999, height: 2, scale: 1.5, geometry: "mountain" },
1851
+ hill: { color: 7048739, height: 0.8, scale: 1.2, geometry: "hill" },
1852
+ water: { color: 4491468, height: 0.1, scale: 1, geometry: "water" },
1853
+ chest: { color: 16766720, height: 0.3, scale: 0.4, geometry: "chest" },
1854
+ sign: { color: 9127187, height: 0.8, scale: 0.3, geometry: "sign" },
1855
+ portal: { color: 10040012, height: 1.5, scale: 1, geometry: "portal" }
1856
+ };
1857
+ function TreeFeature({ height, color }) {
1858
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1859
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.3, 0], children: [
1860
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.08, 0.1, height * 0.6, 6] }),
1861
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
1862
+ ] }),
1863
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.7, 0], children: [
1864
+ /* @__PURE__ */ jsx("coneGeometry", { args: [0.4, height * 0.5, 8] }),
1865
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1866
+ ] }),
1867
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.9, 0], children: [
1868
+ /* @__PURE__ */ jsx("coneGeometry", { args: [0.3, height * 0.4, 8] }),
1869
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1870
+ ] }),
1871
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 1.05, 0], children: [
1872
+ /* @__PURE__ */ jsx("coneGeometry", { args: [0.15, height * 0.25, 8] }),
1873
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1874
+ ] })
1875
+ ] });
1876
+ }
1877
+ function RockFeature({ height, color }) {
1878
+ return /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.4, 0], children: [
1879
+ /* @__PURE__ */ jsx("dodecahedronGeometry", { args: [height * 0.5, 0] }),
1880
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color, roughness: 0.9 })
1881
+ ] });
1882
+ }
1883
+ function BushFeature({ height, color }) {
1884
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1885
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.3, 0], children: [
1886
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [height * 0.4, 8, 8] }),
1887
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1888
+ ] }),
1889
+ /* @__PURE__ */ jsxs("mesh", { position: [0.1, height * 0.4, 0.1], children: [
1890
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [height * 0.25, 8, 8] }),
1891
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1892
+ ] })
1893
+ ] });
1894
+ }
1895
+ function HouseFeature({ height, color }) {
1896
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1897
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.4, 0], children: [
1898
+ /* @__PURE__ */ jsx("boxGeometry", { args: [0.8, height * 0.8, 0.8] }),
1899
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: 13808780 })
1900
+ ] }),
1901
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.9, 0], children: [
1902
+ /* @__PURE__ */ jsx("coneGeometry", { args: [0.6, height * 0.4, 4] }),
1903
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1904
+ ] }),
1905
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.25, 0.41], children: [
1906
+ /* @__PURE__ */ jsx("planeGeometry", { args: [0.25, height * 0.5] }),
1907
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: 4863784 })
1908
+ ] })
1909
+ ] });
1910
+ }
1911
+ function TowerFeature({ height, color }) {
1912
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1913
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
1914
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.35, height, 8] }),
1915
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1916
+ ] }),
1917
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height + 0.05, 0], children: [
1918
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.35, 0.35, 0.1, 8] }),
1919
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1920
+ ] })
1921
+ ] });
1922
+ }
1923
+ function ChestFeature({ height, color }) {
1924
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1925
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
1926
+ /* @__PURE__ */ jsx("boxGeometry", { args: [0.3, height, 0.2] }),
1927
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
1928
+ ] }),
1929
+ /* @__PURE__ */ jsxs("mesh", { position: [0, height + 0.05, 0], children: [
1930
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.15, 0.15, 0.3, 8, 1, false, 0, Math.PI] }),
1931
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
1932
+ ] })
1933
+ ] });
1934
+ }
1935
+ function DefaultFeature({ height, color }) {
1936
+ return /* @__PURE__ */ jsxs("mesh", { position: [0, height * 0.5, 0], children: [
1937
+ /* @__PURE__ */ jsx("boxGeometry", { args: [0.5, height, 0.5] }),
1938
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color })
1939
+ ] });
1940
+ }
1941
+ function FeatureVisual({
1942
+ feature,
1943
+ position,
1944
+ isSelected,
1945
+ onClick,
1946
+ onHover
1947
+ }) {
1948
+ const config = DEFAULT_FEATURE_CONFIGS[feature.type] || {
1949
+ color: 8947848,
1950
+ height: 0.5,
1951
+ scale: 1,
1952
+ geometry: "default"
1953
+ };
1954
+ const color = feature.color ? parseInt(feature.color.replace("#", ""), 16) : config.color;
1955
+ const renderGeometry = () => {
1956
+ switch (config.geometry) {
1957
+ case "tree":
1958
+ return /* @__PURE__ */ jsx(TreeFeature, { height: config.height, color });
1959
+ case "rock":
1960
+ return /* @__PURE__ */ jsx(RockFeature, { height: config.height, color });
1961
+ case "bush":
1962
+ return /* @__PURE__ */ jsx(BushFeature, { height: config.height, color });
1963
+ case "house":
1964
+ return /* @__PURE__ */ jsx(HouseFeature, { height: config.height, color });
1965
+ case "tower":
1966
+ return /* @__PURE__ */ jsx(TowerFeature, { height: config.height, color });
1967
+ case "chest":
1968
+ return /* @__PURE__ */ jsx(ChestFeature, { height: config.height, color });
1969
+ default:
1970
+ return /* @__PURE__ */ jsx(DefaultFeature, { height: config.height, color });
1971
+ }
1972
+ };
1973
+ return /* @__PURE__ */ jsxs(
1974
+ "group",
1975
+ {
1976
+ position,
1977
+ scale: config.scale,
1978
+ onClick,
1979
+ onPointerEnter: () => onHover(true),
1980
+ onPointerLeave: () => onHover(false),
1981
+ userData: { type: "feature", featureId: feature.id, featureType: feature.type },
1982
+ children: [
1983
+ isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
1984
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
1985
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
1986
+ ] }),
1987
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
1988
+ /* @__PURE__ */ jsx("circleGeometry", { args: [0.35, 16] }),
1989
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
1990
+ ] }),
1991
+ renderGeometry()
1992
+ ]
1993
+ }
1994
+ );
1995
+ }
1996
+ function FeatureRenderer({
1997
+ features,
1998
+ cellSize = 1,
1999
+ offsetX = 0,
2000
+ offsetZ = 0,
2001
+ onFeatureClick,
2002
+ onFeatureHover,
2003
+ selectedFeatureIds = [],
2004
+ featureColors
2005
+ }) {
2006
+ return /* @__PURE__ */ jsx("group", { children: features.map((feature) => {
2007
+ const x = (feature.x - offsetX) * cellSize;
2008
+ const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
2009
+ const y = (feature.elevation ?? 0) * 0.1;
2010
+ const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
2011
+ return /* @__PURE__ */ jsx(
2012
+ FeatureVisual,
2013
+ {
2014
+ feature,
2015
+ position: [x, y, z],
2016
+ isSelected,
2017
+ onClick: () => onFeatureClick?.(feature),
2018
+ onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
2019
+ },
2020
+ feature.id ?? `feature-${feature.x}-${feature.y}`
2021
+ );
2022
+ }) });
2023
+ }
2024
+ function detectAssetRoot3(modelUrl) {
2025
+ const idx = modelUrl.indexOf("/3d/");
2026
+ if (idx !== -1) {
2027
+ return modelUrl.substring(0, idx + 4);
2028
+ }
2029
+ return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
2030
+ }
2031
+ function useGLTFModel2(url) {
2032
+ const [model, setModel] = useState(null);
2033
+ const [isLoading, setIsLoading] = useState(false);
2034
+ const [error, setError] = useState(null);
2035
+ useEffect(() => {
2036
+ if (!url) {
2037
+ setModel(null);
2038
+ return;
2039
+ }
2040
+ setIsLoading(true);
2041
+ setError(null);
2042
+ const assetRoot = detectAssetRoot3(url);
2043
+ const loader = new GLTFLoader$1();
2044
+ loader.setResourcePath(assetRoot);
2045
+ loader.load(
2046
+ url,
2047
+ (gltf) => {
2048
+ setModel(gltf.scene);
2049
+ setIsLoading(false);
2050
+ },
2051
+ void 0,
2052
+ (err) => {
2053
+ setError(err instanceof Error ? err : new Error(String(err)));
2054
+ setIsLoading(false);
2055
+ }
2056
+ );
2057
+ }, [url]);
2058
+ return { model, isLoading, error };
2059
+ }
2060
+ function FeatureModel({
2061
+ feature,
2062
+ position,
2063
+ isSelected,
2064
+ onClick,
2065
+ onHover
2066
+ }) {
2067
+ const groupRef = useRef(null);
2068
+ const { model: loadedModel, isLoading } = useGLTFModel2(feature.assetUrl);
2069
+ const model = useMemo(() => {
2070
+ if (!loadedModel) return null;
2071
+ const cloned = loadedModel.clone();
2072
+ cloned.scale.setScalar(0.3);
2073
+ cloned.traverse((child) => {
2074
+ if (child instanceof THREE6.Mesh) {
2075
+ child.castShadow = true;
2076
+ child.receiveShadow = true;
2077
+ }
2078
+ });
2079
+ return cloned;
2080
+ }, [loadedModel]);
2081
+ useFrame((state) => {
2082
+ if (groupRef.current) {
2083
+ const featureRotation = feature.rotation;
2084
+ const baseRotation = featureRotation !== void 0 ? featureRotation * Math.PI / 180 - Math.PI / 4 : -Math.PI / 4;
2085
+ const wobble = isSelected ? Math.sin(state.clock.elapsedTime * 2) * 0.1 : 0;
2086
+ groupRef.current.rotation.y = baseRotation + wobble;
2087
+ }
2088
+ });
2089
+ if (isLoading) {
2090
+ return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
2091
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
2092
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
2093
+ ] }) });
2094
+ }
2095
+ if (!model && !feature.assetUrl) {
2096
+ return /* @__PURE__ */ jsxs(
2097
+ "group",
2098
+ {
2099
+ position,
2100
+ onClick,
2101
+ onPointerEnter: () => onHover(true),
2102
+ onPointerLeave: () => onHover(false),
2103
+ userData: { type: "feature", featureId: feature.id, featureType: feature.type },
2104
+ children: [
2105
+ isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
2106
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
2107
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
2108
+ ] }),
2109
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.5, 0], children: [
2110
+ /* @__PURE__ */ jsx("boxGeometry", { args: [0.4, 0.4, 0.4] }),
2111
+ /* @__PURE__ */ jsx("meshStandardMaterial", { color: 8947848 })
2112
+ ] })
2113
+ ]
2114
+ }
2115
+ );
2116
+ }
2117
+ return /* @__PURE__ */ jsxs(
2118
+ "group",
2119
+ {
2120
+ ref: groupRef,
2121
+ position,
2122
+ onClick,
2123
+ onPointerEnter: () => onHover(true),
2124
+ onPointerLeave: () => onHover(false),
2125
+ userData: { type: "feature", featureId: feature.id, featureType: feature.type },
2126
+ children: [
2127
+ isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
2128
+ /* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
2129
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
2130
+ ] }),
2131
+ /* @__PURE__ */ jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
2132
+ /* @__PURE__ */ jsx("circleGeometry", { args: [0.35, 16] }),
2133
+ /* @__PURE__ */ jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
2134
+ ] }),
2135
+ model && /* @__PURE__ */ jsx("primitive", { object: model })
2136
+ ]
2137
+ }
2138
+ );
2139
+ }
2140
+ function FeatureRenderer3D({
2141
+ features,
2142
+ cellSize = 1,
2143
+ offsetX = 0,
2144
+ offsetZ = 0,
2145
+ onFeatureClick,
2146
+ onFeatureHover,
2147
+ selectedFeatureIds = []
2148
+ }) {
2149
+ return /* @__PURE__ */ jsx("group", { children: features.map((feature) => {
2150
+ const x = (feature.x - offsetX) * cellSize;
2151
+ const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
2152
+ const y = (feature.elevation ?? 0) * 0.1;
2153
+ const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
2154
+ return /* @__PURE__ */ jsx(
2155
+ FeatureModel,
2156
+ {
2157
+ feature,
2158
+ position: [x, y, z],
2159
+ isSelected,
2160
+ onClick: () => onFeatureClick?.(feature),
2161
+ onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
2162
+ },
2163
+ feature.id ?? `feature-${feature.x}-${feature.y}`
2164
+ );
2165
+ }) });
2166
+ }
2167
+ function preloadFeatures(urls) {
2168
+ urls.forEach((url) => {
2169
+ if (url) {
2170
+ const loader = new GLTFLoader$1();
2171
+ loader.setResourcePath(detectAssetRoot3(url));
2172
+ loader.load(url, () => {
2173
+ console.log("[FeatureRenderer3D] Preloaded:", url);
2174
+ });
2175
+ }
2176
+ });
2177
+ }
433
2178
  var DEFAULT_CONFIG = {
434
2179
  cellSize: 1,
435
2180
  offsetX: 0,
@@ -438,7 +2183,7 @@ var DEFAULT_CONFIG = {
438
2183
  };
439
2184
  function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
440
2185
  const opts = { ...DEFAULT_CONFIG, ...config };
441
- return new THREE.Vector3(
2186
+ return new THREE6.Vector3(
442
2187
  gridX * opts.cellSize + opts.offsetX,
443
2188
  opts.elevation,
444
2189
  gridZ * opts.cellSize + opts.offsetZ
@@ -452,17 +2197,17 @@ function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
452
2197
  };
453
2198
  }
454
2199
  function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
455
- const raycaster = new THREE.Raycaster();
456
- const mouse = new THREE.Vector2(mouseX, mouseY);
2200
+ const raycaster = new THREE6.Raycaster();
2201
+ const mouse = new THREE6.Vector2(mouseX, mouseY);
457
2202
  raycaster.setFromCamera(mouse, camera);
458
- const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -planeY);
459
- const target = new THREE.Vector3();
2203
+ const plane = new THREE6.Plane(new THREE6.Vector3(0, 1, 0), -planeY);
2204
+ const target = new THREE6.Vector3();
460
2205
  const intersection = raycaster.ray.intersectPlane(plane, target);
461
2206
  return intersection ? target : null;
462
2207
  }
463
2208
  function raycastToObjects(camera, mouseX, mouseY, objects) {
464
- const raycaster = new THREE.Raycaster();
465
- const mouse = new THREE.Vector2(mouseX, mouseY);
2209
+ const raycaster = new THREE6.Raycaster();
2210
+ const mouse = new THREE6.Vector2(mouseX, mouseY);
466
2211
  raycaster.setFromCamera(mouse, camera);
467
2212
  const intersects = raycaster.intersectObjects(objects, true);
468
2213
  return intersects.length > 0 ? intersects[0] : null;
@@ -514,14 +2259,14 @@ function getCellsInRadius(centerX, centerZ, radius) {
514
2259
  return cells;
515
2260
  }
516
2261
  function createGridHighlight(color = 16776960, opacity = 0.3) {
517
- const geometry = new THREE.PlaneGeometry(0.95, 0.95);
518
- const material = new THREE.MeshBasicMaterial({
2262
+ const geometry = new THREE6.PlaneGeometry(0.95, 0.95);
2263
+ const material = new THREE6.MeshBasicMaterial({
519
2264
  color,
520
2265
  transparent: true,
521
2266
  opacity,
522
- side: THREE.DoubleSide
2267
+ side: THREE6.DoubleSide
523
2268
  });
524
- const mesh = new THREE.Mesh(geometry, material);
2269
+ const mesh = new THREE6.Mesh(geometry, material);
525
2270
  mesh.rotation.x = -Math.PI / 2;
526
2271
  mesh.position.y = 0.01;
527
2272
  return mesh;
@@ -534,31 +2279,31 @@ function normalizeMouseCoordinates(clientX, clientY, element) {
534
2279
  };
535
2280
  }
536
2281
  function isInFrustum(position, camera, padding = 0) {
537
- const frustum = new THREE.Frustum();
538
- const projScreenMatrix = new THREE.Matrix4();
2282
+ const frustum = new THREE6.Frustum();
2283
+ const projScreenMatrix = new THREE6.Matrix4();
539
2284
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
540
2285
  frustum.setFromProjectionMatrix(projScreenMatrix);
541
- const sphere = new THREE.Sphere(position, padding);
2286
+ const sphere = new THREE6.Sphere(position, padding);
542
2287
  return frustum.intersectsSphere(sphere);
543
2288
  }
544
2289
  function filterByFrustum(positions, camera, padding = 0) {
545
- const frustum = new THREE.Frustum();
546
- const projScreenMatrix = new THREE.Matrix4();
2290
+ const frustum = new THREE6.Frustum();
2291
+ const projScreenMatrix = new THREE6.Matrix4();
547
2292
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
548
2293
  frustum.setFromProjectionMatrix(projScreenMatrix);
549
2294
  return positions.filter((position) => {
550
- const sphere = new THREE.Sphere(position, padding);
2295
+ const sphere = new THREE6.Sphere(position, padding);
551
2296
  return frustum.intersectsSphere(sphere);
552
2297
  });
553
2298
  }
554
2299
  function getVisibleIndices(positions, camera, padding = 0) {
555
- const frustum = new THREE.Frustum();
556
- const projScreenMatrix = new THREE.Matrix4();
2300
+ const frustum = new THREE6.Frustum();
2301
+ const projScreenMatrix = new THREE6.Matrix4();
557
2302
  const visible = /* @__PURE__ */ new Set();
558
2303
  projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
559
2304
  frustum.setFromProjectionMatrix(projScreenMatrix);
560
2305
  positions.forEach((position, index) => {
561
- const sphere = new THREE.Sphere(position, padding);
2306
+ const sphere = new THREE6.Sphere(position, padding);
562
2307
  if (frustum.intersectsSphere(sphere)) {
563
2308
  visible.add(index);
564
2309
  }
@@ -582,7 +2327,7 @@ function updateInstanceLOD(instancedMesh, positions, camera, lodDistances) {
582
2327
  return lodIndices;
583
2328
  }
584
2329
  function cullInstancedMesh(instancedMesh, positions, visibleIndices) {
585
- const dummy = new THREE.Object3D();
2330
+ const dummy = new THREE6.Object3D();
586
2331
  let visibleCount = 0;
587
2332
  positions.forEach((position, index) => {
588
2333
  if (visibleIndices.has(index)) {
@@ -715,4 +2460,4 @@ var SpatialHashGrid = class {
715
2460
  }
716
2461
  };
717
2462
 
718
- export { SpatialHashGrid, calculateLODLevel, createGridHighlight, cullInstancedMesh, filterByFrustum, getCellsInRadius, getNeighbors, getVisibleIndices, gridDistance, gridManhattanDistance, gridToWorld, isInBounds, isInFrustum, normalizeMouseCoordinates, raycastToObjects, raycastToPlane, updateInstanceLOD, useRaycaster, useSceneGraph, useThree, worldToGrid };
2463
+ export { AssetLoader, Camera3D, Canvas3DErrorBoundary, Canvas3DLoadingState, FeatureRenderer, FeatureRenderer3D, Lighting3D, ModelLoader, PhysicsObject3D, Scene3D, SpatialHashGrid, TileRenderer, UnitRenderer, assetLoader, calculateLODLevel, createGridHighlight, cullInstancedMesh, filterByFrustum, getCellsInRadius, getNeighbors, getVisibleIndices, gridDistance, gridManhattanDistance, gridToWorld, isInBounds, isInFrustum, normalizeMouseCoordinates, preloadFeatures, raycastToObjects, raycastToPlane, updateInstanceLOD, useAssetLoader, useGameCanvas3DEvents, usePhysics3DController, useRaycaster, useSceneGraph, useThree3 as useThree, worldToGrid };