@babylonjs/core 8.47.0 → 8.47.1

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 (161) hide show
  1. package/Behaviors/Cameras/geospatialClippingBehavior.d.ts +39 -0
  2. package/Behaviors/Cameras/geospatialClippingBehavior.js +75 -0
  3. package/Behaviors/Cameras/geospatialClippingBehavior.js.map +1 -0
  4. package/Behaviors/Cameras/index.d.ts +2 -0
  5. package/Behaviors/Cameras/index.js +2 -0
  6. package/Behaviors/Cameras/index.js.map +1 -1
  7. package/Cameras/Inputs/geospatialCameraPointersInput.js +6 -4
  8. package/Cameras/Inputs/geospatialCameraPointersInput.js.map +1 -1
  9. package/Cameras/Limits/geospatialLimits.d.ts +25 -1
  10. package/Cameras/Limits/geospatialLimits.js +60 -3
  11. package/Cameras/Limits/geospatialLimits.js.map +1 -1
  12. package/Cameras/geospatialCamera.d.ts +33 -9
  13. package/Cameras/geospatialCamera.js +114 -42
  14. package/Cameras/geospatialCamera.js.map +1 -1
  15. package/Cameras/geospatialCameraMovement.d.ts +1 -3
  16. package/Cameras/geospatialCameraMovement.js +10 -4
  17. package/Cameras/geospatialCameraMovement.js.map +1 -1
  18. package/Debug/debugLayer.js +11 -3
  19. package/Debug/debugLayer.js.map +1 -1
  20. package/Engines/abstractEngine.js +2 -2
  21. package/Engines/abstractEngine.js.map +1 -1
  22. package/Engines/thinEngine.d.ts +3 -2
  23. package/Engines/thinEngine.js.map +1 -1
  24. package/IAssetContainer.d.ts +5 -0
  25. package/IAssetContainer.js.map +1 -1
  26. package/Lights/Clustered/clusteredLightContainer.js +1 -1
  27. package/Lights/Clustered/clusteredLightContainer.js.map +1 -1
  28. package/Loading/Plugins/babylonFileLoader.js +2 -0
  29. package/Loading/Plugins/babylonFileLoader.js.map +1 -1
  30. package/Materials/Node/Blocks/Fragment/fragmentOutputBlock.js +1 -1
  31. package/Materials/Node/Blocks/Fragment/fragmentOutputBlock.js.map +1 -1
  32. package/Materials/PBR/openpbrMaterial.d.ts +135 -5
  33. package/Materials/PBR/openpbrMaterial.js +205 -22
  34. package/Materials/PBR/openpbrMaterial.js.map +1 -1
  35. package/Materials/effectRenderer.d.ts +2 -1
  36. package/Materials/effectRenderer.js +2 -12
  37. package/Materials/effectRenderer.js.map +1 -1
  38. package/Materials/materialHelper.functions.js +1 -0
  39. package/Materials/materialHelper.functions.js.map +1 -1
  40. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +6 -1
  41. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +21 -17
  42. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  43. package/Misc/sceneRecorder.d.ts +6 -2
  44. package/Misc/sceneRecorder.js +7 -0
  45. package/Misc/sceneRecorder.js.map +1 -1
  46. package/Particles/Node/Blocks/index.d.ts +3 -0
  47. package/Particles/Node/Blocks/index.js +3 -0
  48. package/Particles/Node/Blocks/index.js.map +1 -1
  49. package/Particles/Node/Blocks/particleNLerpBlock.d.ts +35 -0
  50. package/Particles/Node/Blocks/particleNLerpBlock.js +97 -0
  51. package/Particles/Node/Blocks/particleNLerpBlock.js.map +1 -0
  52. package/Particles/Node/Blocks/particleSmoothStepBlock.d.ts +34 -0
  53. package/Particles/Node/Blocks/particleSmoothStepBlock.js +91 -0
  54. package/Particles/Node/Blocks/particleSmoothStepBlock.js.map +1 -0
  55. package/Particles/Node/Blocks/particleStepBlock.d.ts +30 -0
  56. package/Particles/Node/Blocks/particleStepBlock.js +84 -0
  57. package/Particles/Node/Blocks/particleStepBlock.js.map +1 -0
  58. package/Particles/solidParticleSystem.d.ts +3 -0
  59. package/Particles/solidParticleSystem.js +7 -4
  60. package/Particles/solidParticleSystem.js.map +1 -1
  61. package/Rendering/IBLShadows/iblShadowsPluginMaterial.js +8 -10
  62. package/Rendering/IBLShadows/iblShadowsPluginMaterial.js.map +1 -1
  63. package/Shaders/ShadersInclude/hdrFilteringFunctions.js +7 -9
  64. package/Shaders/ShadersInclude/hdrFilteringFunctions.js.map +1 -1
  65. package/Shaders/ShadersInclude/helperFunctions.js +4 -0
  66. package/Shaders/ShadersInclude/helperFunctions.js.map +1 -1
  67. package/Shaders/ShadersInclude/{openpbrBlockAmbientOcclusion.d.ts → openpbrAmbientOcclusionData.d.ts} +1 -1
  68. package/Shaders/ShadersInclude/openpbrAmbientOcclusionData.js +15 -0
  69. package/Shaders/ShadersInclude/openpbrAmbientOcclusionData.js.map +1 -0
  70. package/{ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.d.ts → Shaders/ShadersInclude/openpbrAmbientOcclusionFunctions.d.ts} +1 -1
  71. package/Shaders/ShadersInclude/openpbrAmbientOcclusionFunctions.js +13 -0
  72. package/Shaders/ShadersInclude/openpbrAmbientOcclusionFunctions.js.map +1 -0
  73. package/Shaders/ShadersInclude/openpbrBackgroundTransmission.d.ts +5 -0
  74. package/Shaders/ShadersInclude/openpbrBackgroundTransmission.js +26 -0
  75. package/Shaders/ShadersInclude/openpbrBackgroundTransmission.js.map +1 -0
  76. package/Shaders/ShadersInclude/openpbrBaseLayerData.js +18 -7
  77. package/Shaders/ShadersInclude/openpbrBaseLayerData.js.map +1 -1
  78. package/Shaders/ShadersInclude/openpbrDirectLighting.js +52 -6
  79. package/Shaders/ShadersInclude/openpbrDirectLighting.js.map +1 -1
  80. package/Shaders/ShadersInclude/openpbrEnvironmentLighting.js +109 -6
  81. package/Shaders/ShadersInclude/openpbrEnvironmentLighting.js.map +1 -1
  82. package/Shaders/ShadersInclude/openpbrFragmentDeclaration.d.ts +1 -0
  83. package/Shaders/ShadersInclude/openpbrFragmentDeclaration.js +24 -4
  84. package/Shaders/ShadersInclude/openpbrFragmentDeclaration.js.map +1 -1
  85. package/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.js +10 -1
  86. package/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.js.map +1 -1
  87. package/Shaders/ShadersInclude/openpbrIblFunctions.js +9 -2
  88. package/Shaders/ShadersInclude/openpbrIblFunctions.js.map +1 -1
  89. package/Shaders/ShadersInclude/openpbrThinFilmLayerData.js +1 -0
  90. package/Shaders/ShadersInclude/openpbrThinFilmLayerData.js.map +1 -1
  91. package/Shaders/ShadersInclude/openpbrTransmissionLayerData.d.ts +5 -0
  92. package/Shaders/ShadersInclude/openpbrTransmissionLayerData.js +47 -0
  93. package/Shaders/ShadersInclude/openpbrTransmissionLayerData.js.map +1 -0
  94. package/Shaders/ShadersInclude/openpbrUboDeclaration.js +1 -1
  95. package/Shaders/ShadersInclude/openpbrUboDeclaration.js.map +1 -1
  96. package/Shaders/ShadersInclude/openpbrVertexDeclaration.d.ts +1 -0
  97. package/Shaders/ShadersInclude/openpbrVertexDeclaration.js +17 -4
  98. package/Shaders/ShadersInclude/openpbrVertexDeclaration.js.map +1 -1
  99. package/Shaders/ShadersInclude/pbrDirectLightingFunctions.js +1 -1
  100. package/Shaders/ShadersInclude/pbrDirectLightingFunctions.js.map +1 -1
  101. package/Shaders/ShadersInclude/pbrHelperFunctions.js +1 -1
  102. package/Shaders/ShadersInclude/pbrHelperFunctions.js.map +1 -1
  103. package/Shaders/openpbr.fragment.d.ts +4 -1
  104. package/Shaders/openpbr.fragment.js +33 -15
  105. package/Shaders/openpbr.fragment.js.map +1 -1
  106. package/Shaders/openpbr.vertex.js +30 -18
  107. package/Shaders/openpbr.vertex.js.map +1 -1
  108. package/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.js +10 -12
  109. package/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.js.map +1 -1
  110. package/ShadersWGSL/ShadersInclude/helperFunctions.js +4 -0
  111. package/ShadersWGSL/ShadersInclude/helperFunctions.js.map +1 -1
  112. package/ShadersWGSL/ShadersInclude/importanceSampling.js +1 -1
  113. package/ShadersWGSL/ShadersInclude/importanceSampling.js.map +1 -1
  114. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionData.d.ts +5 -0
  115. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionData.js +15 -0
  116. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionData.js.map +1 -0
  117. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionFunctions.d.ts +5 -0
  118. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionFunctions.js +13 -0
  119. package/ShadersWGSL/ShadersInclude/openpbrAmbientOcclusionFunctions.js.map +1 -0
  120. package/ShadersWGSL/ShadersInclude/openpbrBackgroundTransmission.d.ts +5 -0
  121. package/ShadersWGSL/ShadersInclude/openpbrBackgroundTransmission.js +25 -0
  122. package/ShadersWGSL/ShadersInclude/openpbrBackgroundTransmission.js.map +1 -0
  123. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js +18 -7
  124. package/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.js.map +1 -1
  125. package/ShadersWGSL/ShadersInclude/openpbrDirectLighting.js +52 -6
  126. package/ShadersWGSL/ShadersInclude/openpbrDirectLighting.js.map +1 -1
  127. package/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.js +115 -7
  128. package/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.js.map +1 -1
  129. package/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.js +10 -1
  130. package/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.js.map +1 -1
  131. package/ShadersWGSL/ShadersInclude/openpbrFuzzLayerData.js +4 -4
  132. package/ShadersWGSL/ShadersInclude/openpbrFuzzLayerData.js.map +1 -1
  133. package/ShadersWGSL/ShadersInclude/openpbrIblFunctions.js +10 -3
  134. package/ShadersWGSL/ShadersInclude/openpbrIblFunctions.js.map +1 -1
  135. package/ShadersWGSL/ShadersInclude/openpbrThinFilmLayerData.js +1 -0
  136. package/ShadersWGSL/ShadersInclude/openpbrThinFilmLayerData.js.map +1 -1
  137. package/ShadersWGSL/ShadersInclude/openpbrTransmissionLayerData.d.ts +5 -0
  138. package/ShadersWGSL/ShadersInclude/openpbrTransmissionLayerData.js +47 -0
  139. package/ShadersWGSL/ShadersInclude/openpbrTransmissionLayerData.js.map +1 -0
  140. package/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.js +1 -1
  141. package/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.js.map +1 -1
  142. package/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.js +1 -1
  143. package/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.js.map +1 -1
  144. package/ShadersWGSL/ShadersInclude/pbrHelperFunctions.js +1 -1
  145. package/ShadersWGSL/ShadersInclude/pbrHelperFunctions.js.map +1 -1
  146. package/ShadersWGSL/openpbr.fragment.d.ts +4 -1
  147. package/ShadersWGSL/openpbr.fragment.js +33 -15
  148. package/ShadersWGSL/openpbr.fragment.js.map +1 -1
  149. package/ShadersWGSL/openpbr.vertex.js +12 -0
  150. package/ShadersWGSL/openpbr.vertex.js.map +1 -1
  151. package/Sprites/spriteManager.d.ts +3 -0
  152. package/Sprites/spriteManager.js +9 -0
  153. package/Sprites/spriteManager.js.map +1 -1
  154. package/assetContainer.d.ts +5 -0
  155. package/assetContainer.js +32 -0
  156. package/assetContainer.js.map +1 -1
  157. package/package.json +2 -2
  158. package/Shaders/ShadersInclude/openpbrBlockAmbientOcclusion.js +0 -35
  159. package/Shaders/ShadersInclude/openpbrBlockAmbientOcclusion.js.map +0 -1
  160. package/ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.js +0 -36
  161. package/ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.js.map +0 -1
@@ -0,0 +1,39 @@
1
+ import type { Behavior } from "../../Behaviors/behavior.js";
2
+ import type { GeospatialCamera } from "../../Cameras/geospatialCamera.js";
3
+ import type { Nullable } from "../../types.js";
4
+ /**
5
+ * The GeospatialClippingBehavior automatically adjusts the near and far clip planes of a GeospatialCamera
6
+ * based on altitude to optimize depth buffer precision for geospatial applications.
7
+ *
8
+ * The near plane scales with altitude (distance to planet surface) to maintain good depth precision.
9
+ * The far plane is calculated based on the visible horizon distance.
10
+ */
11
+ export declare class GeospatialClippingBehavior implements Behavior<GeospatialCamera> {
12
+ /**
13
+ * Gets the name of the behavior.
14
+ */
15
+ get name(): string;
16
+ private _attachedCamera;
17
+ private _onBeforeRenderObserver;
18
+ /**
19
+ * Gets the attached camera.
20
+ */
21
+ get attachedNode(): Nullable<GeospatialCamera>;
22
+ /**
23
+ * Initializes the behavior.
24
+ */
25
+ init(): void;
26
+ /**
27
+ * Attaches the behavior to its geospatial camera.
28
+ * @param camera Defines the camera to attach the behavior to
29
+ */
30
+ attach(camera: GeospatialCamera): void;
31
+ /**
32
+ * Detaches the behavior from its current geospatial camera.
33
+ */
34
+ detach(): void;
35
+ /**
36
+ * Updates the camera's near and far clip planes based on altitude.
37
+ */
38
+ private _updateCameraClipPlanes;
39
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The GeospatialClippingBehavior automatically adjusts the near and far clip planes of a GeospatialCamera
3
+ * based on altitude to optimize depth buffer precision for geospatial applications.
4
+ *
5
+ * The near plane scales with altitude (distance to planet surface) to maintain good depth precision.
6
+ * The far plane is calculated based on the visible horizon distance.
7
+ */
8
+ export class GeospatialClippingBehavior {
9
+ constructor() {
10
+ this._attachedCamera = null;
11
+ this._onBeforeRenderObserver = null;
12
+ }
13
+ /**
14
+ * Gets the name of the behavior.
15
+ */
16
+ get name() {
17
+ return "GeospatialClipping";
18
+ }
19
+ /**
20
+ * Gets the attached camera.
21
+ */
22
+ get attachedNode() {
23
+ return this._attachedCamera;
24
+ }
25
+ /**
26
+ * Initializes the behavior.
27
+ */
28
+ init() {
29
+ // Do nothing
30
+ }
31
+ /**
32
+ * Attaches the behavior to its geospatial camera.
33
+ * @param camera Defines the camera to attach the behavior to
34
+ */
35
+ attach(camera) {
36
+ this._attachedCamera = camera;
37
+ const scene = camera.getScene();
38
+ this._onBeforeRenderObserver = scene.onBeforeRenderObservable.add(() => {
39
+ this._updateCameraClipPlanes();
40
+ });
41
+ }
42
+ /**
43
+ * Detaches the behavior from its current geospatial camera.
44
+ */
45
+ detach() {
46
+ if (this._attachedCamera) {
47
+ const scene = this._attachedCamera.getScene();
48
+ if (this._onBeforeRenderObserver) {
49
+ scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
50
+ this._onBeforeRenderObserver = null;
51
+ }
52
+ }
53
+ this._attachedCamera = null;
54
+ }
55
+ /**
56
+ * Updates the camera's near and far clip planes based on altitude.
57
+ */
58
+ _updateCameraClipPlanes() {
59
+ const camera = this._attachedCamera;
60
+ if (!camera) {
61
+ return;
62
+ }
63
+ const planetRadius = camera.limits.planetRadius;
64
+ // Camera position length gives distance to world origin (planet center)
65
+ const altitude = Math.max(1, camera.position.length() - planetRadius);
66
+ // Near plane: scale with altitude to maintain depth buffer precision
67
+ // Use a fraction of altitude - the closest visible point on a sphere is straight down at distance = altitude
68
+ camera.minZ = Math.max(1, altitude * 0.001);
69
+ // Far plane: see to the horizon and beyond
70
+ // Horizon distance formula: √(2Rh + h²) where h is altitude above surface
71
+ const horizonDist = Math.sqrt(2 * planetRadius * altitude + altitude * altitude);
72
+ camera.maxZ = horizonDist + planetRadius * 0.1;
73
+ }
74
+ }
75
+ //# sourceMappingURL=geospatialClippingBehavior.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geospatialClippingBehavior.js","sourceRoot":"","sources":["../../../../../dev/core/src/Behaviors/Cameras/geospatialClippingBehavior.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,OAAO,0BAA0B;IAAvC;QAQY,oBAAe,GAA+B,IAAI,CAAC;QACnD,4BAAuB,GAA8B,IAAI,CAAC;IAiEtE,CAAC;IAzEG;;OAEG;IACH,IAAW,IAAI;QACX,OAAO,oBAAoB,CAAC;IAChC,CAAC;IAKD;;OAEG;IACH,IAAW,YAAY;QACnB,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED;;OAEG;IACI,IAAI;QACP,aAAa;IACjB,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,MAAwB;QAClC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QAEhC,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC,wBAAwB,CAAC,GAAG,CAAC,GAAG,EAAE;YACnE,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACI,MAAM;QACT,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAC/B,KAAK,CAAC,wBAAwB,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;gBACpE,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;YACxC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAChC,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO;QACX,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;QAChD,wEAAwE;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,YAAY,CAAC,CAAC;QAEtE,qEAAqE;QACrE,6GAA6G;QAC7G,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;QAE5C,2CAA2C;QAC3C,0EAA0E;QAC1E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;QACjF,MAAM,CAAC,IAAI,GAAG,WAAW,GAAG,YAAY,GAAG,GAAG,CAAC;IACnD,CAAC;CACJ","sourcesContent":["import type { Behavior } from \"../../Behaviors/behavior\";\r\nimport type { GeospatialCamera } from \"../../Cameras/geospatialCamera\";\r\nimport type { Nullable } from \"../../types\";\r\nimport type { Observer } from \"../../Misc/observable\";\r\nimport type { Scene } from \"../../scene\";\r\n\r\n/**\r\n * The GeospatialClippingBehavior automatically adjusts the near and far clip planes of a GeospatialCamera\r\n * based on altitude to optimize depth buffer precision for geospatial applications.\r\n *\r\n * The near plane scales with altitude (distance to planet surface) to maintain good depth precision.\r\n * The far plane is calculated based on the visible horizon distance.\r\n */\r\nexport class GeospatialClippingBehavior implements Behavior<GeospatialCamera> {\r\n /**\r\n * Gets the name of the behavior.\r\n */\r\n public get name(): string {\r\n return \"GeospatialClipping\";\r\n }\r\n\r\n private _attachedCamera: Nullable<GeospatialCamera> = null;\r\n private _onBeforeRenderObserver: Nullable<Observer<Scene>> = null;\r\n\r\n /**\r\n * Gets the attached camera.\r\n */\r\n public get attachedNode(): Nullable<GeospatialCamera> {\r\n return this._attachedCamera;\r\n }\r\n\r\n /**\r\n * Initializes the behavior.\r\n */\r\n public init(): void {\r\n // Do nothing\r\n }\r\n\r\n /**\r\n * Attaches the behavior to its geospatial camera.\r\n * @param camera Defines the camera to attach the behavior to\r\n */\r\n public attach(camera: GeospatialCamera): void {\r\n this._attachedCamera = camera;\r\n const scene = camera.getScene();\r\n\r\n this._onBeforeRenderObserver = scene.onBeforeRenderObservable.add(() => {\r\n this._updateCameraClipPlanes();\r\n });\r\n }\r\n\r\n /**\r\n * Detaches the behavior from its current geospatial camera.\r\n */\r\n public detach(): void {\r\n if (this._attachedCamera) {\r\n const scene = this._attachedCamera.getScene();\r\n if (this._onBeforeRenderObserver) {\r\n scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);\r\n this._onBeforeRenderObserver = null;\r\n }\r\n }\r\n this._attachedCamera = null;\r\n }\r\n\r\n /**\r\n * Updates the camera's near and far clip planes based on altitude.\r\n */\r\n private _updateCameraClipPlanes(): void {\r\n const camera = this._attachedCamera;\r\n if (!camera) {\r\n return;\r\n }\r\n\r\n const planetRadius = camera.limits.planetRadius;\r\n // Camera position length gives distance to world origin (planet center)\r\n const altitude = Math.max(1, camera.position.length() - planetRadius);\r\n\r\n // Near plane: scale with altitude to maintain depth buffer precision\r\n // Use a fraction of altitude - the closest visible point on a sphere is straight down at distance = altitude\r\n camera.minZ = Math.max(1, altitude * 0.001);\r\n\r\n // Far plane: see to the horizon and beyond\r\n // Horizon distance formula: √(2Rh + h²) where h is altitude above surface\r\n const horizonDist = Math.sqrt(2 * planetRadius * altitude + altitude * altitude);\r\n camera.maxZ = horizonDist + planetRadius * 0.1;\r\n }\r\n}\r\n"]}
@@ -1,3 +1,5 @@
1
1
  export * from "./autoRotationBehavior.js";
2
2
  export * from "./bouncingBehavior.js";
3
3
  export * from "./framingBehavior.js";
4
+ export * from "./interpolatingBehavior.js";
5
+ export * from "./geospatialClippingBehavior.js";
@@ -1,4 +1,6 @@
1
1
  export * from "./autoRotationBehavior.js";
2
2
  export * from "./bouncingBehavior.js";
3
3
  export * from "./framingBehavior.js";
4
+ export * from "./interpolatingBehavior.js";
5
+ export * from "./geospatialClippingBehavior.js";
4
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../dev/core/src/Behaviors/Cameras/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC","sourcesContent":["export * from \"./autoRotationBehavior\";\r\nexport * from \"./bouncingBehavior\";\r\nexport * from \"./framingBehavior\";\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../dev/core/src/Behaviors/Cameras/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC","sourcesContent":["export * from \"./autoRotationBehavior\";\r\nexport * from \"./bouncingBehavior\";\r\nexport * from \"./framingBehavior\";\r\nexport * from \"./interpolatingBehavior\";\r\nexport * from \"./geospatialClippingBehavior\";\r\n"]}
@@ -67,19 +67,21 @@ export class GeospatialCameraPointersInput extends OrbitCameraPointersInput {
67
67
  const canvasX = this._pinchCentroid.x - canvasRect.left;
68
68
  const canvasY = this._pinchCentroid.y - canvasRect.top;
69
69
  // Pick at centroid
70
- const pickResult = scene.pick(canvasX, canvasY, this.camera.pickPredicate);
70
+ const pickResult = scene.pick(canvasX, canvasY, this.camera.movement.pickPredicate);
71
71
  if (pickResult?.pickedPoint) {
72
72
  // Scale zoom by distance to picked point
73
73
  const distanceToPoint = this.camera.position.subtract(pickResult.pickedPoint).length();
74
74
  const zoomDistance = pinchDelta * distanceToPoint * 0.005;
75
- this.camera.zoomToPoint(pickResult.pickedPoint, zoomDistance);
75
+ const clampedZoom = this.camera.limits.clampZoomDistance(zoomDistance, this.camera.radius, distanceToPoint);
76
+ this.camera.zoomToPoint(pickResult.pickedPoint, clampedZoom);
76
77
  return;
77
78
  }
78
79
  }
79
80
  }
80
81
  // Fallback: scale zoom by camera radius along lookat vector
81
82
  const zoomDistance = pinchDelta * this.camera.radius * 0.005;
82
- this.camera.zoomAlongLookAt(zoomDistance);
83
+ const clampedZoom = this.camera.limits.clampZoomDistance(zoomDistance, this.camera.radius);
84
+ this.camera.zoomAlongLookAt(clampedZoom);
83
85
  }
84
86
  /**
85
87
  * Move camera from multi touch panning positions.
@@ -95,7 +97,7 @@ export class GeospatialCameraPointersInput extends OrbitCameraPointersInput {
95
97
  }
96
98
  }
97
99
  onDoubleTap(type) {
98
- const pickResult = this.camera._scene.pick(this.camera._scene.pointerX, this.camera._scene.pointerY, this.camera.pickPredicate);
100
+ const pickResult = this.camera._scene.pick(this.camera._scene.pointerX, this.camera._scene.pointerY, this.camera.movement.pickPredicate);
99
101
  if (pickResult.pickedPoint) {
100
102
  void this.camera.flyToPointAsync(pickResult.pickedPoint);
101
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"geospatialCameraPointersInput.js","sourceRoot":"","sources":["../../../../../dev/core/src/Cameras/Inputs/geospatialCameraPointersInput.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GeospatialCamera } from \"../../Cameras/geospatialCamera\";\r\nimport type { IPointerEvent } from \"../../Events/deviceInputEvents\";\r\nimport type { PointerTouch } from \"../../Events/pointerEvents\";\r\nimport type { Nullable } from \"../../types\";\r\nimport { OrbitCameraPointersInput } from \"./orbitCameraPointersInput\";\r\n\r\n/**\r\n * @experimental\r\n * Geospatial camera inputs can simulate dragging the globe around or tilting the camera around some point on the globe\r\n * This class will update the GeospatialCameraMovement class's movementDeltaCurrentFrame, and the camera is responsible for using these updates to calculate viewMatrix appropriately\r\n *\r\n * As of right now, the camera correction logic (to keep the camera geospatially oriented around the globe) is happening within the camera class when calculating viewmatrix\r\n * As this is experimental, it is possible we move that correction step to live within the input class (to enable non-corrected translations in the future), say if we want to allow the camera to move outside of the globe's orbit\r\n *\r\n * Left mouse button: drag globe\r\n * Middle mouse button: tilt globe\r\n * Right mouse button: tilt globe\r\n *\r\n */\r\nexport class GeospatialCameraPointersInput extends OrbitCameraPointersInput {\r\n public camera: GeospatialCamera;\r\n\r\n private _initialPinchSquaredDistance: number = 0;\r\n private _pinchCentroid: Nullable<PointerTouch> = null;\r\n\r\n public override getClassName(): string {\r\n return \"GeospatialCameraPointersInput\";\r\n }\r\n\r\n public override onButtonDown(evt: IPointerEvent): void {\r\n this.camera.movement.activeInput = true;\r\n const scene = this.camera.getScene();\r\n switch (evt.button) {\r\n case 0: // Left button - drag/pan globe under cursor\r\n this.camera.movement.startDrag(scene.pointerX, scene.pointerY);\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n public override onTouch(point: Nullable<PointerTouch>, offsetX: number, offsetY: number): void {\r\n // Single finger touch (no button property) or left button (button 0) = drag\r\n const button = point?.button ?? 0; // Default to button 0 (drag) if undefined\r\n const scene = this.camera.getScene();\r\n switch (button) {\r\n case 0: // Left button / single touch - drag/pan globe under cursor\r\n this.camera.movement.handleDrag(scene.pointerX, scene.pointerY);\r\n break;\r\n case 1: // Middle button - tilt camera\r\n case 2: // Right button - tilt camera\r\n this._handleTilt(offsetX, offsetY);\r\n break;\r\n }\r\n }\r\n\r\n /**\r\n * Move camera from multitouch (pinch) zoom distances.\r\n * Zooms towards the centroid (midpoint between the two fingers).\r\n * @param previousPinchSquaredDistance\r\n * @param pinchSquaredDistance\r\n */\r\n protected override _computePinchZoom(previousPinchSquaredDistance: number, pinchSquaredDistance: number): void {\r\n // Calculate zoom distance based on pinch delta\r\n const previousDistance = Math.sqrt(previousPinchSquaredDistance);\r\n const currentDistance = Math.sqrt(pinchSquaredDistance);\r\n const pinchDelta = currentDistance - previousDistance;\r\n\r\n // Try to zoom towards centroid if we have it\r\n if (this._pinchCentroid) {\r\n const scene = this.camera.getScene();\r\n const engine = scene.getEngine();\r\n const canvasRect = engine.getInputElementClientRect();\r\n\r\n if (canvasRect) {\r\n // Convert centroid from clientX/Y to canvas-relative coordinates (same as scene.pointerX/Y)\r\n const canvasX = this._pinchCentroid.x - canvasRect.left;\r\n const canvasY = this._pinchCentroid.y - canvasRect.top;\r\n\r\n // Pick at centroid\r\n const pickResult = scene.pick(canvasX, canvasY, this.camera.pickPredicate);\r\n if (pickResult?.pickedPoint) {\r\n // Scale zoom by distance to picked point\r\n const distanceToPoint = this.camera.position.subtract(pickResult.pickedPoint).length();\r\n const zoomDistance = pinchDelta * distanceToPoint * 0.005;\r\n this.camera.zoomToPoint(pickResult.pickedPoint, zoomDistance);\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Fallback: scale zoom by camera radius along lookat vector\r\n const zoomDistance = pinchDelta * this.camera.radius * 0.005;\r\n this.camera.zoomAlongLookAt(zoomDistance);\r\n }\r\n\r\n /**\r\n * Move camera from multi touch panning positions.\r\n * In geospatialcamera, multi touch panning tilts the globe (whereas single touch will pan/drag it)\r\n * @param previousMultiTouchPanPosition\r\n * @param multiTouchPanPosition\r\n */\r\n protected override _computeMultiTouchPanning(previousMultiTouchPanPosition: Nullable<PointerTouch>, multiTouchPanPosition: Nullable<PointerTouch>): void {\r\n if (previousMultiTouchPanPosition && multiTouchPanPosition) {\r\n const moveDeltaX = multiTouchPanPosition.x - previousMultiTouchPanPosition.x;\r\n const moveDeltaY = multiTouchPanPosition.y - previousMultiTouchPanPosition.y;\r\n this._handleTilt(moveDeltaX, moveDeltaY);\r\n }\r\n }\r\n\r\n public override onDoubleTap(type: string): void {\r\n const pickResult = this.camera._scene.pick(this.camera._scene.pointerX, this.camera._scene.pointerY, this.camera.pickPredicate);\r\n if (pickResult.pickedPoint) {\r\n void this.camera.flyToPointAsync(pickResult.pickedPoint);\r\n }\r\n }\r\n\r\n public override onMultiTouch(\r\n pointA: Nullable<PointerTouch>,\r\n pointB: Nullable<PointerTouch>,\r\n previousPinchSquaredDistance: number,\r\n pinchSquaredDistance: number,\r\n previousMultiTouchPanPosition: Nullable<PointerTouch>,\r\n multiTouchPanPosition: Nullable<PointerTouch>\r\n ): void {\r\n // Store centroid for use in _computePinchZoom (it's already calculated by parent)\r\n this._pinchCentroid = multiTouchPanPosition;\r\n\r\n // Reset on gesture end\r\n if (pinchSquaredDistance === 0 && multiTouchPanPosition === null) {\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onMultiTouch(pointA, pointB, previousPinchSquaredDistance, pinchSquaredDistance, previousMultiTouchPanPosition, multiTouchPanPosition);\r\n return;\r\n }\r\n\r\n // Track initial distance at gesture start for cumulative threshold detection\r\n if (this._initialPinchSquaredDistance === 0 && pinchSquaredDistance !== 0) {\r\n this._initialPinchSquaredDistance = pinchSquaredDistance;\r\n }\r\n\r\n // Use cumulative delta from gesture start for threshold detection (more forgiving than frame-to-frame)\r\n const cumulativeDelta = Math.abs(Math.sqrt(pinchSquaredDistance) - Math.sqrt(this._initialPinchSquaredDistance));\r\n this._shouldStartPinchZoom = this._twoFingerActivityCount < 20 && cumulativeDelta > this.camera.limits.pinchToPanMax;\r\n\r\n super.onMultiTouch(pointA, pointB, previousPinchSquaredDistance, pinchSquaredDistance, previousMultiTouchPanPosition, multiTouchPanPosition);\r\n }\r\n\r\n public override onButtonUp(_evt: IPointerEvent): void {\r\n this.camera.movement.stopDrag();\r\n this.camera.movement.activeInput = false;\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onButtonUp(_evt);\r\n }\r\n\r\n public override onLostFocus(): void {\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onLostFocus();\r\n }\r\n\r\n private _handleTilt(deltaX: number, deltaY: number): void {\r\n this.camera.movement.rotationAccumulatedPixels.y -= deltaX; // yaw - looking side to side\r\n this.camera.movement.rotationAccumulatedPixels.x -= deltaY; // pitch - look up towards sky / down towards ground\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"geospatialCameraPointersInput.js","sourceRoot":"","sources":["../../../../../dev/core/src/Cameras/Inputs/geospatialCameraPointersInput.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GeospatialCamera } from \"../../Cameras/geospatialCamera\";\r\nimport type { IPointerEvent } from \"../../Events/deviceInputEvents\";\r\nimport type { PointerTouch } from \"../../Events/pointerEvents\";\r\nimport type { Nullable } from \"../../types\";\r\nimport { OrbitCameraPointersInput } from \"./orbitCameraPointersInput\";\r\n\r\n/**\r\n * @experimental\r\n * Geospatial camera inputs can simulate dragging the globe around or tilting the camera around some point on the globe\r\n * This class will update the GeospatialCameraMovement class's movementDeltaCurrentFrame, and the camera is responsible for using these updates to calculate viewMatrix appropriately\r\n *\r\n * As of right now, the camera correction logic (to keep the camera geospatially oriented around the globe) is happening within the camera class when calculating viewmatrix\r\n * As this is experimental, it is possible we move that correction step to live within the input class (to enable non-corrected translations in the future), say if we want to allow the camera to move outside of the globe's orbit\r\n *\r\n * Left mouse button: drag globe\r\n * Middle mouse button: tilt globe\r\n * Right mouse button: tilt globe\r\n *\r\n */\r\nexport class GeospatialCameraPointersInput extends OrbitCameraPointersInput {\r\n public camera: GeospatialCamera;\r\n\r\n private _initialPinchSquaredDistance: number = 0;\r\n private _pinchCentroid: Nullable<PointerTouch> = null;\r\n\r\n public override getClassName(): string {\r\n return \"GeospatialCameraPointersInput\";\r\n }\r\n\r\n public override onButtonDown(evt: IPointerEvent): void {\r\n this.camera.movement.activeInput = true;\r\n const scene = this.camera.getScene();\r\n switch (evt.button) {\r\n case 0: // Left button - drag/pan globe under cursor\r\n this.camera.movement.startDrag(scene.pointerX, scene.pointerY);\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n public override onTouch(point: Nullable<PointerTouch>, offsetX: number, offsetY: number): void {\r\n // Single finger touch (no button property) or left button (button 0) = drag\r\n const button = point?.button ?? 0; // Default to button 0 (drag) if undefined\r\n const scene = this.camera.getScene();\r\n switch (button) {\r\n case 0: // Left button / single touch - drag/pan globe under cursor\r\n this.camera.movement.handleDrag(scene.pointerX, scene.pointerY);\r\n break;\r\n case 1: // Middle button - tilt camera\r\n case 2: // Right button - tilt camera\r\n this._handleTilt(offsetX, offsetY);\r\n break;\r\n }\r\n }\r\n\r\n /**\r\n * Move camera from multitouch (pinch) zoom distances.\r\n * Zooms towards the centroid (midpoint between the two fingers).\r\n * @param previousPinchSquaredDistance\r\n * @param pinchSquaredDistance\r\n */\r\n protected override _computePinchZoom(previousPinchSquaredDistance: number, pinchSquaredDistance: number): void {\r\n // Calculate zoom distance based on pinch delta\r\n const previousDistance = Math.sqrt(previousPinchSquaredDistance);\r\n const currentDistance = Math.sqrt(pinchSquaredDistance);\r\n const pinchDelta = currentDistance - previousDistance;\r\n\r\n // Try to zoom towards centroid if we have it\r\n if (this._pinchCentroid) {\r\n const scene = this.camera.getScene();\r\n const engine = scene.getEngine();\r\n const canvasRect = engine.getInputElementClientRect();\r\n\r\n if (canvasRect) {\r\n // Convert centroid from clientX/Y to canvas-relative coordinates (same as scene.pointerX/Y)\r\n const canvasX = this._pinchCentroid.x - canvasRect.left;\r\n const canvasY = this._pinchCentroid.y - canvasRect.top;\r\n\r\n // Pick at centroid\r\n const pickResult = scene.pick(canvasX, canvasY, this.camera.movement.pickPredicate);\r\n if (pickResult?.pickedPoint) {\r\n // Scale zoom by distance to picked point\r\n const distanceToPoint = this.camera.position.subtract(pickResult.pickedPoint).length();\r\n const zoomDistance = pinchDelta * distanceToPoint * 0.005;\r\n const clampedZoom = this.camera.limits.clampZoomDistance(zoomDistance, this.camera.radius, distanceToPoint);\r\n this.camera.zoomToPoint(pickResult.pickedPoint, clampedZoom);\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // Fallback: scale zoom by camera radius along lookat vector\r\n const zoomDistance = pinchDelta * this.camera.radius * 0.005;\r\n const clampedZoom = this.camera.limits.clampZoomDistance(zoomDistance, this.camera.radius);\r\n this.camera.zoomAlongLookAt(clampedZoom);\r\n }\r\n\r\n /**\r\n * Move camera from multi touch panning positions.\r\n * In geospatialcamera, multi touch panning tilts the globe (whereas single touch will pan/drag it)\r\n * @param previousMultiTouchPanPosition\r\n * @param multiTouchPanPosition\r\n */\r\n protected override _computeMultiTouchPanning(previousMultiTouchPanPosition: Nullable<PointerTouch>, multiTouchPanPosition: Nullable<PointerTouch>): void {\r\n if (previousMultiTouchPanPosition && multiTouchPanPosition) {\r\n const moveDeltaX = multiTouchPanPosition.x - previousMultiTouchPanPosition.x;\r\n const moveDeltaY = multiTouchPanPosition.y - previousMultiTouchPanPosition.y;\r\n this._handleTilt(moveDeltaX, moveDeltaY);\r\n }\r\n }\r\n\r\n public override onDoubleTap(type: string): void {\r\n const pickResult = this.camera._scene.pick(this.camera._scene.pointerX, this.camera._scene.pointerY, this.camera.movement.pickPredicate);\r\n if (pickResult.pickedPoint) {\r\n void this.camera.flyToPointAsync(pickResult.pickedPoint);\r\n }\r\n }\r\n\r\n public override onMultiTouch(\r\n pointA: Nullable<PointerTouch>,\r\n pointB: Nullable<PointerTouch>,\r\n previousPinchSquaredDistance: number,\r\n pinchSquaredDistance: number,\r\n previousMultiTouchPanPosition: Nullable<PointerTouch>,\r\n multiTouchPanPosition: Nullable<PointerTouch>\r\n ): void {\r\n // Store centroid for use in _computePinchZoom (it's already calculated by parent)\r\n this._pinchCentroid = multiTouchPanPosition;\r\n\r\n // Reset on gesture end\r\n if (pinchSquaredDistance === 0 && multiTouchPanPosition === null) {\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onMultiTouch(pointA, pointB, previousPinchSquaredDistance, pinchSquaredDistance, previousMultiTouchPanPosition, multiTouchPanPosition);\r\n return;\r\n }\r\n\r\n // Track initial distance at gesture start for cumulative threshold detection\r\n if (this._initialPinchSquaredDistance === 0 && pinchSquaredDistance !== 0) {\r\n this._initialPinchSquaredDistance = pinchSquaredDistance;\r\n }\r\n\r\n // Use cumulative delta from gesture start for threshold detection (more forgiving than frame-to-frame)\r\n const cumulativeDelta = Math.abs(Math.sqrt(pinchSquaredDistance) - Math.sqrt(this._initialPinchSquaredDistance));\r\n this._shouldStartPinchZoom = this._twoFingerActivityCount < 20 && cumulativeDelta > this.camera.limits.pinchToPanMax;\r\n\r\n super.onMultiTouch(pointA, pointB, previousPinchSquaredDistance, pinchSquaredDistance, previousMultiTouchPanPosition, multiTouchPanPosition);\r\n }\r\n\r\n public override onButtonUp(_evt: IPointerEvent): void {\r\n this.camera.movement.stopDrag();\r\n this.camera.movement.activeInput = false;\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onButtonUp(_evt);\r\n }\r\n\r\n public override onLostFocus(): void {\r\n this._initialPinchSquaredDistance = 0;\r\n this._pinchCentroid = null;\r\n super.onLostFocus();\r\n }\r\n\r\n private _handleTilt(deltaX: number, deltaY: number): void {\r\n this.camera.movement.rotationAccumulatedPixels.y -= deltaX; // yaw - looking side to side\r\n this.camera.movement.rotationAccumulatedPixels.x -= deltaY; // pitch - look up towards sky / down towards ground\r\n }\r\n}\r\n"]}
@@ -1,3 +1,4 @@
1
+ import { Vector2 } from "../../Maths/math.vector.js";
1
2
  /**
2
3
  * Limits for geospatial camera
3
4
  */
@@ -7,8 +8,15 @@ export declare class GeospatialLimits {
7
8
  private _radiusMax;
8
9
  /** Gets the minimum pitch angle (angle from horizon) -- 0 means looking straight down at planet */
9
10
  pitchMin: number;
10
- /** Gets the maximum pitch angle (angle from horizon) -- Pi/1 means looking at horizon */
11
+ /** Gets the maximum pitch angle (angle from horizon) -- Pi/2 means looking at horizon */
11
12
  pitchMax: number;
13
+ /**
14
+ * Controls how pitch is disabled as the camera zooms out.
15
+ * x = radius scale at which full pitch is allowed (e.g., 1.5 means 1.5 * planetRadius)
16
+ * y = radius scale at which pitch is fully disabled (forced to pitchMin)
17
+ * Set to undefined to disable this feature.
18
+ */
19
+ pitchDisabledRadiusScale?: Vector2;
12
20
  /** Gets the minimum yaw angle (rotation around up axis) */
13
21
  yawMin: number;
14
22
  /** Gets the maximum yaw angle (rotation around up axis) */
@@ -39,4 +47,20 @@ export declare class GeospatialLimits {
39
47
  get planetRadius(): number;
40
48
  /** Sets the planet radius and updates the radius limits to maintain current altitude */
41
49
  set planetRadius(value: number);
50
+ /**
51
+ * Clamps a zoom distance to respect the radius limits.
52
+ * @param zoomDistance The requested zoom distance (positive = zoom in, negative = zoom out)
53
+ * @param currentRadius The current camera radius
54
+ * @param distanceToTarget Optional distance to the zoom target point (used for zoom-in clamping)
55
+ * @returns The clamped zoom distance
56
+ */
57
+ clampZoomDistance(zoomDistance: number, currentRadius: number, distanceToTarget?: number): number;
58
+ /**
59
+ * Computes the effective maximum pitch based on the current camera radius.
60
+ * When pitchDisabledRadiusScale is set, pitch is interpolated from pitchMax to pitchMin
61
+ * as the camera zooms out from x*planetRadius to y*planetRadius.
62
+ * @param currentRadius The current camera radius (distance from planet center)
63
+ * @returns The effective maximum pitch angle
64
+ */
65
+ getEffectivePitchMax(currentRadius: number): number;
42
66
  }
@@ -1,4 +1,6 @@
1
1
  import { Epsilon } from "../../Maths/math.constants.js";
2
+ import { Vector2 } from "../../Maths/math.vector.js";
3
+ import { Clamp } from "../../Maths/math.scalar.functions.js";
2
4
  /**
3
5
  * Limits for geospatial camera
4
6
  */
@@ -7,12 +9,19 @@ export class GeospatialLimits {
7
9
  * @param planetRadius The radius of the planet
8
10
  */
9
11
  constructor(planetRadius) {
10
- this._radiusMin = Epsilon;
12
+ this._radiusMin = 10;
11
13
  this._radiusMax = Infinity;
12
14
  /** Gets the minimum pitch angle (angle from horizon) -- 0 means looking straight down at planet */
13
15
  this.pitchMin = Epsilon;
14
- /** Gets the maximum pitch angle (angle from horizon) -- Pi/1 means looking at horizon */
15
- this.pitchMax = Math.PI / 2;
16
+ /** Gets the maximum pitch angle (angle from horizon) -- Pi/2 means looking at horizon */
17
+ this.pitchMax = Math.PI / 2 - 0.01;
18
+ /**
19
+ * Controls how pitch is disabled as the camera zooms out.
20
+ * x = radius scale at which full pitch is allowed (e.g., 1.5 means 1.5 * planetRadius)
21
+ * y = radius scale at which pitch is fully disabled (forced to pitchMin)
22
+ * Set to undefined to disable this feature.
23
+ */
24
+ this.pitchDisabledRadiusScale = new Vector2(2, 4);
16
25
  /** Gets the minimum yaw angle (rotation around up axis) */
17
26
  this.yawMin = -Infinity;
18
27
  /** Gets the maximum yaw angle (rotation around up axis) */
@@ -24,6 +33,7 @@ export class GeospatialLimits {
24
33
  */
25
34
  this.pinchToPanMax = 20;
26
35
  this._planetRadius = planetRadius;
36
+ this.radiusMax = planetRadius * 4;
27
37
  }
28
38
  get radiusMin() {
29
39
  return this._radiusMin;
@@ -53,5 +63,52 @@ export class GeospatialLimits {
53
63
  set planetRadius(value) {
54
64
  this._planetRadius = value;
55
65
  }
66
+ /**
67
+ * Clamps a zoom distance to respect the radius limits.
68
+ * @param zoomDistance The requested zoom distance (positive = zoom in, negative = zoom out)
69
+ * @param currentRadius The current camera radius
70
+ * @param distanceToTarget Optional distance to the zoom target point (used for zoom-in clamping)
71
+ * @returns The clamped zoom distance
72
+ */
73
+ clampZoomDistance(zoomDistance, currentRadius, distanceToTarget) {
74
+ if (zoomDistance > 0) {
75
+ // Zooming IN - don't zoom past the surface or below radiusMin
76
+ const maxZoomIn = (distanceToTarget ?? currentRadius) - this._radiusMin;
77
+ return Math.min(zoomDistance, Math.max(0, maxZoomIn));
78
+ }
79
+ else {
80
+ // Zooming OUT - don't exceed radiusMax
81
+ const maxZoomOut = this._radiusMax - currentRadius;
82
+ return Math.max(zoomDistance, -Math.max(0, maxZoomOut));
83
+ }
84
+ }
85
+ /**
86
+ * Computes the effective maximum pitch based on the current camera radius.
87
+ * When pitchDisabledRadiusScale is set, pitch is interpolated from pitchMax to pitchMin
88
+ * as the camera zooms out from x*planetRadius to y*planetRadius.
89
+ * @param currentRadius The current camera radius (distance from planet center)
90
+ * @returns The effective maximum pitch angle
91
+ */
92
+ getEffectivePitchMax(currentRadius) {
93
+ if (!this.pitchDisabledRadiusScale) {
94
+ return this.pitchMax;
95
+ }
96
+ const fullPitchRadius = this.pitchDisabledRadiusScale.x * this._planetRadius;
97
+ const noPitchRadius = this.pitchDisabledRadiusScale.y * this._planetRadius;
98
+ if (currentRadius <= fullPitchRadius) {
99
+ // Full pitch allowed
100
+ return this.pitchMax;
101
+ }
102
+ else if (currentRadius >= noPitchRadius) {
103
+ // No pitch allowed
104
+ return this.pitchMin;
105
+ }
106
+ else {
107
+ // Interpolate between pitchMax and pitchMin
108
+ const t = (currentRadius - fullPitchRadius) / (noPitchRadius - fullPitchRadius);
109
+ const clampedT = Clamp(t, 0, 1);
110
+ return this.pitchMax * (1 - clampedT) + this.pitchMin * clampedT;
111
+ }
112
+ }
56
113
  }
57
114
  //# sourceMappingURL=geospatialLimits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"geospatialLimits.js","sourceRoot":"","sources":["../../../../../dev/core/src/Cameras/Limits/geospatialLimits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAuBzB;;OAEG;IACH,YAAY,YAAoB;QAxBxB,eAAU,GAAW,OAAO,CAAC;QAC7B,eAAU,GAAW,QAAQ,CAAC;QAEtC,mGAAmG;QAC5F,aAAQ,GAAW,OAAO,CAAC;QAElC,0FAA0F;QACnF,aAAQ,GAAW,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QAEtC,2DAA2D;QACpD,WAAM,GAAW,CAAC,QAAQ,CAAC;QAElC,2DAA2D;QACpD,WAAM,GAAW,QAAQ,CAAC;QACjC;;;;WAIG;QACI,kBAAa,GAAW,EAAE,CAAC;QAM9B,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;IACtC,CAAC;IAED,IAAW,SAAS;QAChB,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,IAAW,SAAS,CAAC,KAAa;QAC9B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED,IAAW,SAAS;QAChB,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,IAAW,SAAS,CAAC,KAAa;QAC9B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,IAAW,YAAY;QACnB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9B,CAAC;IAED,wFAAwF;IACxF,IAAW,YAAY,CAAC,KAAa;QACjC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;IAC/B,CAAC;CACJ","sourcesContent":["import { Epsilon } from \"../../Maths/math.constants\";\r\n/**\r\n * Limits for geospatial camera\r\n */\r\nexport class GeospatialLimits {\r\n private _planetRadius: number;\r\n private _radiusMin: number = Epsilon;\r\n private _radiusMax: number = Infinity;\r\n\r\n /** Gets the minimum pitch angle (angle from horizon) -- 0 means looking straight down at planet */\r\n public pitchMin: number = Epsilon;\r\n\r\n /** Gets the maximum pitch angle (angle from horizon) -- Pi/1 means looking at horizon */\r\n public pitchMax: number = Math.PI / 2;\r\n\r\n /** Gets the minimum yaw angle (rotation around up axis) */\r\n public yawMin: number = -Infinity;\r\n\r\n /** Gets the maximum yaw angle (rotation around up axis) */\r\n public yawMax: number = Infinity;\r\n /**\r\n * Defines the distance used to consider the camera in pan mode vs pinch/zoom.\r\n * Basically if your fingers moves away from more than this distance you will be considered\r\n * in pinch mode.\r\n */\r\n public pinchToPanMax: number = 20;\r\n\r\n /**\r\n * @param planetRadius The radius of the planet\r\n */\r\n constructor(planetRadius: number) {\r\n this._planetRadius = planetRadius;\r\n }\r\n\r\n public get radiusMin(): number {\r\n return this._radiusMin;\r\n }\r\n\r\n /**\r\n * Sets the minimum radius\r\n */\r\n public set radiusMin(value: number) {\r\n this._radiusMin = value;\r\n }\r\n\r\n public get radiusMax(): number {\r\n return this._radiusMax;\r\n }\r\n\r\n /**\r\n * Sets the maximum radius\r\n */\r\n public set radiusMax(value: number) {\r\n this._radiusMax = value;\r\n }\r\n\r\n /**\r\n * Gets the planet radius used for altitude/radius conversions\r\n */\r\n public get planetRadius(): number {\r\n return this._planetRadius;\r\n }\r\n\r\n /** Sets the planet radius and updates the radius limits to maintain current altitude */\r\n public set planetRadius(value: number) {\r\n this._planetRadius = value;\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"geospatialLimits.js","sourceRoot":"","sources":["../../../../../dev/core/src/Cameras/Limits/geospatialLimits.ts"],"names":[],"mappings":"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","sourcesContent":["import { Epsilon } from \"../../Maths/math.constants\";\r\nimport { Vector2 } from \"../../Maths/math.vector\";\r\nimport { Clamp } from \"../../Maths/math.scalar.functions\";\r\n/**\r\n * Limits for geospatial camera\r\n */\r\nexport class GeospatialLimits {\r\n private _planetRadius: number;\r\n private _radiusMin: number = 10;\r\n private _radiusMax: number = Infinity;\r\n\r\n /** Gets the minimum pitch angle (angle from horizon) -- 0 means looking straight down at planet */\r\n public pitchMin: number = Epsilon;\r\n\r\n /** Gets the maximum pitch angle (angle from horizon) -- Pi/2 means looking at horizon */\r\n public pitchMax: number = Math.PI / 2 - 0.01;\r\n\r\n /**\r\n * Controls how pitch is disabled as the camera zooms out.\r\n * x = radius scale at which full pitch is allowed (e.g., 1.5 means 1.5 * planetRadius)\r\n * y = radius scale at which pitch is fully disabled (forced to pitchMin)\r\n * Set to undefined to disable this feature.\r\n */\r\n public pitchDisabledRadiusScale?: Vector2 = new Vector2(2, 4);\r\n\r\n /** Gets the minimum yaw angle (rotation around up axis) */\r\n public yawMin: number = -Infinity;\r\n\r\n /** Gets the maximum yaw angle (rotation around up axis) */\r\n public yawMax: number = Infinity;\r\n /**\r\n * Defines the distance used to consider the camera in pan mode vs pinch/zoom.\r\n * Basically if your fingers moves away from more than this distance you will be considered\r\n * in pinch mode.\r\n */\r\n public pinchToPanMax: number = 20;\r\n\r\n /**\r\n * @param planetRadius The radius of the planet\r\n */\r\n constructor(planetRadius: number) {\r\n this._planetRadius = planetRadius;\r\n this.radiusMax = planetRadius * 4;\r\n }\r\n\r\n public get radiusMin(): number {\r\n return this._radiusMin;\r\n }\r\n\r\n /**\r\n * Sets the minimum radius\r\n */\r\n public set radiusMin(value: number) {\r\n this._radiusMin = value;\r\n }\r\n\r\n public get radiusMax(): number {\r\n return this._radiusMax;\r\n }\r\n\r\n /**\r\n * Sets the maximum radius\r\n */\r\n public set radiusMax(value: number) {\r\n this._radiusMax = value;\r\n }\r\n\r\n /**\r\n * Gets the planet radius used for altitude/radius conversions\r\n */\r\n public get planetRadius(): number {\r\n return this._planetRadius;\r\n }\r\n\r\n /** Sets the planet radius and updates the radius limits to maintain current altitude */\r\n public set planetRadius(value: number) {\r\n this._planetRadius = value;\r\n }\r\n\r\n /**\r\n * Clamps a zoom distance to respect the radius limits.\r\n * @param zoomDistance The requested zoom distance (positive = zoom in, negative = zoom out)\r\n * @param currentRadius The current camera radius\r\n * @param distanceToTarget Optional distance to the zoom target point (used for zoom-in clamping)\r\n * @returns The clamped zoom distance\r\n */\r\n public clampZoomDistance(zoomDistance: number, currentRadius: number, distanceToTarget?: number): number {\r\n if (zoomDistance > 0) {\r\n // Zooming IN - don't zoom past the surface or below radiusMin\r\n const maxZoomIn = (distanceToTarget ?? currentRadius) - this._radiusMin;\r\n return Math.min(zoomDistance, Math.max(0, maxZoomIn));\r\n } else {\r\n // Zooming OUT - don't exceed radiusMax\r\n const maxZoomOut = this._radiusMax - currentRadius;\r\n return Math.max(zoomDistance, -Math.max(0, maxZoomOut));\r\n }\r\n }\r\n\r\n /**\r\n * Computes the effective maximum pitch based on the current camera radius.\r\n * When pitchDisabledRadiusScale is set, pitch is interpolated from pitchMax to pitchMin\r\n * as the camera zooms out from x*planetRadius to y*planetRadius.\r\n * @param currentRadius The current camera radius (distance from planet center)\r\n * @returns The effective maximum pitch angle\r\n */\r\n public getEffectivePitchMax(currentRadius: number): number {\r\n if (!this.pitchDisabledRadiusScale) {\r\n return this.pitchMax;\r\n }\r\n\r\n const fullPitchRadius = this.pitchDisabledRadiusScale.x * this._planetRadius;\r\n const noPitchRadius = this.pitchDisabledRadiusScale.y * this._planetRadius;\r\n\r\n if (currentRadius <= fullPitchRadius) {\r\n // Full pitch allowed\r\n return this.pitchMax;\r\n } else if (currentRadius >= noPitchRadius) {\r\n // No pitch allowed\r\n return this.pitchMin;\r\n } else {\r\n // Interpolate between pitchMax and pitchMin\r\n const t = (currentRadius - fullPitchRadius) / (noPitchRadius - fullPitchRadius);\r\n const clampedT = Clamp(t, 0, 1);\r\n return this.pitchMax * (1 - clampedT) + this.pitchMin * clampedT;\r\n }\r\n }\r\n}\r\n"]}
@@ -1,5 +1,6 @@
1
1
  import { GeospatialCameraInputsManager } from "./geospatialCameraInputsManager.js";
2
2
  import { Vector3, Matrix } from "../Maths/math.vector.js";
3
+ import type { Vector2 } from "../Maths/math.vector.js";
3
4
  import { Camera } from "./camera.js";
4
5
  import type { Scene } from "../scene.js";
5
6
  import type { MeshPredicate } from "../Culling/ray.core.js";
@@ -8,19 +9,21 @@ import { GeospatialLimits } from "./Limits/geospatialLimits.js";
8
9
  import { GeospatialCameraMovement } from "./geospatialCameraMovement.js";
9
10
  import type { IVector3Like } from "../Maths/math.like.js";
10
11
  import type { EasingFunction } from "../Animations/easing.js";
11
- type CameraOptions = {
12
+ export type GeospatialCameraOptions = {
13
+ /**
14
+ * Radius of the planet being orbited
15
+ */
12
16
  planetRadius: number;
17
+ /**
18
+ * If supplied, will be used by the movement class when picking the globe. Can later update camera.movement.pickPredicate directly
19
+ */
20
+ pickPredicate?: MeshPredicate;
13
21
  };
14
22
  /**
15
- * @experimental
16
- * This camera's movements are limited to a camera orbiting a globe, and as the API evolves it will introduce conversions between cartesian coordinates and true lat/long/alt
17
- *
18
- * Please note this is marked as experimental and the API (including the constructor!) will change until we remove that flag
23
+ * Camera equipped to orbit a spherical planet centered at world origin
19
24
  */
20
25
  export declare class GeospatialCamera extends Camera {
21
26
  inputs: GeospatialCameraInputsManager;
22
- /** If supplied, will be used when picking the globe */
23
- pickPredicate?: MeshPredicate;
24
27
  /** Movement controller that turns input pixelDeltas into currentFrameDeltas used by camera*/
25
28
  readonly movement: GeospatialCameraMovement;
26
29
  private _tempPosition;
@@ -37,7 +40,7 @@ export declare class GeospatialCamera extends Camera {
37
40
  perFrameCollisionOffset: Vector3;
38
41
  /** Enable or disable collision checking for this camera. Default is false. */
39
42
  checkCollisions: boolean;
40
- constructor(name: string, scene: Scene, options: CameraOptions, pickPredicate?: MeshPredicate);
43
+ constructor(name: string, scene: Scene, options: GeospatialCameraOptions);
41
44
  private _center;
42
45
  /** The point on the globe that we are anchoring around. If no alternate rotation point is supplied, this will represent the center of screen*/
43
46
  get center(): Vector3;
@@ -146,4 +149,25 @@ export declare class GeospatialCamera extends Camera {
146
149
  attachControl(noPreventDefault?: boolean): void;
147
150
  detachControl(): void;
148
151
  }
149
- export {};
152
+ /**
153
+ * Compute the lookAt direction vector from yaw and pitch angles at a given center point.
154
+ * This is the forward formula used by GeospatialCamera._setOrientation.
155
+ * @param yaw - The yaw angle in radians (0 = north, π/2 = east)
156
+ * @param pitch - The pitch angle in radians (0 = looking at planet center, π/2 = looking at horizon)
157
+ * @param center - The center point on the globe
158
+ * @param useRightHandedSystem - Whether the scene uses a right-handed coordinate system
159
+ * @param result - The vector to store the result in
160
+ * @returns The normalized lookAt direction vector (same as result)
161
+ */
162
+ export declare function ComputeLookAtFromYawPitchToRef(yaw: number, pitch: number, center: Vector3, useRightHandedSystem: boolean, result: Vector3): Vector3;
163
+ /**
164
+ * Given a lookAt direction and center, compute the yaw and pitch angles that would produce that lookAt.
165
+ * This is the inverse of ComputeLookAtFromYawPitchToRef.
166
+ * @param lookAt - The normalized lookAt direction vector
167
+ * @param center - The center point on the globe
168
+ * @param useRightHandedSystem - Whether the scene uses a right-handed coordinate system
169
+ * @param currentYaw - The current yaw value to use as fallback when pitch is near 0 (looking straight down/up)
170
+ * @param result - The Vector2 to store the result in (x = yaw, y = pitch)
171
+ * @returns The result Vector2
172
+ */
173
+ export declare function ComputeYawPitchFromLookAtToRef(lookAt: Vector3, center: Vector3, useRightHandedSystem: boolean, currentYaw: number, result: Vector2): Vector2;