@cesium/engine 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/Build/Workers/{AttributeCompression-4589338a.js → AttributeCompression-b646d393.js} +1 -1
  2. package/Build/Workers/{AxisAlignedBoundingBox-bfb1234f.js → AxisAlignedBoundingBox-a4321399.js} +1 -1
  3. package/Build/Workers/{BoundingRectangle-7ffc2153.js → BoundingRectangle-80d8801f.js} +1 -1
  4. package/Build/Workers/{BoxGeometry-93f344a3.js → BoxGeometry-33c10a4f.js} +1 -1
  5. package/Build/Workers/{Color-0c5aaf47.js → Color-1bf9a6e0.js} +1 -1
  6. package/Build/Workers/{CoplanarPolygonGeometryLibrary-6597982a.js → CoplanarPolygonGeometryLibrary-5c5b981b.js} +1 -1
  7. package/Build/Workers/{CorridorGeometryLibrary-2119580e.js → CorridorGeometryLibrary-7cbae7a0.js} +1 -1
  8. package/Build/Workers/{CylinderGeometry-f9503c6d.js → CylinderGeometry-42740df0.js} +1 -1
  9. package/Build/Workers/{EllipseGeometry-fedf72d5.js → EllipseGeometry-49a3ac21.js} +1 -1
  10. package/Build/Workers/{EllipseGeometryLibrary-4ea68687.js → EllipseGeometryLibrary-b891b2f8.js} +1 -1
  11. package/Build/Workers/{EllipseOutlineGeometry-e2ae6a35.js → EllipseOutlineGeometry-fbd72e07.js} +1 -1
  12. package/Build/Workers/{EllipsoidGeodesic-16de63ea.js → EllipsoidGeodesic-98c62a56.js} +1 -1
  13. package/Build/Workers/{EllipsoidGeometry-95c0330e.js → EllipsoidGeometry-a351dba8.js} +1 -1
  14. package/Build/Workers/{EllipsoidOutlineGeometry-d6d2ceec.js → EllipsoidOutlineGeometry-3e8779bf.js} +1 -1
  15. package/Build/Workers/{EllipsoidRhumbLine-ae5e65e0.js → EllipsoidRhumbLine-19756602.js} +1 -1
  16. package/Build/Workers/{EllipsoidTangentPlane-bfc674bf.js → EllipsoidTangentPlane-cfb50678.js} +1 -1
  17. package/Build/Workers/{EncodedCartesian3-f473ea18.js → EncodedCartesian3-81f70735.js} +1 -1
  18. package/Build/Workers/{FrustumGeometry-3bf2e4eb.js → FrustumGeometry-c5754412.js} +1 -1
  19. package/Build/Workers/{GeometryAttribute-df5f473e.js → GeometryAttribute-0bfd05e8.js} +1 -1
  20. package/Build/Workers/{GeometryInstance-aa75b4cf.js → GeometryInstance-451dc1cd.js} +1 -1
  21. package/Build/Workers/{GeometryPipeline-f0e46c42.js → GeometryPipeline-0166905d.js} +1 -1
  22. package/Build/Workers/{IntersectionTests-9de29d51.js → IntersectionTests-a93d3de9.js} +1 -1
  23. package/Build/Workers/{Matrix2-ce637455.js → Matrix2-13178034.js} +4 -2
  24. package/Build/Workers/{Matrix3-4f4df527.js → Matrix3-315394f6.js} +2 -1
  25. package/Build/Workers/{OrientedBoundingBox-d3ff3d4d.js → OrientedBoundingBox-b450cb61.js} +5 -5
  26. package/Build/Workers/{Plane-b81241c2.js → Plane-900aa728.js} +1 -1
  27. package/Build/Workers/{PolygonGeometryLibrary-6f4937bb.js → PolygonGeometryLibrary-f918ac26.js} +1 -1
  28. package/Build/Workers/PolygonPipeline-f59a8f0a.js +1345 -0
  29. package/Build/Workers/{PolylinePipeline-2e2b015c.js → PolylinePipeline-d8e6bd9b.js} +1 -1
  30. package/Build/Workers/{PolylineVolumeGeometryLibrary-7ee1d449.js → PolylineVolumeGeometryLibrary-125d2edf.js} +1 -1
  31. package/Build/Workers/{PrimitivePipeline-a9b7aae0.js → PrimitivePipeline-0d7b5ed4.js} +1 -1
  32. package/Build/Workers/{RectangleGeometryLibrary-9aa6c8f6.js → RectangleGeometryLibrary-0768db63.js} +1 -1
  33. package/Build/Workers/{TerrainEncoding-b52dcfc5.js → TerrainEncoding-06470d7f.js} +1 -1
  34. package/Build/Workers/{Transforms-2f755df8.js → Transforms-26539bce.js} +97 -83
  35. package/Build/Workers/{WallGeometryLibrary-ab19a93d.js → WallGeometryLibrary-6167b0fc.js} +1 -1
  36. package/Build/Workers/{WebMercatorProjection-9f25de49.js → WebMercatorProjection-13a90d41.js} +1 -1
  37. package/Build/Workers/cesiumWorkerBootstrapper.js +25 -1
  38. package/Build/Workers/combineGeometry.js +1 -1
  39. package/Build/Workers/createBoxGeometry.js +1 -1
  40. package/Build/Workers/createBoxOutlineGeometry.js +1 -1
  41. package/Build/Workers/createCircleGeometry.js +1 -1
  42. package/Build/Workers/createCircleOutlineGeometry.js +1 -1
  43. package/Build/Workers/createCoplanarPolygonGeometry.js +1 -1
  44. package/Build/Workers/createCoplanarPolygonOutlineGeometry.js +1 -1
  45. package/Build/Workers/createCorridorGeometry.js +1 -1
  46. package/Build/Workers/createCorridorOutlineGeometry.js +1 -1
  47. package/Build/Workers/createCylinderGeometry.js +1 -1
  48. package/Build/Workers/createCylinderOutlineGeometry.js +1 -1
  49. package/Build/Workers/createEllipseGeometry.js +1 -1
  50. package/Build/Workers/createEllipseOutlineGeometry.js +1 -1
  51. package/Build/Workers/createEllipsoidGeometry.js +1 -1
  52. package/Build/Workers/createEllipsoidOutlineGeometry.js +1 -1
  53. package/Build/Workers/createFrustumGeometry.js +1 -1
  54. package/Build/Workers/createFrustumOutlineGeometry.js +1 -1
  55. package/Build/Workers/createGeometry.js +1 -1
  56. package/Build/Workers/createGroundPolylineGeometry.js +1 -1
  57. package/Build/Workers/createPlaneGeometry.js +1 -1
  58. package/Build/Workers/createPlaneOutlineGeometry.js +1 -1
  59. package/Build/Workers/createPolygonGeometry.js +1 -1
  60. package/Build/Workers/createPolygonOutlineGeometry.js +1 -1
  61. package/Build/Workers/createPolylineGeometry.js +1 -1
  62. package/Build/Workers/createPolylineVolumeGeometry.js +1 -1
  63. package/Build/Workers/createPolylineVolumeOutlineGeometry.js +1 -1
  64. package/Build/Workers/createRectangleGeometry.js +1 -1
  65. package/Build/Workers/createRectangleOutlineGeometry.js +1 -1
  66. package/Build/Workers/createSimplePolylineGeometry.js +1 -1
  67. package/Build/Workers/createSphereGeometry.js +1 -1
  68. package/Build/Workers/createSphereOutlineGeometry.js +1 -1
  69. package/Build/Workers/createVectorTileClampedPolylines.js +1 -1
  70. package/Build/Workers/createVectorTileGeometries.js +1 -1
  71. package/Build/Workers/createVectorTilePoints.js +1 -1
  72. package/Build/Workers/createVectorTilePolygons.js +1 -1
  73. package/Build/Workers/createVectorTilePolylines.js +1 -1
  74. package/Build/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +1 -1
  75. package/Build/Workers/createVerticesFromHeightmap.js +7 -3
  76. package/Build/Workers/createVerticesFromQuantizedTerrainMesh.js +1 -1
  77. package/Build/Workers/createWallGeometry.js +1 -1
  78. package/Build/Workers/createWallOutlineGeometry.js +1 -1
  79. package/Build/Workers/decodeI3S.js +1 -1
  80. package/Build/Workers/package.js +25 -1
  81. package/Build/Workers/transferTypedArrayTest.js +25 -1
  82. package/Build/Workers/upsampleQuantizedTerrainMesh.js +1 -1
  83. package/Source/Core/EarthOrientationParameters.js +67 -63
  84. package/Source/Core/Ion.js +1 -1
  85. package/Source/Core/Matrix2.js +2 -1
  86. package/Source/Core/Matrix3.js +2 -1
  87. package/Source/Core/Matrix4.js +1 -0
  88. package/Source/Core/OrientedBoundingBox.js +4 -4
  89. package/Source/Core/S2Cell.js +3 -3
  90. package/Source/Core/Transforms.js +3 -7
  91. package/Source/DataSources/GpxDataSource.js +4 -6
  92. package/Source/DataSources/KmlDataSource.js +3 -5
  93. package/Source/Renderer/Context.js +27 -9
  94. package/Source/Renderer/Pass.js +3 -2
  95. package/Source/Scene/Cesium3DTileContentType.js +19 -0
  96. package/Source/Scene/Cesium3DTilesVoxelProvider.js +482 -0
  97. package/Source/Scene/Cesium3DTilesetMetadata.js +2 -2
  98. package/Source/Scene/ContentMetadata.js +2 -2
  99. package/Source/Scene/DerivedCommand.js +7 -0
  100. package/Source/Scene/Expression.js +1 -1
  101. package/Source/Scene/Globe.js +10 -0
  102. package/Source/Scene/GlobeSurfaceTileProvider.js +7 -0
  103. package/Source/Scene/GltfImageLoader.js +1 -1
  104. package/Source/Scene/GltfVertexBufferLoader.js +10 -2
  105. package/Source/Scene/GroupMetadata.js +2 -2
  106. package/Source/Scene/ImplicitMetadataView.js +2 -2
  107. package/Source/Scene/ImplicitSubtreeCache.js +127 -0
  108. package/Source/Scene/ImplicitSubtreeMetadata.js +2 -2
  109. package/Source/Scene/ImplicitTileset.js +3 -2
  110. package/Source/Scene/KeyframeNode.js +47 -0
  111. package/Source/Scene/Megatexture.js +490 -0
  112. package/Source/Scene/MetadataClass.js +60 -25
  113. package/Source/Scene/MetadataClassProperty.js +132 -68
  114. package/Source/Scene/MetadataComponentType.js +78 -57
  115. package/Source/Scene/MetadataEnum.js +59 -23
  116. package/Source/Scene/MetadataEnumValue.js +49 -15
  117. package/Source/Scene/MetadataSchema.js +53 -24
  118. package/Source/Scene/MetadataSchemaLoader.js +2 -2
  119. package/Source/Scene/MetadataTableProperty.js +2 -2
  120. package/Source/Scene/MetadataType.js +6 -14
  121. package/Source/Scene/Model/GeoJsonLoader.js +1 -1
  122. package/Source/Scene/Model/Model.js +2 -3
  123. package/Source/Scene/Model/buildDrawCommand.js +19 -11
  124. package/Source/Scene/PointCloud.js +1 -1
  125. package/Source/Scene/PropertyAttribute.js +2 -2
  126. package/Source/Scene/PropertyAttributeProperty.js +2 -2
  127. package/Source/Scene/PropertyTable.js +2 -2
  128. package/Source/Scene/PropertyTexture.js +2 -2
  129. package/Source/Scene/PropertyTextureProperty.js +2 -2
  130. package/Source/Scene/ResourceCache.js +9 -1
  131. package/Source/Scene/ResourceCacheKey.js +9 -1
  132. package/Source/Scene/Scene.js +17 -0
  133. package/Source/Scene/SpatialNode.js +375 -0
  134. package/Source/Scene/StructuralMetadata.js +2 -2
  135. package/Source/Scene/TileMetadata.js +2 -2
  136. package/Source/Scene/TilesetMetadata.js +2 -2
  137. package/Source/Scene/VoxelBoxShape.js +527 -0
  138. package/Source/Scene/VoxelContent.js +169 -0
  139. package/Source/Scene/VoxelCylinderShape.js +937 -0
  140. package/Source/Scene/VoxelEllipsoidShape.js +937 -0
  141. package/Source/Scene/VoxelPrimitive.js +1919 -0
  142. package/Source/Scene/VoxelProvider.js +255 -0
  143. package/Source/Scene/VoxelRenderResources.js +224 -0
  144. package/Source/Scene/VoxelShape.js +152 -0
  145. package/Source/Scene/VoxelShapeType.js +105 -0
  146. package/Source/Scene/VoxelTraversal.js +1144 -0
  147. package/Source/Scene/buildVoxelDrawCommands.js +116 -0
  148. package/Source/Scene/parseBatchTable.js +18 -2
  149. package/Source/Scene/preprocess3DTileContent.js +8 -0
  150. package/Source/Scene/processVoxelProperties.js +455 -0
  151. package/Source/Shaders/BillboardCollectionVS.glsl +1 -1
  152. package/Source/Shaders/BillboardCollectionVS.js +1 -1
  153. package/Source/Shaders/Builtin/Constants/passOverlay.glsl +1 -1
  154. package/Source/Shaders/Builtin/Constants/passOverlay.js +1 -1
  155. package/Source/Shaders/Builtin/Constants/passVoxels.glsl +9 -0
  156. package/Source/Shaders/Builtin/Constants/passVoxels.js +11 -0
  157. package/Source/Shaders/Builtin/CzmBuiltins.js +17 -15
  158. package/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.glsl +55 -43
  159. package/Source/Shaders/Builtin/Functions/windowToEyeCoordinates.js +55 -43
  160. package/Source/Shaders/GlobeFS.glsl +2 -1
  161. package/Source/Shaders/GlobeFS.js +2 -1
  162. package/Source/Shaders/PointPrimitiveCollectionVS.glsl +5 -2
  163. package/Source/Shaders/PointPrimitiveCollectionVS.js +5 -2
  164. package/Source/Shaders/Voxels/IntersectBox.glsl +79 -0
  165. package/Source/Shaders/Voxels/IntersectBox.js +81 -0
  166. package/Source/Shaders/Voxels/IntersectClippingPlanes.glsl +70 -0
  167. package/Source/Shaders/Voxels/IntersectClippingPlanes.js +72 -0
  168. package/Source/Shaders/Voxels/IntersectCylinder.glsl +273 -0
  169. package/Source/Shaders/Voxels/IntersectCylinder.js +275 -0
  170. package/Source/Shaders/Voxels/IntersectDepth.glsl +23 -0
  171. package/Source/Shaders/Voxels/IntersectDepth.js +25 -0
  172. package/Source/Shaders/Voxels/IntersectEllipsoid.glsl +337 -0
  173. package/Source/Shaders/Voxels/IntersectEllipsoid.js +339 -0
  174. package/Source/Shaders/Voxels/Intersection.glsl +54 -0
  175. package/Source/Shaders/Voxels/Intersection.js +56 -0
  176. package/Source/Shaders/Voxels/IntersectionUtils.glsl +151 -0
  177. package/Source/Shaders/Voxels/IntersectionUtils.js +153 -0
  178. package/Source/Shaders/Voxels/Megatexture.glsl +136 -0
  179. package/Source/Shaders/Voxels/Megatexture.js +138 -0
  180. package/Source/Shaders/Voxels/Octree.glsl +221 -0
  181. package/Source/Shaders/Voxels/Octree.js +223 -0
  182. package/Source/Shaders/Voxels/VoxelFS.glsl +150 -0
  183. package/Source/Shaders/Voxels/VoxelFS.js +152 -0
  184. package/Source/Shaders/Voxels/VoxelVS.glsl +11 -0
  185. package/Source/Shaders/Voxels/VoxelVS.js +13 -0
  186. package/Source/Shaders/Voxels/convertUvToBox.glsl +16 -0
  187. package/Source/Shaders/Voxels/convertUvToBox.js +18 -0
  188. package/Source/Shaders/Voxels/convertUvToCylinder.glsl +79 -0
  189. package/Source/Shaders/Voxels/convertUvToCylinder.js +81 -0
  190. package/Source/Shaders/Voxels/convertUvToEllipsoid.glsl +133 -0
  191. package/Source/Shaders/Voxels/convertUvToEllipsoid.js +135 -0
  192. package/Source/ThirdParty/Workers/z-worker-pako.js +0 -0
  193. package/index.d.ts +3124 -2287
  194. package/index.js +1117 -1085
  195. package/package.json +3 -4
  196. package/Build/Workers/PolygonPipeline-774a8aaf.js +0 -1341
@@ -0,0 +1,1144 @@
1
+ import Cartesian2 from "../Core/Cartesian2.js";
2
+ import CesiumMath from "../Core/Math.js";
3
+ import CullingVolume from "../Core/CullingVolume.js";
4
+ import defined from "../Core/defined.js";
5
+ import destroyObject from "../Core/destroyObject.js";
6
+ import DoubleEndedPriorityQueue from "../Core/DoubleEndedPriorityQueue.js";
7
+ import getTimestamp from "../Core/getTimestamp.js";
8
+ import KeyframeNode from "./KeyframeNode.js";
9
+ import MetadataType from "./MetadataType.js";
10
+ import Megatexture from "./Megatexture.js";
11
+ import PixelFormat from "../Core/PixelFormat.js";
12
+ import PixelDatatype from "../Renderer/PixelDatatype.js";
13
+ import Sampler from "../Renderer/Sampler.js";
14
+ import SpatialNode from "./SpatialNode.js";
15
+ import Texture from "../Renderer/Texture.js";
16
+ import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
17
+ import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
18
+
19
+ /**
20
+ * Handles tileset traversal, tile requests, and GPU resources. Intended to be
21
+ * private and paired with a {@link VoxelPrimitive}, which has a user-facing API.
22
+ *
23
+ * @alias VoxelTraversal
24
+ * @constructor
25
+ *
26
+ * @param {VoxelPrimitive} primitive
27
+ * @param {Context} context
28
+ * @param {Cartesian3} dimensions
29
+ * @param {MetadataType[]} types
30
+ * @param {MetadataComponentType[]} componentTypes
31
+ * @param {Number} keyframeCount
32
+ * @param {Number} [maximumTextureMemoryByteLength]
33
+ *
34
+ * @private
35
+ */
36
+ function VoxelTraversal(
37
+ primitive,
38
+ context,
39
+ dimensions,
40
+ types,
41
+ componentTypes,
42
+ keyframeCount,
43
+ maximumTextureMemoryByteLength
44
+ ) {
45
+ /**
46
+ * TODO: maybe this shouldn't be stored or passed into update function?
47
+ * @type {VoxelPrimitive}
48
+ * @private
49
+ */
50
+ this._primitive = primitive;
51
+
52
+ const length = types.length;
53
+
54
+ /**
55
+ * @type {Megatexture[]}
56
+ * @readonly
57
+ */
58
+ this.megatextures = new Array(length);
59
+
60
+ // TODO make sure to split the maximumTextureMemoryByteLength across all the megatextures
61
+ for (let i = 0; i < length; i++) {
62
+ const type = types[i];
63
+ const componentCount = MetadataType.getComponentCount(type);
64
+ const componentType = componentTypes[i];
65
+
66
+ this.megatextures[i] = new Megatexture(
67
+ context,
68
+ dimensions,
69
+ componentCount,
70
+ componentType,
71
+ maximumTextureMemoryByteLength
72
+ );
73
+ }
74
+
75
+ const maximumTileCount = this.megatextures[0].maximumTileCount;
76
+
77
+ /**
78
+ * @type {Number}
79
+ * @private
80
+ */
81
+ this._simultaneousRequestCount = 0;
82
+
83
+ /**
84
+ * @type {Boolean}
85
+ * @private
86
+ */
87
+ this._debugPrint = false;
88
+
89
+ /**
90
+ * @type {Number}
91
+ * @private
92
+ */
93
+ this._frameNumber = 0;
94
+
95
+ const shape = primitive._shape;
96
+ const rootLevel = 0;
97
+ const rootX = 0;
98
+ const rootY = 0;
99
+ const rootZ = 0;
100
+ const rootParent = undefined;
101
+
102
+ /**
103
+ * @type {SpatialNode}
104
+ * @readonly
105
+ */
106
+ this.rootNode = new SpatialNode(
107
+ rootLevel,
108
+ rootX,
109
+ rootY,
110
+ rootZ,
111
+ rootParent,
112
+ shape,
113
+ dimensions
114
+ );
115
+
116
+ /**
117
+ * @type {DoubleEndedPriorityQueue}
118
+ * @private
119
+ */
120
+ this._priorityQueue = new DoubleEndedPriorityQueue({
121
+ maximumLength: maximumTileCount,
122
+ comparator: KeyframeNode.priorityComparator,
123
+ });
124
+
125
+ /**
126
+ * @type {KeyframeNode[]}
127
+ * @private
128
+ */
129
+ this._highPriorityKeyframeNodes = new Array(maximumTileCount);
130
+
131
+ /**
132
+ * @type {KeyframeNode[]}
133
+ * @private
134
+ */
135
+ this._keyframeNodesInMegatexture = new Array(maximumTileCount);
136
+
137
+ /**
138
+ * @type {Number}
139
+ * @private
140
+ */
141
+ this._keyframeCount = keyframeCount;
142
+
143
+ /**
144
+ * @type {Number}
145
+ * @private
146
+ */
147
+ this._sampleCount = undefined;
148
+
149
+ /**
150
+ * @type {Number}
151
+ * @private
152
+ */
153
+ this._keyframeLocation = 0;
154
+
155
+ /**
156
+ * @type {Number[]}
157
+ * @private
158
+ */
159
+ this._binaryTreeKeyframeWeighting = new Array(keyframeCount);
160
+
161
+ function binaryTreeWeightingRecursive(arr, start, end, depth) {
162
+ if (start > end) {
163
+ return;
164
+ }
165
+ const mid = Math.floor((start + end) / 2);
166
+ arr[mid] = depth;
167
+ binaryTreeWeightingRecursive(arr, start, mid - 1, depth + 1);
168
+ binaryTreeWeightingRecursive(arr, mid + 1, end, depth + 1);
169
+ }
170
+
171
+ const binaryTreeKeyframeWeighting = this._binaryTreeKeyframeWeighting;
172
+ binaryTreeKeyframeWeighting[0] = 0;
173
+ binaryTreeKeyframeWeighting[keyframeCount - 1] = 0;
174
+ binaryTreeWeightingRecursive(
175
+ binaryTreeKeyframeWeighting,
176
+ 1,
177
+ keyframeCount - 2,
178
+ 0
179
+ );
180
+
181
+ const internalNodeTexelCount = 9;
182
+ const internalNodeTextureDimensionX = 2048;
183
+ const internalNodeTilesPerRow = Math.floor(
184
+ internalNodeTextureDimensionX / internalNodeTexelCount
185
+ );
186
+ const internalNodeTextureDimensionY = Math.ceil(
187
+ maximumTileCount / internalNodeTilesPerRow
188
+ );
189
+
190
+ /**
191
+ * @type {Texture}
192
+ * @readonly
193
+ */
194
+ this.internalNodeTexture = new Texture({
195
+ context: context,
196
+ pixelFormat: PixelFormat.RGBA,
197
+ pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
198
+ flipY: false,
199
+ width: internalNodeTextureDimensionX,
200
+ height: internalNodeTextureDimensionY,
201
+ sampler: new Sampler({
202
+ minificationFilter: TextureMinificationFilter.NEAREST,
203
+ magnificationFilter: TextureMagnificationFilter.NEAREST,
204
+ }),
205
+ });
206
+
207
+ /**
208
+ * @type {Number}
209
+ * @readonly
210
+ */
211
+ this.internalNodeTilesPerRow = internalNodeTilesPerRow;
212
+
213
+ /**
214
+ * @type {Cartesian2}
215
+ * @readonly
216
+ */
217
+ this.internalNodeTexelSizeUv = new Cartesian2(
218
+ 1.0 / internalNodeTextureDimensionX,
219
+ 1.0 / internalNodeTextureDimensionY
220
+ );
221
+
222
+ /**
223
+ * Only generated when there are two or more samples.
224
+ * @type {Texture}
225
+ * @readonly
226
+ */
227
+ this.leafNodeTexture = undefined;
228
+
229
+ /**
230
+ * Only generated when there are two or more samples.
231
+ * @type {Number}
232
+ * @readonly
233
+ */
234
+ this.leafNodeTilesPerRow = undefined;
235
+
236
+ /**
237
+ * Only generated when there are two or more samples.
238
+ * @type {Cartesian2}
239
+ * @readonly
240
+ */
241
+ this.leafNodeTexelSizeUv = new Cartesian2();
242
+ }
243
+
244
+ VoxelTraversal.simultaneousRequestCountMaximum = 50;
245
+
246
+ /**
247
+ * @param {FrameState} frameState
248
+ * @param {Number} keyframeLocation
249
+ * @param {Boolean} recomputeBoundingVolumes
250
+ * @param {Boolean} pauseUpdate
251
+ */
252
+ VoxelTraversal.prototype.update = function (
253
+ frameState,
254
+ keyframeLocation,
255
+ recomputeBoundingVolumes,
256
+ pauseUpdate
257
+ ) {
258
+ const primitive = this._primitive;
259
+ const context = frameState.context;
260
+ const maximumTileCount = this.megatextures[0].maximumTileCount;
261
+ const keyframeCount = this._keyframeCount;
262
+
263
+ const levelBlendFactor = primitive._levelBlendFactor;
264
+ const hasLevelBlendFactor = levelBlendFactor > 0.0;
265
+ const hasKeyframes = keyframeCount > 1;
266
+ const sampleCount = (hasLevelBlendFactor ? 2 : 1) * (hasKeyframes ? 2 : 1);
267
+ this._sampleCount = sampleCount;
268
+
269
+ const useLeafNodes = sampleCount >= 2;
270
+ if (useLeafNodes && !defined(this.leafNodeTexture)) {
271
+ const leafNodeTexelCount = 2;
272
+ const leafNodeTextureDimensionX = 1024;
273
+ const leafNodeTilesPerRow = Math.floor(
274
+ leafNodeTextureDimensionX / leafNodeTexelCount
275
+ );
276
+ const leafNodeTextureDimensionY = Math.ceil(
277
+ maximumTileCount / leafNodeTilesPerRow
278
+ );
279
+
280
+ this.leafNodeTexture = new Texture({
281
+ context: context,
282
+ pixelFormat: PixelFormat.RGBA,
283
+ pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
284
+ flipY: false,
285
+ width: leafNodeTextureDimensionX,
286
+ height: leafNodeTextureDimensionY,
287
+ sampler: new Sampler({
288
+ minificationFilter: TextureMinificationFilter.NEAREST,
289
+ magnificationFilter: TextureMagnificationFilter.NEAREST,
290
+ }),
291
+ });
292
+ this.leafNodeTexelSizeUv = Cartesian2.fromElements(
293
+ 1.0 / leafNodeTextureDimensionX,
294
+ 1.0 / leafNodeTextureDimensionY,
295
+ this.leafNodeTexelSizeUv
296
+ );
297
+ this.leafNodeTilesPerRow = leafNodeTilesPerRow;
298
+ } else if (!useLeafNodes && defined(this.leafNodeTexture)) {
299
+ this.leafNodeTexture = this.leafNodeTexture.destroy();
300
+ }
301
+
302
+ this._keyframeLocation = CesiumMath.clamp(
303
+ keyframeLocation,
304
+ 0.0,
305
+ keyframeCount - 1
306
+ );
307
+
308
+ if (recomputeBoundingVolumes) {
309
+ recomputeBoundingVolumesRecursive(this, this.rootNode);
310
+ }
311
+
312
+ if (pauseUpdate) {
313
+ return;
314
+ }
315
+
316
+ this._frameNumber = frameState.frameNumber;
317
+ const timestamp0 = getTimestamp();
318
+ loadAndUnload(this, frameState);
319
+ const timestamp1 = getTimestamp();
320
+ generateOctree(this, sampleCount, levelBlendFactor);
321
+ const timestamp2 = getTimestamp();
322
+
323
+ if (this._debugPrint) {
324
+ const loadAndUnloadTimeMs = timestamp1 - timestamp0;
325
+ const generateOctreeTimeMs = timestamp2 - timestamp1;
326
+ const totalTimeMs = timestamp2 - timestamp0;
327
+ printDebugInformation(
328
+ this,
329
+ loadAndUnloadTimeMs,
330
+ generateOctreeTimeMs,
331
+ totalTimeMs
332
+ );
333
+ }
334
+ };
335
+
336
+ /**
337
+ * Check if a node is renderable.
338
+ * @param {SpatialNode} tile
339
+ * @returns {Boolean}
340
+ */
341
+ VoxelTraversal.prototype.isRenderable = function (tile) {
342
+ return tile.isRenderable(this._frameNumber);
343
+ };
344
+
345
+ /**
346
+ * Returns true if this object was destroyed; otherwise, false.
347
+ * <br /><br />
348
+ * If this object was destroyed, it should not be used; calling any function other than
349
+ * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
350
+ *
351
+ * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
352
+ *
353
+ * @see VoxelTraversal#destroy
354
+ */
355
+ VoxelTraversal.prototype.isDestroyed = function () {
356
+ return false;
357
+ };
358
+
359
+ /**
360
+ * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
361
+ * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
362
+ * <br /><br />
363
+ * Once an object is destroyed, it should not be used; calling any function other than
364
+ * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
365
+ * assign the return value (<code>undefined</code>) to the object as done in the example.
366
+ *
367
+ * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
368
+ *
369
+ * @see VoxelTraversal#isDestroyed
370
+ *
371
+ * @example
372
+ * voxelTraversal = voxelTraversal && voxelTraversal.destroy();
373
+ */
374
+ VoxelTraversal.prototype.destroy = function () {
375
+ const megatextures = this.megatextures;
376
+ const megatextureLength = megatextures.length;
377
+ for (let i = 0; i < megatextureLength; i++) {
378
+ megatextures[i] = megatextures[i] && megatextures[i].destroy();
379
+ }
380
+
381
+ this.internalNodeTexture =
382
+ this.internalNodeTexture && this.internalNodeTexture.destroy();
383
+
384
+ this.leafNodeTexture = this.leafNodeTexture && this.leafNodeTexture.destroy();
385
+
386
+ return destroyObject(this);
387
+ };
388
+
389
+ /**
390
+ * @function
391
+ *
392
+ * @param {VoxelTraversal} that
393
+ * @param {SpatialNode} node
394
+ *
395
+ * @private
396
+ */
397
+ function recomputeBoundingVolumesRecursive(that, node) {
398
+ const primitive = that._primitive;
399
+ const shape = primitive._shape;
400
+ const dimensions = primitive._provider.dimensions;
401
+ node.computeBoundingVolumes(shape, dimensions);
402
+ if (defined(node.children)) {
403
+ for (let i = 0; i < 8; i++) {
404
+ const child = node.children[i];
405
+ recomputeBoundingVolumesRecursive(that, child);
406
+ }
407
+ }
408
+ }
409
+
410
+ /**
411
+ * @function
412
+ *
413
+ * @param {VoxelTraversal} that
414
+ * @param {KeyframeNode} keyframeNode
415
+ *
416
+ * @private
417
+ */
418
+ function requestData(that, keyframeNode) {
419
+ if (
420
+ that._simultaneousRequestCount >=
421
+ VoxelTraversal.simultaneousRequestCountMaximum
422
+ ) {
423
+ return;
424
+ }
425
+
426
+ const primitive = that._primitive;
427
+ const provider = primitive._provider;
428
+ const keyframe = keyframeNode.keyframe;
429
+ const spatialNode = keyframeNode.spatialNode;
430
+ const tileLevel = spatialNode.level;
431
+ const tileX = spatialNode.x;
432
+ const tileY = spatialNode.y;
433
+ const tileZ = spatialNode.z;
434
+
435
+ function postRequestSuccess(result) {
436
+ that._simultaneousRequestCount--;
437
+ const length = primitive._provider.types.length;
438
+
439
+ if (!defined(result)) {
440
+ keyframeNode.state = KeyframeNode.LoadState.UNAVAILABLE;
441
+ } else if (result === KeyframeNode.LoadState.FAILED) {
442
+ keyframeNode.state = KeyframeNode.LoadState.FAILED;
443
+ } else if (!Array.isArray(result) || result.length !== length) {
444
+ // TODO should this throw runtime error?
445
+ keyframeNode.state = KeyframeNode.LoadState.FAILED;
446
+ } else {
447
+ const megatextures = that.megatextures;
448
+ for (let i = 0; i < length; i++) {
449
+ const { voxelCountPerTile, channelCount } = megatextures[i];
450
+ const { x, y, z } = voxelCountPerTile;
451
+ const tileVoxelCount = x * y * z;
452
+
453
+ const data = result[i];
454
+ const expectedLength = tileVoxelCount * channelCount;
455
+ if (data.length === expectedLength) {
456
+ keyframeNode.metadatas[i] = data;
457
+ // State is received only when all metadata requests have been received
458
+ keyframeNode.state = KeyframeNode.LoadState.RECEIVED;
459
+ } else {
460
+ keyframeNode.state = KeyframeNode.LoadState.FAILED;
461
+ break;
462
+ }
463
+ }
464
+ }
465
+ }
466
+
467
+ function postRequestFailure() {
468
+ that._simultaneousRequestCount--;
469
+ keyframeNode.state = KeyframeNode.LoadState.FAILED;
470
+ }
471
+
472
+ const promise = provider.requestData({
473
+ tileLevel: tileLevel,
474
+ tileX: tileX,
475
+ tileY: tileY,
476
+ tileZ: tileZ,
477
+ keyframe: keyframe,
478
+ });
479
+
480
+ if (defined(promise)) {
481
+ that._simultaneousRequestCount++;
482
+ keyframeNode.state = KeyframeNode.LoadState.RECEIVING;
483
+ promise.then(postRequestSuccess).catch(postRequestFailure);
484
+ } else {
485
+ keyframeNode.state = KeyframeNode.LoadState.FAILED;
486
+ }
487
+ }
488
+
489
+ /**
490
+ * @function
491
+ *
492
+ * @param {Number} x
493
+ * @returns {Number}
494
+ *
495
+ * @private
496
+ */
497
+ function mapInfiniteRangeToZeroOne(x) {
498
+ return x / (1.0 + x);
499
+ }
500
+
501
+ /**
502
+ * @function
503
+ *
504
+ * @param {VoxelTraversal} that
505
+ * @param {FrameState} frameState
506
+ *
507
+ * @private
508
+ */
509
+ function loadAndUnload(that, frameState) {
510
+ const frameNumber = that._frameNumber;
511
+ const primitive = that._primitive;
512
+ const shape = primitive._shape;
513
+ const voxelDimensions = primitive._provider.dimensions;
514
+ const targetScreenSpaceError = primitive._screenSpaceError;
515
+ const priorityQueue = that._priorityQueue;
516
+ const keyframeLocation = that._keyframeLocation;
517
+ const keyframeCount = that._keyframeCount;
518
+ const rootNode = that.rootNode;
519
+
520
+ const cameraPosition = frameState.camera.positionWC;
521
+ const screenSpaceErrorDenominator = frameState.camera.frustum.sseDenominator;
522
+ const screenHeight =
523
+ frameState.context.drawingBufferHeight / frameState.pixelRatio;
524
+ const screenSpaceErrorMultiplier = screenHeight / screenSpaceErrorDenominator;
525
+
526
+ /**
527
+ * @ignore
528
+ * @param {SpatialNode} spatialNode
529
+ * @param {Number} visibilityPlaneMask
530
+ */
531
+ function addToQueueRecursive(spatialNode, visibilityPlaneMask) {
532
+ spatialNode.computeScreenSpaceError(
533
+ cameraPosition,
534
+ screenSpaceErrorMultiplier
535
+ );
536
+
537
+ visibilityPlaneMask = spatialNode.visibility(
538
+ frameState,
539
+ visibilityPlaneMask
540
+ );
541
+ if (visibilityPlaneMask === CullingVolume.MASK_OUTSIDE) {
542
+ return;
543
+ }
544
+ spatialNode.visitedFrameNumber = frameNumber;
545
+
546
+ const previousKeyframe = CesiumMath.clamp(
547
+ Math.floor(keyframeLocation),
548
+ 0,
549
+ keyframeCount - 2
550
+ );
551
+ const nextKeyframe = previousKeyframe + 1;
552
+
553
+ // Create keyframe nodes at the playhead.
554
+ // If they already exist, nothing will be created.
555
+ if (keyframeCount === 1) {
556
+ spatialNode.createKeyframeNode(0);
557
+ } else if (spatialNode.keyframeNodes.length !== keyframeCount) {
558
+ for (let k = 0; k < keyframeCount; k++) {
559
+ spatialNode.createKeyframeNode(k);
560
+ }
561
+ }
562
+ const ssePriority = mapInfiniteRangeToZeroOne(spatialNode.screenSpaceError);
563
+
564
+ let hasLoadedKeyframe = false;
565
+ const keyframeNodes = spatialNode.keyframeNodes;
566
+ for (let i = 0; i < keyframeNodes.length; i++) {
567
+ const keyframeNode = keyframeNodes[i];
568
+ const keyframe = keyframeNode.keyframe;
569
+
570
+ // Balanced prioritization
571
+ const keyframeDifference = Math.min(
572
+ Math.abs(keyframe - previousKeyframe),
573
+ Math.abs(keyframe - nextKeyframe)
574
+ );
575
+ const maxKeyframeDifference = Math.max(
576
+ previousKeyframe,
577
+ keyframeCount - nextKeyframe - 1,
578
+ 1
579
+ );
580
+ const keyframeFactor = Math.pow(
581
+ 1.0 - keyframeDifference / maxKeyframeDifference,
582
+ 4.0
583
+ );
584
+ const binaryTreeFactor = Math.exp(
585
+ -that._binaryTreeKeyframeWeighting[keyframe]
586
+ );
587
+ keyframeNode.priority = 10.0 * ssePriority;
588
+ keyframeNode.priority += CesiumMath.lerp(
589
+ binaryTreeFactor,
590
+ keyframeFactor,
591
+ 0.15 + 0.85 * keyframeFactor
592
+ );
593
+
594
+ if (
595
+ keyframeNode.state !== KeyframeNode.LoadState.UNAVAILABLE &&
596
+ keyframeNode.state !== KeyframeNode.LoadState.FAILED &&
597
+ keyframeNode.priority !== -Number.MAX_VALUE
598
+ ) {
599
+ priorityQueue.insert(keyframeNode);
600
+ }
601
+ if (keyframeNode.state === KeyframeNode.LoadState.LOADED) {
602
+ hasLoadedKeyframe = true;
603
+ }
604
+ }
605
+
606
+ const meetsScreenSpaceError =
607
+ spatialNode.screenSpaceError < targetScreenSpaceError;
608
+ if (meetsScreenSpaceError || !hasLoadedKeyframe) {
609
+ // Free up memory
610
+ spatialNode.children = undefined;
611
+ return;
612
+ }
613
+
614
+ if (!defined(spatialNode.children)) {
615
+ const childCoords = getChildCoords(spatialNode);
616
+ const childLevel = spatialNode.level + 1;
617
+ spatialNode.children = childCoords.map(([x, y, z]) => {
618
+ return new SpatialNode(
619
+ childLevel,
620
+ x,
621
+ y,
622
+ z,
623
+ spatialNode,
624
+ shape,
625
+ voxelDimensions
626
+ );
627
+ });
628
+ }
629
+ for (let childIndex = 0; childIndex < 8; childIndex++) {
630
+ const child = spatialNode.children[childIndex];
631
+ addToQueueRecursive(child, visibilityPlaneMask);
632
+ }
633
+ }
634
+
635
+ priorityQueue.reset();
636
+ addToQueueRecursive(rootNode, CullingVolume.MASK_INDETERMINATE);
637
+
638
+ const highPriorityKeyframeNodes = that._highPriorityKeyframeNodes;
639
+ let highPriorityKeyframeNodeCount = 0;
640
+ let highPriorityKeyframeNode;
641
+ while (priorityQueue.length > 0) {
642
+ highPriorityKeyframeNode = priorityQueue.removeMaximum();
643
+ highPriorityKeyframeNode.highPriorityFrameNumber = frameNumber;
644
+ highPriorityKeyframeNodes[
645
+ highPriorityKeyframeNodeCount
646
+ ] = highPriorityKeyframeNode;
647
+ highPriorityKeyframeNodeCount++;
648
+ }
649
+
650
+ const keyframeNodesInMegatexture = that._keyframeNodesInMegatexture;
651
+ // TODO: some of the megatexture state should be stored once, not duplicate for each megatexture
652
+ const megatexture = that.megatextures[0];
653
+ const keyframeNodesInMegatextureCount = megatexture.occupiedCount;
654
+ keyframeNodesInMegatexture.length = keyframeNodesInMegatextureCount;
655
+ keyframeNodesInMegatexture.sort(function (a, b) {
656
+ if (a.highPriorityFrameNumber === b.highPriorityFrameNumber) {
657
+ return b.priority - a.priority;
658
+ }
659
+ return b.highPriorityFrameNumber - a.highPriorityFrameNumber;
660
+ });
661
+
662
+ let destroyedCount = 0;
663
+ let addedCount = 0;
664
+
665
+ for (
666
+ let highPriorityKeyframeNodeIndex = 0;
667
+ highPriorityKeyframeNodeIndex < highPriorityKeyframeNodeCount;
668
+ highPriorityKeyframeNodeIndex++
669
+ ) {
670
+ highPriorityKeyframeNode =
671
+ highPriorityKeyframeNodes[highPriorityKeyframeNodeIndex];
672
+
673
+ if (
674
+ highPriorityKeyframeNode.state === KeyframeNode.LoadState.LOADED ||
675
+ highPriorityKeyframeNode.spatialNode === undefined
676
+ ) {
677
+ // Already loaded, so nothing to do.
678
+ // Or destroyed when adding a higher priority node
679
+ continue;
680
+ }
681
+ if (highPriorityKeyframeNode.state === KeyframeNode.LoadState.UNLOADED) {
682
+ requestData(that, highPriorityKeyframeNode);
683
+ }
684
+ if (highPriorityKeyframeNode.state === KeyframeNode.LoadState.RECEIVED) {
685
+ let addNodeIndex = 0;
686
+ if (megatexture.isFull()) {
687
+ // If the megatexture is full, try removing a discardable node with the lowest priority.
688
+ addNodeIndex = keyframeNodesInMegatextureCount - 1 - destroyedCount;
689
+ destroyedCount++;
690
+
691
+ const discardNode = keyframeNodesInMegatexture[addNodeIndex];
692
+ discardNode.spatialNode.destroyKeyframeNode(
693
+ discardNode,
694
+ that.megatextures
695
+ );
696
+ } else {
697
+ addNodeIndex = keyframeNodesInMegatextureCount + addedCount;
698
+ addedCount++;
699
+ }
700
+ highPriorityKeyframeNode.spatialNode.addKeyframeNodeToMegatextures(
701
+ highPriorityKeyframeNode,
702
+ that.megatextures
703
+ );
704
+ keyframeNodesInMegatexture[addNodeIndex] = highPriorityKeyframeNode;
705
+ }
706
+ }
707
+ }
708
+
709
+ /**
710
+ * Compute the X, Y, Z coordinates of the children of a node
711
+ * @param {SpatialNode} spatialNode The parent node
712
+ * @returns {Array[]} Child coordinate arrays
713
+ * @private
714
+ */
715
+ function getChildCoords(spatialNode) {
716
+ const { x, y, z } = spatialNode;
717
+ const xMin = x * 2;
718
+ const yMin = y * 2;
719
+ const zMin = z * 2;
720
+ const yMax = yMin + 1;
721
+ const xMax = xMin + 1;
722
+ const zMax = zMin + 1;
723
+
724
+ return [
725
+ [xMin, yMin, zMin],
726
+ [xMax, yMin, zMin],
727
+ [xMin, yMax, zMin],
728
+ [xMax, yMax, zMin],
729
+ [xMin, yMin, zMax],
730
+ [xMax, yMin, zMax],
731
+ [xMin, yMax, zMax],
732
+ [xMax, yMax, zMax],
733
+ ];
734
+ }
735
+
736
+ /**
737
+ * @function
738
+ *
739
+ * @param {VoxelTraversal} that
740
+ *
741
+ * @private
742
+ */
743
+ function printDebugInformation(
744
+ that,
745
+ loadAndUnloadTimeMs,
746
+ generateOctreeTimeMs,
747
+ totalTimeMs
748
+ ) {
749
+ const keyframeCount = that._keyframeCount;
750
+ const rootNode = that.rootNode;
751
+
752
+ const loadStateCount = Object.keys(KeyframeNode.LoadState).length;
753
+ const loadStatesByKeyframe = new Array(loadStateCount);
754
+ const loadStateByCount = new Array(loadStateCount);
755
+ let nodeCountTotal = 0;
756
+
757
+ for (
758
+ let loadStateIndex = 0;
759
+ loadStateIndex < loadStateCount;
760
+ loadStateIndex++
761
+ ) {
762
+ const keyframeArray = new Array(keyframeCount);
763
+ loadStatesByKeyframe[loadStateIndex] = keyframeArray;
764
+ for (let i = 0; i < keyframeCount; i++) {
765
+ keyframeArray[i] = 0;
766
+ }
767
+ loadStateByCount[loadStateIndex] = 0;
768
+ }
769
+
770
+ /**
771
+ * @ignore
772
+ * @param {SpatialNode} node
773
+ */
774
+ function traverseRecursive(node) {
775
+ const keyframeNodes = node.keyframeNodes;
776
+ for (
777
+ let keyframeIndex = 0;
778
+ keyframeIndex < keyframeNodes.length;
779
+ keyframeIndex++
780
+ ) {
781
+ const keyframeNode = keyframeNodes[keyframeIndex];
782
+ const keyframe = keyframeNode.keyframe;
783
+ const state = keyframeNode.state;
784
+ loadStatesByKeyframe[state][keyframe] += 1;
785
+ loadStateByCount[state] += 1;
786
+ nodeCountTotal++;
787
+ }
788
+
789
+ if (defined(node.children)) {
790
+ for (let childIndex = 0; childIndex < 8; childIndex++) {
791
+ const child = node.children[childIndex];
792
+ traverseRecursive(child);
793
+ }
794
+ }
795
+ }
796
+ traverseRecursive(rootNode);
797
+
798
+ const loadedKeyframeStatistics = `KEYFRAMES: ${
799
+ loadStatesByKeyframe[KeyframeNode.LoadState.LOADED]
800
+ }`;
801
+ const loadStateStatistics =
802
+ `UNLOADED: ${loadStateByCount[KeyframeNode.LoadState.UNLOADED]} | ` +
803
+ `RECEIVING: ${loadStateByCount[KeyframeNode.LoadState.RECEIVING]} | ` +
804
+ `RECEIVED: ${loadStateByCount[KeyframeNode.LoadState.RECEIVED]} | ` +
805
+ `LOADED: ${loadStateByCount[KeyframeNode.LoadState.LOADED]} | ` +
806
+ `FAILED: ${loadStateByCount[KeyframeNode.LoadState.FAILED]} | ` +
807
+ `UNAVAILABLE: ${loadStateByCount[KeyframeNode.LoadState.UNAVAILABLE]} | ` +
808
+ `TOTAL: ${nodeCountTotal}`;
809
+
810
+ const loadAndUnloadTimeMsRounded =
811
+ Math.round(loadAndUnloadTimeMs * 100) / 100;
812
+ const generateOctreeTimeMsRounded =
813
+ Math.round(generateOctreeTimeMs * 100) / 100;
814
+ const totalTimeMsRounded = Math.round(totalTimeMs * 100) / 100;
815
+
816
+ const timerStatistics =
817
+ `LOAD: ${loadAndUnloadTimeMsRounded} | ` +
818
+ `OCT: ${generateOctreeTimeMsRounded} | ` +
819
+ `ALL: ${totalTimeMsRounded}`;
820
+
821
+ console.log(
822
+ `${loadedKeyframeStatistics} || ${loadStateStatistics} || ${timerStatistics}`
823
+ );
824
+ }
825
+
826
+ // GPU Octree Layout
827
+ // (shown as binary tree instead of octree for demonstration purposes)
828
+ //
829
+ // Tree representation:
830
+ // 0
831
+ // / \
832
+ // / \
833
+ // / \
834
+ // 1 3
835
+ // / \ / \
836
+ // L0 2 L3 L4
837
+ // / \
838
+ // L1 L2
839
+ //
840
+ //
841
+ // Array representation:
842
+ // L = leaf index
843
+ // * = index to parent node
844
+ // index: 0_______ 1________ 2________ 3_________
845
+ // array: [*0, 1, 3, *0, L0, 2, *1 L1, L2, *0, L3, L4]
846
+ //
847
+ // The array is generated from a depth-first traversal. The end result could be an unbalanced tree,
848
+ // so the parent index is stored at each node to make it possible to traverse upwards.
849
+
850
+ const GpuOctreeFlag = {
851
+ // Data is an octree index.
852
+ INTERNAL: 0,
853
+ // Data is a leaf node.
854
+ LEAF: 1,
855
+ // When leaf data is packed in the octree and there's a node that is forced to
856
+ // render but has no data of its own (such as when its siblings are renderable but it
857
+ // is not), signal that it's using its parent's data.
858
+ PACKED_LEAF_FROM_PARENT: 2,
859
+ };
860
+
861
+ /**
862
+ * @function
863
+ *
864
+ * @param {VoxelTraversal} that
865
+ * @param {FrameState} frameState
866
+ * @param {Number} sampleCount
867
+ * @param {Number} levelBlendFactor
868
+ * @private
869
+ */
870
+ function generateOctree(that, sampleCount, levelBlendFactor) {
871
+ const targetSse = that._primitive._screenSpaceError;
872
+ const keyframeLocation = that._keyframeLocation;
873
+ const frameNumber = that._frameNumber;
874
+ const useLeafNodes = sampleCount >= 2;
875
+
876
+ let internalNodeCount = 0;
877
+ let leafNodeCount = 0;
878
+ const internalNodeOctreeData = [];
879
+ const leafNodeOctreeData = [];
880
+
881
+ /**
882
+ * @ignore
883
+ * @param {SpatialNode} node
884
+ * @param {Number} childOctreeIndex
885
+ * @param {Number} childEntryIndex
886
+ * @param {Number} parentOctreeIndex
887
+ * @param {Number} parentEntryIndex
888
+ */
889
+ function buildOctree(
890
+ node,
891
+ childOctreeIndex,
892
+ childEntryIndex,
893
+ parentOctreeIndex,
894
+ parentEntryIndex
895
+ ) {
896
+ let hasRenderableChildren = false;
897
+ if (defined(node.children)) {
898
+ for (let c = 0; c < 8; c++) {
899
+ const childNode = node.children[c];
900
+ childNode.computeSurroundingRenderableKeyframeNodes(keyframeLocation);
901
+ if (childNode.isRenderable(frameNumber)) {
902
+ hasRenderableChildren = true;
903
+ }
904
+ }
905
+ }
906
+
907
+ if (hasRenderableChildren) {
908
+ // Point the parent and child octree indexes at each other
909
+ internalNodeOctreeData[parentEntryIndex] =
910
+ (GpuOctreeFlag.INTERNAL << 16) | childOctreeIndex;
911
+ internalNodeOctreeData[childEntryIndex] = parentOctreeIndex;
912
+ internalNodeCount++;
913
+
914
+ // Recurse over children
915
+ parentOctreeIndex = childOctreeIndex;
916
+ parentEntryIndex = parentOctreeIndex * 9 + 1;
917
+ for (let cc = 0; cc < 8; cc++) {
918
+ const child = node.children[cc];
919
+ childOctreeIndex = internalNodeCount;
920
+ childEntryIndex = childOctreeIndex * 9 + 0;
921
+ buildOctree(
922
+ child,
923
+ childOctreeIndex,
924
+ childEntryIndex,
925
+ parentOctreeIndex,
926
+ parentEntryIndex + cc
927
+ );
928
+ }
929
+ } else {
930
+ // Store the leaf node information instead
931
+ // Recursion stops here because there are no renderable children
932
+ if (useLeafNodes) {
933
+ const baseIdx = leafNodeCount * 5;
934
+ const keyframeNode = node.renderableKeyframeNodePrevious;
935
+ const levelDifference = node.level - keyframeNode.spatialNode.level;
936
+
937
+ const parentNode = keyframeNode.spatialNode.parent;
938
+ const parentKeyframeNode = defined(parentNode)
939
+ ? parentNode.renderableKeyframeNodePrevious
940
+ : keyframeNode;
941
+
942
+ const lodLerp = getLodLerp(node, targetSse, levelBlendFactor);
943
+ const levelDifferenceChild = levelDifference;
944
+ const levelDifferenceParent = 1;
945
+ const megatextureIndexChild = keyframeNode.megatextureIndex;
946
+ const megatextureIndexParent = parentKeyframeNode.megatextureIndex;
947
+
948
+ leafNodeOctreeData[baseIdx + 0] = lodLerp;
949
+ leafNodeOctreeData[baseIdx + 1] = levelDifferenceChild;
950
+ leafNodeOctreeData[baseIdx + 2] = levelDifferenceParent;
951
+ leafNodeOctreeData[baseIdx + 3] = megatextureIndexChild;
952
+ leafNodeOctreeData[baseIdx + 4] = megatextureIndexParent;
953
+
954
+ internalNodeOctreeData[parentEntryIndex] =
955
+ (GpuOctreeFlag.LEAF << 16) | leafNodeCount;
956
+ } else {
957
+ const keyframeNode = node.renderableKeyframeNodePrevious;
958
+ const levelDifference = node.level - keyframeNode.spatialNode.level;
959
+ const flag =
960
+ levelDifference === 0
961
+ ? GpuOctreeFlag.LEAF
962
+ : GpuOctreeFlag.PACKED_LEAF_FROM_PARENT;
963
+ internalNodeOctreeData[parentEntryIndex] =
964
+ (flag << 16) | keyframeNode.megatextureIndex;
965
+ }
966
+ leafNodeCount++;
967
+ }
968
+ }
969
+
970
+ const rootNode = that.rootNode;
971
+ rootNode.computeSurroundingRenderableKeyframeNodes(keyframeLocation);
972
+ if (rootNode.isRenderable(frameNumber)) {
973
+ buildOctree(rootNode, 0, 0, 0, 0);
974
+ }
975
+
976
+ copyToInternalNodeTexture(
977
+ internalNodeOctreeData,
978
+ 9,
979
+ that.internalNodeTilesPerRow,
980
+ that.internalNodeTexture
981
+ );
982
+ if (useLeafNodes) {
983
+ copyToLeafNodeTexture(
984
+ leafNodeOctreeData,
985
+ 2,
986
+ that.leafNodeTilesPerRow,
987
+ that.leafNodeTexture
988
+ );
989
+ }
990
+ }
991
+
992
+ /**
993
+ * Compute an interpolation factor between a node and its parent
994
+ * @param {SpatialNode} node
995
+ * @param {Number} targetSse
996
+ * @param {Number} levelBlendFactor
997
+ * @returns {Number}
998
+ * @private
999
+ */
1000
+ function getLodLerp(node, targetSse, levelBlendFactor) {
1001
+ if (node.parent === undefined) {
1002
+ return 0.0;
1003
+ }
1004
+ const sse = node.screenSpaceError;
1005
+ const parentSse = node.parent.screenSpaceError;
1006
+ const lodLerp = (targetSse - sse) / (parentSse - sse);
1007
+ const blended = (lodLerp + levelBlendFactor - 1.0) / levelBlendFactor;
1008
+
1009
+ return CesiumMath.clamp(blended, 0.0, 1.0);
1010
+ }
1011
+
1012
+ /**
1013
+ *
1014
+ * @param {Number[]} data
1015
+ * @param {Number} texelsPerTile
1016
+ * @param {Number} tilesPerRow
1017
+ * @param {Texture} texture
1018
+ * @private
1019
+ */
1020
+ function copyToInternalNodeTexture(data, texelsPerTile, tilesPerRow, texture) {
1021
+ const channelCount = PixelFormat.componentsLength(texture.pixelFormat);
1022
+ const tileCount = Math.ceil(data.length / texelsPerTile);
1023
+ const copyWidth = Math.max(
1024
+ 1,
1025
+ texelsPerTile * Math.min(tileCount, tilesPerRow)
1026
+ );
1027
+ const copyHeight = Math.max(1, Math.ceil(tileCount / tilesPerRow));
1028
+
1029
+ const textureData = new Uint8Array(copyWidth * copyHeight * channelCount);
1030
+ for (let i = 0; i < data.length; i++) {
1031
+ const val = data[i];
1032
+ const startIndex = i * channelCount;
1033
+ for (let j = 0; j < channelCount; j++) {
1034
+ textureData[startIndex + j] = (val >>> (j * 8)) & 0xff;
1035
+ }
1036
+ }
1037
+
1038
+ const source = {
1039
+ arrayBufferView: textureData,
1040
+ width: copyWidth,
1041
+ height: copyHeight,
1042
+ };
1043
+
1044
+ const copyOptions = {
1045
+ source: source,
1046
+ xOffset: 0,
1047
+ yOffset: 0,
1048
+ };
1049
+
1050
+ texture.copyFrom(copyOptions);
1051
+ }
1052
+
1053
+ /**
1054
+ *
1055
+ * @param {Number[]} data
1056
+ * @param {Number} texelsPerTile
1057
+ * @param {Number} tilesPerRow
1058
+ * @param {Texture} texture
1059
+ * @private
1060
+ */
1061
+ function copyToLeafNodeTexture(data, texelsPerTile, tilesPerRow, texture) {
1062
+ const channelCount = PixelFormat.componentsLength(texture.pixelFormat);
1063
+ const datasPerTile = 5;
1064
+ const tileCount = Math.ceil(data.length / datasPerTile);
1065
+ const copyWidth = Math.max(
1066
+ 1,
1067
+ texelsPerTile * Math.min(tileCount, tilesPerRow)
1068
+ );
1069
+ const copyHeight = Math.max(1, Math.ceil(tileCount / tilesPerRow));
1070
+
1071
+ const textureData = new Uint8Array(copyWidth * copyHeight * channelCount);
1072
+ for (let tileIndex = 0; tileIndex < tileCount; tileIndex++) {
1073
+ const timeLerp = data[tileIndex * datasPerTile + 0];
1074
+ const previousKeyframeLevelsAbove = data[tileIndex * datasPerTile + 1];
1075
+ const nextKeyframeLevelsAbove = data[tileIndex * datasPerTile + 2];
1076
+ const previousKeyframeMegatextureIndex = data[tileIndex * datasPerTile + 3];
1077
+ const nextKeyframeMegatextureIndex = data[tileIndex * datasPerTile + 4];
1078
+
1079
+ const timeLerpCompressed = CesiumMath.clamp(
1080
+ Math.floor(65536 * timeLerp),
1081
+ 0,
1082
+ 65535
1083
+ );
1084
+ textureData[tileIndex * 8 + 0] = (timeLerpCompressed >>> 0) & 0xff;
1085
+ textureData[tileIndex * 8 + 1] = (timeLerpCompressed >>> 8) & 0xff;
1086
+ textureData[tileIndex * 8 + 2] = previousKeyframeLevelsAbove & 0xff;
1087
+ textureData[tileIndex * 8 + 3] = nextKeyframeLevelsAbove & 0xff;
1088
+ textureData[tileIndex * 8 + 4] =
1089
+ (previousKeyframeMegatextureIndex >>> 0) & 0xff;
1090
+ textureData[tileIndex * 8 + 5] =
1091
+ (previousKeyframeMegatextureIndex >>> 8) & 0xff;
1092
+ textureData[tileIndex * 8 + 6] =
1093
+ (nextKeyframeMegatextureIndex >>> 0) & 0xff;
1094
+ textureData[tileIndex * 8 + 7] =
1095
+ (nextKeyframeMegatextureIndex >>> 8) & 0xff;
1096
+ }
1097
+
1098
+ const source = {
1099
+ arrayBufferView: textureData,
1100
+ width: copyWidth,
1101
+ height: copyHeight,
1102
+ };
1103
+
1104
+ const copyOptions = {
1105
+ source: source,
1106
+ xOffset: 0,
1107
+ yOffset: 0,
1108
+ };
1109
+
1110
+ texture.copyFrom(copyOptions);
1111
+ }
1112
+
1113
+ /**
1114
+ * @param {Number} tileCount
1115
+ * @param {Cartesian3} dimensions
1116
+ * @param {MetadataType[]} types
1117
+ * @param {MetadataComponentType[]} componentTypes
1118
+ */
1119
+ VoxelTraversal.getApproximateTextureMemoryByteLength = function (
1120
+ tileCount,
1121
+ dimensions,
1122
+ types,
1123
+ componentTypes
1124
+ ) {
1125
+ let textureMemoryByteLength = 0;
1126
+
1127
+ const length = types.length;
1128
+ for (let i = 0; i < length; i++) {
1129
+ const type = types[i];
1130
+ const componentType = componentTypes[i];
1131
+ const componentCount = MetadataType.getComponentCount(type);
1132
+
1133
+ textureMemoryByteLength += Megatexture.getApproximateTextureMemoryByteLength(
1134
+ tileCount,
1135
+ dimensions,
1136
+ componentCount,
1137
+ componentType
1138
+ );
1139
+ }
1140
+
1141
+ return textureMemoryByteLength;
1142
+ };
1143
+
1144
+ export default VoxelTraversal;