@loaders.gl/tile-converter 5.0.0-alpha.12 → 5.0.0-alpha.14

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 (132) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +4 -4
  3. package/dist/converter.min.cjs +149 -158
  4. package/dist/i3s-server/bin/i3s-server.min.cjs +113 -158
  5. package/dist/index.cjs +17486 -17479
  6. package/dist/index.js +25572 -25560
  7. package/dist/v5/adapters.cjs +540 -190
  8. package/dist/v5/adapters.d.ts +4 -0
  9. package/dist/v5/adapters.js +501 -151
  10. package/dist/v5/archive-source.d.ts +1 -1
  11. package/dist/v5/browser.d.ts +1 -1
  12. package/dist/v5/conversion-api.d.ts +25 -0
  13. package/dist/v5/converter-cli.js +23 -7
  14. package/dist/v5/core.cjs +76 -17
  15. package/dist/v5/core.d.ts +1 -1
  16. package/dist/v5/core.js +69 -10
  17. package/dist/v5/feature-arrow.d.ts +1 -1
  18. package/dist/v5/i3s-mesh-conversion.d.ts +1 -1
  19. package/dist/v5/i3s-mesh-source-features.d.ts +2 -2
  20. package/dist/v5/i3s-mesh-source-material.d.ts +1 -1
  21. package/dist/v5/i3s-mesh-source.d.ts +2 -2
  22. package/dist/v5/i3s-selection-source.d.ts +3 -3
  23. package/dist/v5/index.cjs +11 -6
  24. package/dist/v5/index.d.ts +5 -1
  25. package/dist/v5/index.js +8 -0
  26. package/dist/v5/mesh-conversion.d.ts +3 -3
  27. package/dist/v5/mesh-draco.d.ts +2 -2
  28. package/dist/v5/mesh-features.d.ts +2 -2
  29. package/dist/v5/mesh-source-features.d.ts +4 -4
  30. package/dist/v5/mesh-source-schema.d.ts +2 -2
  31. package/dist/v5/mesh-source-texture.d.ts +3 -3
  32. package/dist/v5/mesh-source.d.ts +5 -5
  33. package/dist/v5/mesh-tileset-sink.d.ts +1 -1
  34. package/dist/v5/mesh.d.ts +1 -1
  35. package/dist/v5/node-conversion.d.ts +2 -0
  36. package/dist/v5/node-i3s-source.d.ts +3 -3
  37. package/dist/v5/node-source.d.ts +1 -1
  38. package/dist/v5/node.cjs +19 -6
  39. package/dist/v5/node.js +19 -6
  40. package/dist/v5/point-cloud-attributes.d.ts +19 -0
  41. package/dist/v5/point-cloud-conversion.d.ts +1 -1
  42. package/dist/v5/point-cloud-copc.d.ts +44 -0
  43. package/dist/v5/point-cloud-file.d.ts +4 -4
  44. package/dist/v5/point-cloud-potree.d.ts +3 -3
  45. package/dist/v5/point-cloud-source-encoder.d.ts +1 -1
  46. package/dist/v5/point-cloud-source.d.ts +1 -1
  47. package/dist/v5/point-cloud-tileset.d.ts +3 -3
  48. package/dist/v5/point-cloud.d.ts +1 -1
  49. package/dist/v5/spatial-conversion.d.ts +1 -1
  50. package/dist/v5/tileset-source.d.ts +1 -1
  51. package/package.json +17 -32
  52. package/src/v4/3d-tiles-converter/3d-tiles-converter.ts +11 -12
  53. package/src/v4/3d-tiles-converter/helpers/3d-tiles-content-converter.ts +7 -3
  54. package/src/v4/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.ts +2 -2
  55. package/src/v4/3d-tiles-converter/helpers/load-i3s.ts +3 -3
  56. package/src/v4/3d-tiles-converter/helpers/texture-atlas.ts +1 -1
  57. package/src/v4/3d-tiles-converter/helpers/validate-i3s-spatial-reference.ts +2 -2
  58. package/src/v4/3d-tiles-converter/json-templates/tileset.ts +58 -39
  59. package/src/v4/converter-cli.ts +3 -3
  60. package/src/v4/deps-installer/deps-installer.ts +3 -3
  61. package/src/v4/i3s-converter/helpers/attribute-metadata-info.ts +1 -1
  62. package/src/v4/i3s-converter/helpers/batch-ids-extensions.ts +9 -10
  63. package/src/v4/i3s-converter/helpers/conversion-gltf.ts +108 -0
  64. package/src/v4/i3s-converter/helpers/coordinate-converter.ts +6 -5
  65. package/src/v4/i3s-converter/helpers/create-scene-server-path.ts +5 -6
  66. package/src/v4/i3s-converter/helpers/feature-attributes.ts +4 -4
  67. package/src/v4/i3s-converter/helpers/geometry-attributes.ts +1 -1
  68. package/src/v4/i3s-converter/helpers/geometry-converter.ts +69 -77
  69. package/src/v4/i3s-converter/helpers/gltf-attributes.ts +58 -74
  70. package/src/v4/i3s-converter/helpers/load-3d-tiles.ts +5 -5
  71. package/src/v4/i3s-converter/helpers/node-debug.ts +7 -3
  72. package/src/v4/i3s-converter/helpers/node-index-document.ts +6 -7
  73. package/src/v4/i3s-converter/helpers/node-pages.ts +3 -4
  74. package/src/v4/i3s-converter/helpers/preprocess-3d-tiles.ts +1 -1
  75. package/src/v4/i3s-converter/helpers/tileset-traversal.ts +1 -1
  76. package/src/v4/i3s-converter/i3s-converter.ts +29 -31
  77. package/src/v4/i3s-converter/json-templates/geometry-definitions.ts +71 -66
  78. package/src/v4/i3s-converter/json-templates/layers.ts +97 -132
  79. package/src/v4/i3s-converter/json-templates/metadata.ts +31 -21
  80. package/src/v4/i3s-converter/json-templates/node.ts +60 -74
  81. package/src/v4/i3s-converter/json-templates/scene-server.ts +36 -27
  82. package/src/v4/i3s-converter/json-templates/shared-resources.ts +62 -123
  83. package/src/v4/i3s-converter/json-templates/store.ts +39 -58
  84. package/src/v4/i3s-converter/types.ts +4 -4
  85. package/src/v4/i3s-server/controllers/slpk-controller.ts +3 -3
  86. package/src/v4/i3s-server/utils/create-scene-server.ts +3 -3
  87. package/src/v4/lib/utils/builder-input.ts +8 -0
  88. package/src/v4/lib/utils/conversion-dump.ts +5 -1
  89. package/src/v4/lib/utils/file-utils.ts +1 -1
  90. package/src/v4/lib/utils/hash-string.ts +21 -0
  91. package/src/v4/lib/utils/lod-conversion-utils.ts +3 -3
  92. package/src/v4/pgm-loader.ts +2 -2
  93. package/src/v5/adapters.ts +5 -0
  94. package/src/v5/archive-source.ts +4 -4
  95. package/src/v5/browser.ts +1 -0
  96. package/src/v5/conversion-api.ts +115 -8
  97. package/src/v5/conversion-archive.ts +2 -2
  98. package/src/v5/core.ts +1 -0
  99. package/src/v5/feature-arrow.ts +2 -2
  100. package/src/v5/i3s-mesh-conversion.ts +2 -2
  101. package/src/v5/i3s-mesh-source-features.ts +3 -3
  102. package/src/v5/i3s-mesh-source-material.ts +18 -11
  103. package/src/v5/i3s-mesh-source.ts +2 -2
  104. package/src/v5/i3s-selection-source.ts +4 -4
  105. package/src/v5/index.ts +15 -0
  106. package/src/v5/mesh-conversion.ts +4 -4
  107. package/src/v5/mesh-draco.ts +2 -2
  108. package/src/v5/mesh-features.ts +3 -3
  109. package/src/v5/mesh-hierarchy.ts +1 -1
  110. package/src/v5/mesh-source-features.ts +137 -55
  111. package/src/v5/mesh-source-schema.ts +10 -4
  112. package/src/v5/mesh-source-texture.ts +49 -19
  113. package/src/v5/mesh-source.ts +65 -46
  114. package/src/v5/mesh-tileset-archive.ts +1 -1
  115. package/src/v5/mesh-tileset-sink.ts +1 -1
  116. package/src/v5/mesh.ts +1 -1
  117. package/src/v5/node-cli.ts +4 -0
  118. package/src/v5/node-conversion.ts +19 -2
  119. package/src/v5/node-source.ts +2 -2
  120. package/src/v5/point-cloud-attributes.ts +113 -0
  121. package/src/v5/point-cloud-conversion.ts +1 -1
  122. package/src/v5/point-cloud-copc.ts +289 -0
  123. package/src/v5/point-cloud-file.ts +8 -8
  124. package/src/v5/point-cloud-potree.ts +8 -4
  125. package/src/v5/point-cloud-source-encoder.ts +1 -1
  126. package/src/v5/point-cloud-source.ts +1 -1
  127. package/src/v5/point-cloud-spatial.ts +2 -2
  128. package/src/v5/point-cloud-tileset.ts +4 -4
  129. package/src/v5/point-cloud.ts +3 -3
  130. package/src/v5/selected-mesh-source.ts +4 -4
  131. package/src/v5/spatial-conversion.ts +2 -2
  132. package/src/v5/tileset-source.ts +2 -2
@@ -1,5 +1,5 @@
1
1
  // src/v5/feature-arrow.ts
2
- import { ArrowTableBuilder } from "@loaders.gl/schema-utils";
2
+ import { ArrowTableBuilder } from "@loaders.gl/core/schema-utils";
3
3
  import { TileConversionError } from "@loaders.gl/tile-converter/v5/core";
4
4
  var DEFAULT_FEATURE_ID_FIELD = "feature_id";
5
5
  var FEATURE_CLASS_METADATA_KEY = "loaders.gl:feature-class";
@@ -321,8 +321,8 @@ function isBinaryType(dataType) {
321
321
  }
322
322
 
323
323
  // src/v5/point-cloud.ts
324
- import { Tile3DWriter, TILE3D_TYPE } from "@loaders.gl/3d-tiles";
325
- import { convertTableToMesh } from "@loaders.gl/schema-utils";
324
+ import { Tile3DWriter, TILE3D_TYPE } from "@loaders.gl/tiles-3d/3d-tiles";
325
+ import { convertTableToMesh } from "@loaders.gl/core/schema-utils";
326
326
  import { TileConversionError as TileConversionError2 } from "@loaders.gl/tile-converter/v5/core";
327
327
  function encodePointCloudTile(pointBatch, options = {}) {
328
328
  return encodePointCloudTileWithMetadata(pointBatch, options).pnts;
@@ -585,8 +585,8 @@ function validateBatchTable(batchTableJson, batchIds) {
585
585
 
586
586
  // src/v5/point-cloud-spatial.ts
587
587
  import { Matrix4 } from "@math.gl/core";
588
- import { getSpatialCoordinateFrame, createTilesetSpatialReference } from "@loaders.gl/tiles";
589
- import { convertTableToMesh as convertTableToMesh2, makeMeshArrowTable } from "@loaders.gl/schema-utils";
588
+ import { getSpatialCoordinateFrame, createTilesetSpatialReference } from "@loaders.gl/tiles-3d";
589
+ import { convertTableToMesh as convertTableToMesh2, makeMeshArrowTable } from "@loaders.gl/core/schema-utils";
590
590
  import { TileConversionError as TileConversionError3 } from "@loaders.gl/tile-converter/v5/core";
591
591
  async function transformPointCloudSourceTile(sourceTile, options) {
592
592
  options.signal?.throwIfAborted();
@@ -844,7 +844,7 @@ import { GLTFScenegraph, GLTFWriter } from "@loaders.gl/gltf";
844
844
  import { getBinaryImageMetadata } from "@loaders.gl/images";
845
845
 
846
846
  // src/v5/mesh-features.ts
847
- import { convertArrowToSchema } from "@loaders.gl/schema-utils";
847
+ import { convertArrowToSchema } from "@loaders.gl/core/schema-utils";
848
848
  import { TileConversionError as TileConversionError5 } from "@loaders.gl/tile-converter/v5/core";
849
849
  function prepareMeshFeatureGeometry(mesh, features) {
850
850
  if (!features) return mesh;
@@ -1398,7 +1398,7 @@ function validateMeshTextureTransform(transform) {
1398
1398
  }
1399
1399
 
1400
1400
  // src/v5/mesh-conversion.ts
1401
- import { getSpatialCoordinateFrame as getSpatialCoordinateFrame2 } from "@loaders.gl/tiles";
1401
+ import { getSpatialCoordinateFrame as getSpatialCoordinateFrame2 } from "@loaders.gl/tiles-3d";
1402
1402
  import { TileConversionError as TileConversionError8 } from "@loaders.gl/tile-converter/v5/core";
1403
1403
 
1404
1404
  // src/v5/mesh-draco.ts
@@ -1888,8 +1888,8 @@ function createMeshTilesetSink(options) {
1888
1888
  import {
1889
1889
  Tiles3DArchiveWriter,
1890
1890
  encodeTiles3DArchiveInBatches
1891
- } from "@loaders.gl/3d-tiles/3d-tiles-archive-writer";
1892
- import { SLPKWriter, encodeSLPKArchiveInBatches } from "@loaders.gl/i3s/i3s-slpk-writer";
1891
+ } from "@loaders.gl/tiles-3d/3d-tiles/3d-tiles-archive-writer";
1892
+ import { SLPKWriter, encodeSLPKArchiveInBatches } from "@loaders.gl/tiles-3d/i3s/i3s-slpk-writer";
1893
1893
  import { TileConversionError as TileConversionError10 } from "@loaders.gl/tile-converter/v5/core";
1894
1894
  async function createTileConversionArchive(files, options) {
1895
1895
  const { resources, format, maxArchiveBytes, signal } = prepareArchiveInput(files, options);
@@ -1924,7 +1924,7 @@ function prepareArchiveInput(files, options) {
1924
1924
  }
1925
1925
 
1926
1926
  // src/v5/mesh-tileset-archive.ts
1927
- import { Tiles3DArchiveWriter as Tiles3DArchiveWriter2 } from "@loaders.gl/3d-tiles/3d-tiles-archive-writer";
1927
+ import { Tiles3DArchiveWriter as Tiles3DArchiveWriter2 } from "@loaders.gl/tiles-3d/3d-tiles/3d-tiles-archive-writer";
1928
1928
  import { TileConversionError as TileConversionError11 } from "@loaders.gl/tile-converter/v5/core";
1929
1929
  async function createSingleMeshTilesetArchive(files, options) {
1930
1930
  const { maxArchiveBytes, signal } = options;
@@ -1947,16 +1947,18 @@ async function createSingleMeshTilesetArchive(files, options) {
1947
1947
 
1948
1948
  // src/v5/mesh-source.ts
1949
1949
  import { Matrix4 as Matrix42 } from "@math.gl/core";
1950
+ import { getGLTFAccessor as getGLTFAccessor2, getGLTFAccessorValue as getGLTFAccessorValue2 } from "@loaders.gl/gltf";
1950
1951
  import { TileConversionError as TileConversionError15 } from "@loaders.gl/tile-converter/v5/core";
1951
1952
  import { createTilesetConversionSource } from "@loaders.gl/tile-converter/v5/core";
1952
1953
 
1953
1954
  // src/v5/mesh-source-texture.ts
1954
1955
  import { getBinaryImageMetadata as getBinaryImageMetadata2 } from "@loaders.gl/images";
1956
+ import { GLTFScenegraph as GLTFScenegraph2 } from "@loaders.gl/gltf";
1955
1957
  import { TileConversionError as TileConversionError12 } from "@loaders.gl/tile-converter/v5/core";
1956
- function mapMeshSourceTexture(textureInfo) {
1957
- const texture = textureInfo?.texture;
1958
- const image = texture?.source;
1959
- const sampler = texture?.sampler;
1958
+ function mapMeshSourceTexture(gltf, textureInfo) {
1959
+ const texture = textureInfo ? gltf.json.textures?.[textureInfo.index] : void 0;
1960
+ const image = texture?.source === void 0 ? void 0 : gltf.json.images?.[texture.source];
1961
+ const sampler = texture?.sampler === void 0 ? void 0 : gltf.json.samplers?.[texture.sampler];
1960
1962
  const extensions = textureInfo?.extensions || {};
1961
1963
  const hasTransform = Object.hasOwn(extensions, "KHR_texture_transform");
1962
1964
  const transform = extensions.KHR_texture_transform;
@@ -1966,7 +1968,7 @@ function mapMeshSourceTexture(textureInfo) {
1966
1968
  (name) => !["id", "index", "texture", "texCoord", "extensions", "extras"].includes(name)
1967
1969
  ) || !texture || Object.keys(texture).some(
1968
1970
  (name) => !["id", "source", "sampler", "name", "extensions", "extras"].includes(name)
1969
- ) || Object.keys(texture.extensions || {}).length || !image || image.uri !== void 0 && image.bufferView !== void 0 || Object.keys(image.extensions || {}).length || image.uri === void 0 && (!(image.bufferView?.data instanceof Uint8Array) || Object.keys(image.bufferView.extensions || {}).length || !["image/png", "image/jpeg"].includes(image.mimeType || "")) || sampler && (Object.keys(sampler.extensions || {}).length || Object.keys(sampler).some(
1971
+ ) || Object.keys(texture.extensions || {}).length || !image || image.uri !== void 0 && image.bufferView !== void 0 || Object.keys(image.extensions || {}).length || image.uri === void 0 && (image.bufferView === void 0 || !gltf.json.bufferViews?.[image.bufferView] || Object.keys(gltf.json.bufferViews?.[image.bufferView]?.extensions || {}).length || !["image/png", "image/jpeg"].includes(image.mimeType || "")) || sampler && (Object.keys(sampler.extensions || {}).length || Object.keys(sampler).some(
1970
1972
  (name) => ![
1971
1973
  "id",
1972
1974
  "parameters",
@@ -1991,7 +1993,7 @@ function mapMeshSourceTexture(textureInfo) {
1991
1993
  minFilter: sampler.minFilter
1992
1994
  } : void 0;
1993
1995
  return {
1994
- ...mapMeshSourceImage(image),
1996
+ ...mapMeshSourceImage(gltf, image),
1995
1997
  ...hasTransform ? {
1996
1998
  transform: {
1997
1999
  offset: transform.offset,
@@ -2002,9 +2004,12 @@ function mapMeshSourceTexture(textureInfo) {
2002
2004
  ...selectedSampler && Object.values(selectedSampler).some((value) => value !== void 0) ? { sampler: selectedSampler } : {}
2003
2005
  };
2004
2006
  }
2005
- function mapMeshSourceImage(image) {
2007
+ function mapMeshSourceImage(gltf, image) {
2006
2008
  if (image.uri === void 0) {
2007
- return { data: image.bufferView.data, mimeType: image.mimeType };
2009
+ return {
2010
+ data: new GLTFScenegraph2(gltf).getTypedArrayForBufferView(image.bufferView),
2011
+ mimeType: image.mimeType
2012
+ };
2008
2013
  }
2009
2014
  const match = typeof image.uri === "string" ? /^data:(image\/(?:png|jpeg));base64,([A-Za-z0-9+/]+={0,2})$/.exec(image.uri) : null;
2010
2015
  if (!match || image.mimeType !== void 0 && image.mimeType !== match[1]) {
@@ -2024,10 +2029,11 @@ function mapMeshSourceImage(image) {
2024
2029
  mimeType: match[1]
2025
2030
  };
2026
2031
  }
2027
- async function resolveMeshSourceTexture(textureInfo, readExternalResource, contentUri, signal) {
2028
- const image = textureInfo?.texture?.source;
2032
+ async function resolveMeshSourceTexture(gltf, textureInfo, readExternalResource, contentUri, signal) {
2033
+ const texture = textureInfo ? gltf.json.textures?.[textureInfo.index] : void 0;
2034
+ const image = texture?.source === void 0 ? void 0 : gltf.json.images?.[texture.source];
2029
2035
  if (!image || image.uri === void 0 || image.uri.startsWith("data:"))
2030
- return mapMeshSourceTexture(textureInfo);
2036
+ return mapMeshSourceTexture(gltf, textureInfo);
2031
2037
  if (!readExternalResource || image.bufferView !== void 0)
2032
2038
  throw new TileConversionError12(
2033
2039
  "MESH_SOURCE_TEXTURE_UNSUPPORTED",
@@ -2046,16 +2052,39 @@ async function resolveMeshSourceTexture(textureInfo, readExternalResource, conte
2046
2052
  "MESH_SOURCE_TEXTURE_UNSUPPORTED",
2047
2053
  "External images require matching PNG/JPEG headers"
2048
2054
  );
2055
+ const resolvedImage = {
2056
+ ...image,
2057
+ uri: void 0,
2058
+ mimeType: metadata.mimeType,
2059
+ bufferView: (gltf.json.bufferViews || []).length
2060
+ };
2049
2061
  const resolved = {
2050
- ...textureInfo,
2051
- texture: {
2052
- ...textureInfo.texture,
2053
- source: { ...image, uri: void 0, mimeType: metadata.mimeType, bufferView: { data } }
2062
+ ...gltf,
2063
+ buffers: [
2064
+ ...gltf.buffers,
2065
+ {
2066
+ arrayBuffer: data.buffer,
2067
+ byteOffset: data.byteOffset,
2068
+ byteLength: data.byteLength
2069
+ }
2070
+ ],
2071
+ json: {
2072
+ ...gltf.json,
2073
+ images: gltf.json.images.map(
2074
+ (entry, index) => index === texture.source ? resolvedImage : entry
2075
+ ),
2076
+ bufferViews: [
2077
+ ...gltf.json.bufferViews || [],
2078
+ { buffer: gltf.buffers.length, byteLength: data.byteLength }
2079
+ ]
2054
2080
  }
2055
2081
  };
2056
- return mapMeshSourceTexture(resolved);
2082
+ return mapMeshSourceTexture(resolved, textureInfo);
2057
2083
  }
2058
2084
 
2085
+ // src/v5/mesh-source-features.ts
2086
+ import { getGLTFAccessor, getGLTFAccessorValue } from "@loaders.gl/gltf";
2087
+
2059
2088
  // src/v5/mesh-source-schema.ts
2060
2089
  import { TileConversionError as TileConversionError13 } from "@loaders.gl/tile-converter/v5/core";
2061
2090
  var SCALAR_TYPES = {
@@ -2141,9 +2170,9 @@ function createMeshSourceFeatureOptions(gltf, options) {
2141
2170
  import { TileConversionError as TileConversionError14 } from "@loaders.gl/tile-converter/v5/core";
2142
2171
  function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2143
2172
  const declarations = primitive.extensions?.EXT_mesh_features?.featureIds;
2144
- const metadata = gltf.extensions?.EXT_structural_metadata;
2173
+ const metadata = gltf.json.extensions?.EXT_structural_metadata;
2145
2174
  const legacy = primitive.attributes._BATCHID;
2146
- if (!declarations && !legacy && !metadata && !Object.keys(payload.batchTableJson || {}).length && !payload.header?.batchLength) {
2175
+ if (!declarations && legacy === void 0 && !metadata && !Object.keys(payload.batchTableJson || {}).length && !payload.header?.batchLength) {
2147
2176
  if (Object.keys(primitive.attributes).some((name) => /^_FEATURE_ID_\d+$/.test(name)))
2148
2177
  throw new TileConversionError14(
2149
2178
  "MESH_FEATURE_MAPPING_REQUIRED",
@@ -2161,37 +2190,38 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2161
2190
  "MESH_FEATURE_CLASS_REQUIRED",
2162
2191
  "Feature metadata class must be explicit"
2163
2192
  );
2164
- const mapping = "schema" in options ? options : createMeshSourceFeatureOptions(gltf, options);
2193
+ const mapping = "schema" in options ? options : createMeshSourceFeatureOptions(gltf.json, options);
2194
+ const propertyMappings = /* @__PURE__ */ new Map();
2165
2195
  let count;
2166
- let attribute = legacy;
2196
+ let attributeReference = legacy;
2167
2197
  let properties;
2168
2198
  if (declarations) {
2169
- if (legacy || declarations.length !== 1 || metadata?.propertyTables?.length !== 1 || metadata.propertyTextures?.length || metadata.propertyAttributes?.length)
2199
+ if (legacy !== void 0 || declarations.length !== 1 || metadata?.propertyTables?.length !== 1 || metadata.propertyTextures?.length || metadata.propertyAttributes?.length)
2170
2200
  throw new TileConversionError14(
2171
2201
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2172
2202
  "Only one attribute-backed feature set and one property table are supported"
2173
2203
  );
2174
2204
  const declaration = declarations[0];
2175
- if (!Number.isInteger(declaration.attribute) || declaration.attribute < 0 || declaration.propertyTable !== 0 || declaration.texture || declaration.implicit || declaration.nullFeatureId !== void 0)
2205
+ if (typeof declaration.attribute !== "number" || !Number.isInteger(declaration.attribute) || declaration.attribute < 0 || declaration.propertyTable !== 0 || declaration.texture || declaration.implicit || declaration.nullFeatureId !== void 0)
2176
2206
  throw new TileConversionError14(
2177
2207
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2178
2208
  "Features require an attribute and property table 0 without null/texture/implicit IDs"
2179
2209
  );
2180
- attribute = primitive.attributes[`_FEATURE_ID_${declaration.attribute}`];
2210
+ attributeReference = primitive.attributes[`_FEATURE_ID_${declaration.attribute}`];
2181
2211
  const table = metadata.propertyTables[0];
2182
- if (table.class !== options.metadataClass || table.count !== declaration.featureCount)
2212
+ if (table.class !== options.metadataClass || !Number.isSafeInteger(declaration.featureCount) || declaration.featureCount <= 0 || declaration.featureCount > table.count)
2183
2213
  throw new TileConversionError14(
2184
2214
  "MESH_FEATURE_CLASS_MISMATCH",
2185
- "Feature class and declared table counts must agree"
2215
+ "Feature class must agree and declared feature count must fit the property table"
2186
2216
  );
2187
2217
  count = table.count;
2188
- if (!Number.isSafeInteger(count) || count <= 0 || count > mesh.attributes.POSITION.value.length / 3) {
2218
+ if (!Number.isSafeInteger(count) || count <= 0 || count > 4294967295) {
2189
2219
  throw new TileConversionError14(
2190
2220
  "MESH_FEATURE_IDS_INVALID",
2191
- "Feature row count exceeds the available mesh vertices"
2221
+ "Feature row count must fit uint32 indices"
2192
2222
  );
2193
2223
  }
2194
- properties = {};
2224
+ properties = /* @__PURE__ */ Object.create(null);
2195
2225
  const classProperties = metadata.schema?.classes?.[table.class]?.properties;
2196
2226
  if (!classProperties)
2197
2227
  throw new TileConversionError14(
@@ -2224,9 +2254,10 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2224
2254
  );
2225
2255
  }
2226
2256
  const defaultValue = integer64 && property.default !== void 0 ? BigInt(property.default) : property.default ?? null;
2227
- const decoded = data ?? Array.from({ length: count }, () => defaultValue);
2257
+ const decoded = data;
2228
2258
  if (property.noData === void 0 || data === void 0) {
2229
- properties[name] = decoded;
2259
+ if (decoded !== void 0) properties[name] = decoded;
2260
+ else propertyMappings.set(name, { defaultValue });
2230
2261
  continue;
2231
2262
  }
2232
2263
  const sentinel = property.noData;
@@ -2236,10 +2267,9 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2236
2267
  `Property ${name} requires a valid decoded noData mapping`
2237
2268
  );
2238
2269
  const comparable = integer64 ? BigInt(sentinel) : sentinel;
2239
- properties[name] = Array.from(
2240
- decoded,
2241
- (value) => value === comparable ? defaultValue : value
2242
- );
2270
+ const values = decoded;
2271
+ properties[name] = values;
2272
+ propertyMappings.set(name, { noData: comparable, defaultValue });
2243
2273
  }
2244
2274
  } else {
2245
2275
  if (metadata)
@@ -2250,14 +2280,32 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2250
2280
  count = payload.header?.batchLength ?? Number(payload.featureTableJson?.BATCH_LENGTH);
2251
2281
  properties = payload.batchTableJson || {};
2252
2282
  }
2283
+ const accessor = attributeReference === void 0 ? void 0 : getGLTFAccessor(gltf, attributeReference);
2284
+ const attribute = accessor ? {
2285
+ ...accessor,
2286
+ components: accessor.type === "SCALAR" ? 1 : Number(accessor.type.slice(3)),
2287
+ value: getGLTFAccessorValue(gltf, accessor)
2288
+ } : void 0;
2253
2289
  const vertexCount = mesh.attributes.POSITION.value.length / 3;
2254
- if (!Number.isSafeInteger(count) || count <= 0 || !attribute || attribute.components !== 1 || attribute.normalized || attribute.value.length !== vertexCount || Array.from(attribute.value).some(
2255
- (value) => !Number.isSafeInteger(value) || value < 0 || value >= count
2256
- ))
2290
+ if (!Number.isSafeInteger(count) || count <= 0 || !attribute || attribute.components !== 1 || attribute.normalized || attribute.value.length !== vertexCount)
2257
2291
  throw new TileConversionError14(
2258
2292
  "MESH_FEATURE_IDS_INVALID",
2259
2293
  "Feature IDs must be one valid property-table row index per vertex"
2260
2294
  );
2295
+ const vertexRows = declarations ? /* @__PURE__ */ new Set() : void 0;
2296
+ for (const value of attribute.value) {
2297
+ if (!Number.isSafeInteger(value) || value < 0 || value >= count)
2298
+ throw new TileConversionError14(
2299
+ "MESH_FEATURE_IDS_INVALID",
2300
+ "Feature IDs must reference valid property-table rows"
2301
+ );
2302
+ vertexRows?.add(Number(value));
2303
+ }
2304
+ if (declarations && vertexRows.size !== declarations[0].featureCount)
2305
+ throw new TileConversionError14(
2306
+ "MESH_FEATURE_IDS_INVALID",
2307
+ "Declared feature count must match distinct vertex feature IDs"
2308
+ );
2261
2309
  const selectedAttribute = declarations ? `_FEATURE_ID_${declarations[0].attribute}` : "_BATCHID";
2262
2310
  if (Object.keys(primitive.attributes).some(
2263
2311
  (name) => (name === "_BATCHID" || /^_FEATURE_ID_\d+$/.test(name)) && name !== selectedAttribute
@@ -2270,39 +2318,70 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2270
2318
  if (!values || !Array.isArray(values) && !ArrayBuffer.isView(values) || values.length !== count)
2271
2319
  throw new TileConversionError14(
2272
2320
  "MESH_FEATURE_PROPERTY_UNAVAILABLE",
2273
- `Property ${name} must be a decoded column matching the table count`
2321
+ `Property ${name} must be a decoded column matching the feature row count`
2274
2322
  );
2275
2323
  }
2276
2324
  const sourceIdentifier = options.sourceFeatureIdProperty;
2277
- if (sourceIdentifier && !properties[sourceIdentifier])
2325
+ if (sourceIdentifier && !properties[sourceIdentifier] && !propertyMappings.has(sourceIdentifier))
2278
2326
  throw new TileConversionError14(
2279
2327
  "MESH_FEATURE_ID_PROPERTY_REQUIRED",
2280
2328
  "Stable source identifier property is missing"
2281
2329
  );
2282
- const batches = convertFeatureAttributesToArrowBatches(
2283
- Array.from({ length: count }, (_, index) => ({
2284
- featureId: sourceIdentifier ? properties[sourceIdentifier][index] : index,
2285
- metadataClass: options.metadataClass,
2286
- properties: Object.fromEntries(
2287
- Object.entries(properties).filter(([name]) => name !== sourceIdentifier).map(([name, values]) => [name, values[index]])
2288
- )
2289
- })),
2290
- mapping
2291
- );
2292
2330
  const indices = mesh.indices?.value;
2293
- const triangleFeatureIndices = new Uint32Array((indices?.length ?? vertexCount) / 3);
2294
- for (let triangle = 0; triangle < triangleFeatureIndices.length; triangle++) {
2295
- const corners = [0, 1, 2].map(
2296
- (corner) => indices?.[triangle * 3 + corner] ?? triangle * 3 + corner
2331
+ const cornerCount = indices?.length ?? vertexCount;
2332
+ if (cornerCount % 3)
2333
+ throw new TileConversionError14(
2334
+ "MESH_FEATURE_IDS_INVALID",
2335
+ "Triangle geometry requires complete triples"
2297
2336
  );
2298
- const rows = corners.map((corner) => attribute.value[corner]);
2299
- if (rows.some((row) => row !== rows[0]) && new Set(corners).size === 3)
2337
+ const triangleFeatureIndices = new Uint32Array(cornerCount / 3);
2338
+ const referencedRows = /* @__PURE__ */ new Set();
2339
+ const readFeatureRow = (corner) => {
2340
+ if (!Number.isSafeInteger(corner) || corner < 0 || corner >= vertexCount)
2341
+ throw new TileConversionError14(
2342
+ "MESH_FEATURE_IDS_INVALID",
2343
+ "Triangle indices must reference valid mesh vertices"
2344
+ );
2345
+ return Number(attribute.value[corner]);
2346
+ };
2347
+ for (let triangle = 0; triangle < triangleFeatureIndices.length; triangle++) {
2348
+ const firstCorner = indices?.[triangle * 3] ?? triangle * 3;
2349
+ const secondCorner = indices?.[triangle * 3 + 1] ?? triangle * 3 + 1;
2350
+ const thirdCorner = indices?.[triangle * 3 + 2] ?? triangle * 3 + 2;
2351
+ const firstRow = readFeatureRow(firstCorner);
2352
+ const secondRow = readFeatureRow(secondCorner);
2353
+ const thirdRow = readFeatureRow(thirdCorner);
2354
+ if ((firstRow !== secondRow || firstRow !== thirdRow) && firstCorner !== secondCorner && firstCorner !== thirdCorner && secondCorner !== thirdCorner)
2300
2355
  throw new TileConversionError14(
2301
2356
  "MESH_FEATURE_TRIANGLE_MIXED",
2302
2357
  "All vertices of a nondegenerate triangle must reference the same feature row"
2303
2358
  );
2304
- triangleFeatureIndices[triangle] = Number(rows[0]);
2359
+ triangleFeatureIndices[triangle] = firstRow;
2360
+ referencedRows.add(firstRow);
2305
2361
  }
2362
+ const selectedRows = [...referencedRows].sort((left, right) => left - right);
2363
+ const denseRows = new Map(selectedRows.map((row, index) => [row, index]));
2364
+ for (let triangle = 0; triangle < triangleFeatureIndices.length; triangle++) {
2365
+ triangleFeatureIndices[triangle] = denseRows.get(triangleFeatureIndices[triangle]);
2366
+ }
2367
+ const readProperty = (name, row) => {
2368
+ const values = properties[name];
2369
+ const propertyMapping = propertyMappings.get(name);
2370
+ if (!values) return propertyMapping?.defaultValue;
2371
+ const value = values[row];
2372
+ return propertyMapping && value === propertyMapping.noData ? propertyMapping.defaultValue : value;
2373
+ };
2374
+ const propertyNames = [
2375
+ .../* @__PURE__ */ new Set([...Object.keys(properties), ...propertyMappings.keys()])
2376
+ ].filter((name) => name !== sourceIdentifier);
2377
+ const batches = convertFeatureAttributesToArrowBatches(
2378
+ selectedRows.map((row) => ({
2379
+ featureId: sourceIdentifier ? readProperty(sourceIdentifier, row) : row,
2380
+ metadataClass: options.metadataClass,
2381
+ properties: Object.fromEntries(propertyNames.map((name) => [name, readProperty(name, row)]))
2382
+ })),
2383
+ mapping
2384
+ );
2306
2385
  return {
2307
2386
  batches,
2308
2387
  triangleFeatureIndices,
@@ -2361,7 +2440,8 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2361
2440
  "Mesh extraction requires decoded GLB/glTF or B3DM content"
2362
2441
  );
2363
2442
  }
2364
- const gltf = payload.gltf;
2443
+ const gltfWithBuffers = payload.gltf;
2444
+ const gltf = gltfWithBuffers.json;
2365
2445
  if (gltf.animations?.length || gltf.skins?.length || Object.keys(gltf.extensions || {}).some(
2366
2446
  (name) => !["EXT_structural_metadata", "CESIUM_RTC"].includes(name)
2367
2447
  )) {
@@ -2387,19 +2467,20 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2387
2467
  placement.multiplyRight(new Matrix42([1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1]));
2388
2468
  if (axis === "X")
2389
2469
  placement.multiplyRight(new Matrix42([0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1]));
2390
- const roots = gltf.scene?.nodes || (gltf.scenes?.length === 1 ? gltf.scenes[0].nodes : void 0);
2470
+ const roots = (gltf.scene !== void 0 ? gltf.scenes?.[gltf.scene]?.nodes : void 0) || (gltf.scenes?.length === 1 ? gltf.scenes[0].nodes : void 0);
2391
2471
  if (!roots?.length)
2392
2472
  throw new TileConversionError15(
2393
2473
  "MESH_SOURCE_SCENE_UNSUPPORTED",
2394
2474
  "A default scene or exactly one scene is required"
2395
2475
  );
2396
2476
  let primitiveIndex = 0;
2397
- const primitives = traverseMeshNodes(roots, placement);
2477
+ const primitives = traverseMeshNodes(gltfWithBuffers, roots, placement);
2398
2478
  let current = primitives.next();
2399
2479
  while (!current.done) {
2400
2480
  const selected = current.value;
2401
2481
  const next = primitives.next();
2402
2482
  const resource = await extractPrimitive(
2483
+ gltfWithBuffers,
2403
2484
  selected.primitive,
2404
2485
  selected.transform,
2405
2486
  `${item.tile.id}/${content.index}/${primitiveIndex++}`,
@@ -2408,7 +2489,7 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2408
2489
  signal
2409
2490
  );
2410
2491
  const features = extractMeshFeatures(
2411
- gltf,
2492
+ gltfWithBuffers,
2412
2493
  selected.primitive,
2413
2494
  payload,
2414
2495
  resource.mesh,
@@ -2431,9 +2512,10 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2431
2512
  }
2432
2513
  };
2433
2514
  }
2434
- function* traverseMeshNodes(nodes, parent, ancestors = /* @__PURE__ */ new Set()) {
2435
- for (const node of nodes) {
2436
- if (ancestors.has(node) || node.skin || node.weights || Object.keys(node.extensions || {}).length)
2515
+ function* traverseMeshNodes(gltf, nodes, parent, ancestors = /* @__PURE__ */ new Set()) {
2516
+ for (const nodeIndex of nodes) {
2517
+ const node = gltf.json.nodes[nodeIndex];
2518
+ if (!node || ancestors.has(nodeIndex) || node.skin !== void 0 || node.weights || Object.keys(node.extensions || {}).length)
2437
2519
  throw new TileConversionError15(
2438
2520
  "MESH_SOURCE_NODE_UNSUPPORTED",
2439
2521
  "Cyclic, skinned, morphed, or extended nodes are unsupported"
@@ -2441,18 +2523,24 @@ function* traverseMeshNodes(nodes, parent, ancestors = /* @__PURE__ */ new Set()
2441
2523
  const transform = new Matrix42(parent).multiplyRight(
2442
2524
  node.matrix ? new Matrix42(node.matrix) : new Matrix42().translate(node.translation || [0, 0, 0]).multiplyRight(new Matrix42().fromQuaternion(node.rotation || [0, 0, 0, 1])).scale(node.scale || [1, 1, 1])
2443
2525
  );
2444
- if (node.mesh) {
2445
- if (node.mesh.weights || Object.keys(node.mesh.extensions || {}).length)
2526
+ if (node.mesh !== void 0) {
2527
+ const mesh = gltf.json.meshes[node.mesh];
2528
+ if (mesh.weights || Object.keys(mesh.extensions || {}).length)
2446
2529
  throw new TileConversionError15(
2447
2530
  "MESH_SOURCE_NODE_UNSUPPORTED",
2448
2531
  "Morph weights and mesh extensions are unsupported"
2449
2532
  );
2450
- for (const primitive of node.mesh.primitives) yield { primitive, transform };
2533
+ for (const primitive of mesh.primitives) yield { primitive, transform };
2451
2534
  }
2452
- yield* traverseMeshNodes(node.children || [], transform, /* @__PURE__ */ new Set([...ancestors, node]));
2535
+ yield* traverseMeshNodes(
2536
+ gltf,
2537
+ node.children || [],
2538
+ transform,
2539
+ /* @__PURE__ */ new Set([...ancestors, nodeIndex])
2540
+ );
2453
2541
  }
2454
2542
  }
2455
- async function extractPrimitive(primitive, transform, id, readExternalResource, contentUri, signal) {
2543
+ async function extractPrimitive(gltf, primitive, transform, id, readExternalResource, contentUri, signal) {
2456
2544
  if (primitive.targets?.length || Object.keys(primitive.extensions || {}).some((name) => name !== "EXT_mesh_features"))
2457
2545
  throw new TileConversionError15(
2458
2546
  "MESH_SOURCE_PRIMITIVE_UNSUPPORTED",
@@ -2464,7 +2552,8 @@ async function extractPrimitive(primitive, transform, id, readExternalResource,
2464
2552
  "Mesh placement requires a finite invertible affine transform with positive determinant"
2465
2553
  );
2466
2554
  const attributes = {};
2467
- for (const [name, accessor] of Object.entries(primitive.attributes)) {
2555
+ for (const [name, reference] of Object.entries(primitive.attributes)) {
2556
+ const accessor = getGLTFAccessor2(gltf, reference);
2468
2557
  if (name === "_BATCHID" || /^_FEATURE_ID_\d+$/.test(name)) continue;
2469
2558
  if (!["POSITION", "NORMAL", "COLOR_0", "TEXCOORD_0"].includes(name))
2470
2559
  throw new TileConversionError15(
@@ -2472,17 +2561,17 @@ async function extractPrimitive(primitive, transform, id, readExternalResource,
2472
2561
  `Unsupported mesh attribute ${name}`
2473
2562
  );
2474
2563
  attributes[name] = {
2475
- value: accessor.value,
2476
- size: accessor.components,
2564
+ value: getGLTFAccessorValue2(gltf, reference),
2565
+ size: accessor.type === "SCALAR" ? 1 : Number(accessor.type.slice(3)),
2477
2566
  ...accessor.normalized ? { normalized: true } : {}
2478
2567
  };
2479
2568
  }
2480
2569
  const mesh = validateMeshGeometry(
2481
2570
  {
2482
2571
  topology: "triangle-list",
2483
- mode: primitive.mode === 5 ? 4 : primitive.mode ?? 4,
2572
+ mode: [5, 6].includes(primitive.mode ?? 4) ? 4 : primitive.mode ?? 4,
2484
2573
  attributes,
2485
- indices: createTriangleIndices(primitive, (attributes.POSITION?.value.length ?? 0) / 3)
2574
+ indices: createTriangleIndices(gltf, primitive, (attributes.POSITION?.value.length ?? 0) / 3)
2486
2575
  },
2487
2576
  true
2488
2577
  );
@@ -2520,14 +2609,15 @@ async function extractPrimitive(primitive, transform, id, readExternalResource,
2520
2609
  mesh,
2521
2610
  origin: minimum.map((value, axis) => value / 2 + maximum[axis] / 2),
2522
2611
  material: await mapMeshSourceMaterial(
2523
- primitive.material,
2612
+ primitive.material === void 0 ? void 0 : gltf.json.materials?.[primitive.material],
2524
2613
  readExternalResource,
2525
2614
  contentUri,
2526
- signal
2615
+ signal,
2616
+ gltf
2527
2617
  )
2528
2618
  };
2529
2619
  }
2530
- async function mapMeshSourceMaterial(material, readExternalResource, contentUri, signal) {
2620
+ async function mapMeshSourceMaterial(material, readExternalResource, contentUri, signal, gltf) {
2531
2621
  if (!material) return void 0;
2532
2622
  const pbr = material.pbrMetallicRoughness;
2533
2623
  if (Object.entries(material).some(
@@ -2552,6 +2642,7 @@ async function mapMeshSourceMaterial(material, readExternalResource, contentUri,
2552
2642
  baseColorFactor: pbr?.baseColorFactor,
2553
2643
  ..."baseColorTexture" in (pbr || {}) ? {
2554
2644
  baseColorTexture: await resolveMeshSourceTexture(
2645
+ gltf,
2555
2646
  pbr.baseColorTexture,
2556
2647
  readExternalResource,
2557
2648
  contentUri,
@@ -2565,13 +2656,14 @@ async function mapMeshSourceMaterial(material, readExternalResource, contentUri,
2565
2656
  doubleSided: material.doubleSided
2566
2657
  };
2567
2658
  }
2568
- function createTriangleIndices(primitive, vertexCount) {
2569
- const attribute = primitive.indices ? {
2570
- value: primitive.indices.value,
2571
- size: primitive.indices.components,
2572
- normalized: primitive.indices.normalized
2659
+ function createTriangleIndices(gltf, primitive, vertexCount) {
2660
+ const accessor = primitive.indices === void 0 ? void 0 : getGLTFAccessor2(gltf, primitive.indices);
2661
+ const attribute = accessor ? {
2662
+ value: getGLTFAccessorValue2(gltf, accessor),
2663
+ size: 1,
2664
+ normalized: accessor.normalized
2573
2665
  } : void 0;
2574
- if (primitive.mode !== 5) return attribute;
2666
+ if (primitive.mode !== 5 && primitive.mode !== 6) return attribute;
2575
2667
  const sourceIndices = validateMeshIndices(attribute, vertexCount, 5);
2576
2668
  const indexCount = sourceIndices?.length ?? vertexCount;
2577
2669
  if (!Number.isSafeInteger(indexCount) || indexCount < 3) {
@@ -2583,9 +2675,10 @@ function createTriangleIndices(primitive, vertexCount) {
2583
2675
  const IndexArray = vertexCount < 65536 ? Uint16Array : Uint32Array;
2584
2676
  const indices = new IndexArray((indexCount - 2) * 3);
2585
2677
  for (let triangle = 0; triangle < indexCount - 2; triangle++) {
2586
- const second = triangle + 1 + triangle % 2;
2587
- const third = triangle + 2 - triangle % 2;
2588
- indices[triangle * 3] = sourceIndices?.[triangle] ?? triangle;
2678
+ const second = primitive.mode === 6 ? triangle + 1 : triangle + 1 + triangle % 2;
2679
+ const third = primitive.mode === 6 ? triangle + 2 : triangle + 2 - triangle % 2;
2680
+ const first = primitive.mode === 6 ? 0 : triangle;
2681
+ indices[triangle * 3] = sourceIndices?.[first] ?? first;
2589
2682
  indices[triangle * 3 + 1] = sourceIndices?.[second] ?? second;
2590
2683
  indices[triangle * 3 + 2] = sourceIndices?.[third] ?? third;
2591
2684
  }
@@ -2597,7 +2690,7 @@ import {
2597
2690
  encodeI3SMeshLayer,
2598
2691
  encodeI3SMeshLayerWithDraco,
2599
2692
  preloadI3SMeshCompression
2600
- } from "@loaders.gl/i3s";
2693
+ } from "@loaders.gl/tiles-3d/i3s";
2601
2694
  import { TileConversionError as TileConversionError16 } from "@loaders.gl/tile-converter/v5/core";
2602
2695
  import { createBoundedMemoryTileConversionSink as createBoundedMemoryTileConversionSink2 } from "@loaders.gl/tile-converter/v5/core";
2603
2696
  function createI3SMeshConversionCodec(options) {
@@ -3220,6 +3313,7 @@ import {
3220
3313
  import { getBinaryImageMetadata as getBinaryImageMetadata3 } from "@loaders.gl/images";
3221
3314
  import { TileConversionError as TileConversionError20 } from "@loaders.gl/tile-converter/v5/core";
3222
3315
  async function mapI3SMeshSourceMaterial(content, header, readExternalResource, contentUri, signal) {
3316
+ const gltf = { json: { asset: { version: "2.0" } }, buffers: [] };
3223
3317
  const material = Object.fromEntries(
3224
3318
  Object.entries(content.material || {}).filter(([, value]) => value !== void 0)
3225
3319
  );
@@ -3286,7 +3380,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3286
3380
  }
3287
3381
  }
3288
3382
  const declaredFormat = resources[0]?.textureFormat;
3289
- if (!image || !["image/png", "image/jpeg"].includes(image.mimeType) || declaredFormat && (declaredFormat === "png" ? image.mimeType !== "image/png" : declaredFormat === "jpg" ? image.mimeType !== "image/jpeg" : true)) {
3383
+ if (!(data instanceof Uint8Array) || !image || !["image/png", "image/jpeg"].includes(image.mimeType) || declaredFormat && (declaredFormat === "png" ? image.mimeType !== "image/png" : declaredFormat === "jpg" ? image.mimeType !== "image/jpeg" : true)) {
3290
3384
  throw new TileConversionError20(
3291
3385
  "I3S_MESH_SOURCE_TEXTURE_UNSUPPORTED",
3292
3386
  "I3S base-color bytes must have matching PNG/JPEG headers"
@@ -3295,19 +3389,24 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3295
3389
  const sampler = Object.fromEntries(
3296
3390
  ["wrapS", "wrapT"].filter((name) => textureInfo[name] !== void 0).map((name) => [name, wrappingModes[textureInfo[name]]])
3297
3391
  );
3298
- metallicRoughness.baseColorTexture = {
3299
- texCoord: 0,
3300
- texture: {
3301
- source: { mimeType: image.mimeType, bufferView: { data } },
3302
- ...Object.keys(sampler).length ? { sampler } : {}
3303
- }
3304
- };
3392
+ gltf.buffers.push({
3393
+ arrayBuffer: data.buffer,
3394
+ byteOffset: data.byteOffset,
3395
+ byteLength: data.byteLength
3396
+ });
3397
+ gltf.json.buffers = [{ byteLength: data.byteLength }];
3398
+ gltf.json.bufferViews = [{ buffer: 0, byteLength: data.byteLength }];
3399
+ gltf.json.images = [{ mimeType: image.mimeType, bufferView: 0 }];
3400
+ gltf.json.samplers = [sampler];
3401
+ gltf.json.textures = [{ source: 0, sampler: 0 }];
3402
+ metallicRoughness.baseColorTexture = { texCoord: 0, index: 0 };
3305
3403
  }
3306
3404
  const mapped = await mapMeshSourceMaterial(
3307
3405
  material,
3308
3406
  void 0,
3309
3407
  contentUri,
3310
- signal
3408
+ signal,
3409
+ gltf
3311
3410
  );
3312
3411
  if (mapped?.baseColorTexture && !content.sourceAttributes?.uv0) {
3313
3412
  throw new TileConversionError20(
@@ -3319,7 +3418,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3319
3418
  }
3320
3419
 
3321
3420
  // src/v5/i3s-mesh-source-features.ts
3322
- import { I3SAttributeLoader } from "@loaders.gl/i3s";
3421
+ import { I3SAttributeLoader } from "@loaders.gl/tiles-3d/i3s";
3323
3422
  import { TileConversionError as TileConversionError21 } from "@loaders.gl/tile-converter/v5/core";
3324
3423
  async function extractI3SMeshFeatures(content, item, metadata, options, readExternalResource, contentUri, signal) {
3325
3424
  const maximumAttributeBytes = options.maxAttributeBytes;
@@ -3647,19 +3746,267 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3647
3746
  };
3648
3747
  }
3649
3748
 
3650
- // src/v5/point-cloud-tileset.ts
3651
- import { PointCloudTileSource } from "@loaders.gl/tiles/point-cloud-tile-source";
3749
+ // src/v5/point-cloud-attributes.ts
3750
+ import { convertTableToMesh as convertTableToMesh3 } from "@loaders.gl/core/schema-utils";
3652
3751
  import { TileConversionError as TileConversionError23 } from "@loaders.gl/tile-converter/v5/core";
3752
+ function mapPointCloudAttributes(input, mapping) {
3753
+ const mesh = getPointCloudMesh(input);
3754
+ const attributes = /* @__PURE__ */ Object.create(null);
3755
+ const selected = /* @__PURE__ */ new Set();
3756
+ for (const [target, source] of Object.entries(mapping.attributes)) {
3757
+ if (!target || ["__proto__", "constructor", "prototype"].includes(target) || !Object.hasOwn(mesh.attributes, source))
3758
+ throw new TileConversionError23(
3759
+ "POINT_CLOUD_MAPPING_INVALID",
3760
+ `Invalid point attribute mapping ${target}: ${source}`
3761
+ );
3762
+ if (Object.hasOwn(mapping.drop || {}, source))
3763
+ throw new TileConversionError23(
3764
+ "POINT_CLOUD_MAPPING_INVALID",
3765
+ `Point attribute ${source} is both selected and dropped`
3766
+ );
3767
+ attributes[target] = { ...mesh.attributes[source] };
3768
+ selected.add(source);
3769
+ }
3770
+ const droppedAttributes = /* @__PURE__ */ Object.create(null);
3771
+ for (const [source, reason] of Object.entries(mapping.drop || {}).sort(
3772
+ ([left], [right]) => left < right ? -1 : left > right ? 1 : 0
3773
+ )) {
3774
+ if (!Object.hasOwn(mesh.attributes, source) || typeof reason !== "string" || !reason.trim())
3775
+ throw new TileConversionError23(
3776
+ "POINT_CLOUD_MAPPING_INVALID",
3777
+ `Invalid loss declaration for ${source}`
3778
+ );
3779
+ droppedAttributes[source] = reason;
3780
+ }
3781
+ const unmapped = Object.keys(mesh.attributes).filter(
3782
+ (name) => !selected.has(name) && !Object.hasOwn(droppedAttributes, name)
3783
+ );
3784
+ if (unmapped.length || !attributes.POSITION)
3785
+ throw new TileConversionError23(
3786
+ "POINT_CLOUD_MAPPING_REQUIRED",
3787
+ `Map POSITION and explicitly retain or drop every attribute: ${unmapped.join(", ")}`
3788
+ );
3789
+ return {
3790
+ mesh: { ...mesh, attributes, schema: { fields: [], metadata: { ...mesh.schema?.metadata } } },
3791
+ droppedAttributes
3792
+ };
3793
+ }
3794
+ function getPointCloudMesh(input, maxInputBytes = Infinity) {
3795
+ const buffers = /* @__PURE__ */ new Set();
3796
+ let bytes = 0;
3797
+ const countBuffer = (buffer) => {
3798
+ if (!buffers.has(buffer)) {
3799
+ buffers.add(buffer);
3800
+ bytes += buffer.byteLength;
3801
+ }
3802
+ if (bytes > maxInputBytes)
3803
+ throw new TileConversionError23("POINT_CLOUD_INPUT_BUDGET", "Point input byte budget exceeded");
3804
+ };
3805
+ if ("shape" in input) {
3806
+ const checkColumn = (column) => {
3807
+ if (column.nullCount)
3808
+ throw new TileConversionError23(
3809
+ "POINT_CLOUD_NULL_UNSUPPORTED",
3810
+ "Null point values require an explicit preprocessing step"
3811
+ );
3812
+ for (const buffer of Object.values(column.buffers)) if (buffer) countBuffer(buffer.buffer);
3813
+ for (const view of column.variadicBuffers) countBuffer(view.buffer);
3814
+ for (const child of column.children) checkColumn(child);
3815
+ for (const dictionary of column.dictionary?.data || []) checkColumn(dictionary);
3816
+ };
3817
+ for (const batch of input.data.batches) checkColumn(batch.data);
3818
+ } else
3819
+ for (const attribute of Object.values(input.attributes)) countBuffer(attribute.value.buffer);
3820
+ const mesh = "shape" in input ? convertTableToMesh3(input) : input;
3821
+ if (mesh.topology !== "point-list" || mesh.indices)
3822
+ throw new TileConversionError23(
3823
+ "POINT_CLOUD_INPUT_INVALID",
3824
+ "Decoded unindexed point-list input is required"
3825
+ );
3826
+ return mesh;
3827
+ }
3828
+
3829
+ // src/v5/point-cloud-copc.ts
3830
+ import { COPCWriter } from "@loaders.gl/tiles-3d/copc";
3831
+ import { TileConversionError as TileConversionError24 } from "@loaders.gl/tile-converter/v5/core";
3832
+ var SCALAR_RANGES = {
3833
+ intensity: [0, 65535],
3834
+ classification: [0, 255],
3835
+ nir: [0, 65535],
3836
+ gpsTime: [-Infinity, Infinity],
3837
+ scanAngle: [-32768, 32767],
3838
+ userData: [0, 255],
3839
+ pointSourceId: [0, 65535],
3840
+ returnNumber: [1, 15],
3841
+ numberOfReturns: [1, 15],
3842
+ scannerChannel: [0, 3],
3843
+ scanDirectionFlag: [0, 1],
3844
+ edgeOfFlightLine: [0, 1],
3845
+ synthetic: [0, 1],
3846
+ keyPoint: [0, 1],
3847
+ withheld: [0, 1],
3848
+ overlap: [0, 1]
3849
+ };
3850
+ async function encodePointCloudCOPC(input, options) {
3851
+ const maxInputBytes = options.maxInputBytes ?? 64 * 1024 * 1024;
3852
+ const maxPoints = options.maxPoints ?? 1e6;
3853
+ const maxOutputBytes = options.maxOutputBytes ?? 128 * 1024 * 1024;
3854
+ if ([maxInputBytes, maxPoints, maxOutputBytes].some(
3855
+ (value) => !Number.isSafeInteger(value) || value < 1
3856
+ ) || options.scale.length !== 3 || options.scale.some((value) => !Number.isFinite(value) || value <= 0) || options.offset.length !== 3 || options.offset.some((value) => !Number.isFinite(value)) || typeof options.wkt !== "string" || !options.wkt.trim() || !Number.isFinite(options.maxPositionError) || options.maxPositionError < 0)
3857
+ throw new TileConversionError24(
3858
+ "POINT_CLOUD_COPC_OPTIONS_INVALID",
3859
+ "COPC requires explicit finite scale, offset, CRS, precision and positive budgets"
3860
+ );
3861
+ options.signal?.throwIfAborted();
3862
+ const source = getPointCloudMesh(input, maxInputBytes);
3863
+ const { mesh, droppedAttributes } = mapPointCloudAttributes(source, options.mapping);
3864
+ const position = mesh.attributes.POSITION;
3865
+ const pointCount = mesh.header?.vertexCount ?? position.value.length / 3;
3866
+ if (!Number.isSafeInteger(pointCount) || pointCount < 1 || pointCount > maxPoints)
3867
+ throw new TileConversionError24(
3868
+ "POINT_CLOUD_POINT_BUDGET",
3869
+ "COPC requires a nonempty batch within the point limit"
3870
+ );
3871
+ if (!(position.value instanceof Float32Array || position.value instanceof Float64Array) || position.size !== 3 || position.normalized)
3872
+ throw new TileConversionError24(
3873
+ "POINT_CLOUD_POSITION_INVALID",
3874
+ "COPC requires decoded floating XYZ"
3875
+ );
3876
+ const extraNames = new Set(options.extraBytes?.map((field) => field.attribute) || []);
3877
+ const descriptorNames = new Set(
3878
+ options.extraBytes?.map((field) => field.name || field.attribute) || []
3879
+ );
3880
+ if (extraNames.size !== (options.extraBytes?.length || 0) || descriptorNames.size !== extraNames.size || options.extraBytes?.some((field) => {
3881
+ const name = field.name || field.attribute;
3882
+ const description = field.description || "";
3883
+ return !mesh.attributes[field.attribute] || !field.attribute || !name.length || name.length > 32 || description.length > 32 || /[^\x20-\x7e]/.test(name) || /[^\x20-\x7e]/.test(description);
3884
+ }))
3885
+ throw new TileConversionError24(
3886
+ "POINT_CLOUD_EXTRA_BYTES_INVALID",
3887
+ "Extra Bytes descriptors require unique mapped attributes and names/descriptions within 32 printable ASCII bytes"
3888
+ );
3889
+ for (const name of extraNames)
3890
+ if (name === "POSITION" || name === "COLOR_0" || Object.hasOwn(SCALAR_RANGES, name))
3891
+ throw new TileConversionError24(
3892
+ "POINT_CLOUD_EXTRA_BYTES_INVALID",
3893
+ "Map standard attributes directly instead of duplicating them as Extra Bytes"
3894
+ );
3895
+ for (const [name, attribute] of Object.entries(mesh.attributes)) {
3896
+ const color2 = name === "COLOR_0";
3897
+ const extra = extraNames.has(name);
3898
+ if (name !== "POSITION" && !color2 && !extra && !Object.hasOwn(SCALAR_RANGES, name))
3899
+ throw new TileConversionError24(
3900
+ "POINT_CLOUD_ATTRIBUTE_UNSUPPORTED",
3901
+ `COPC cannot preserve ${name}; supply an explicit supported mapping or drop reason`
3902
+ );
3903
+ if (attribute.transform || attribute.byteOffset || attribute.byteStride || attribute.componentType === "float16" || attribute.value.length !== pointCount * attribute.size || !color2 && attribute.normalized || name !== "POSITION" && !color2 && (extra ? ![1, 3].includes(attribute.size) : attribute.size !== 1))
3904
+ throw new TileConversionError24(
3905
+ "POINT_CLOUD_ATTRIBUTE_INVALID",
3906
+ `COPC requires packed decoded attributes: ${name}`
3907
+ );
3908
+ if (color2) {
3909
+ if (attribute.size !== 3 || !(attribute.value instanceof Uint8Array || attribute.value instanceof Uint16Array))
3910
+ throw new TileConversionError24(
3911
+ "POINT_CLOUD_COLOR_INVALID",
3912
+ "COPC requires explicitly mapped uint8/uint16 RGB"
3913
+ );
3914
+ } else if (extra) {
3915
+ for (const value of attribute.value)
3916
+ if (typeof value !== "bigint" && !Number.isFinite(value))
3917
+ throw new TileConversionError24(
3918
+ "POINT_CLOUD_ATTRIBUTE_RANGE",
3919
+ `Nonfinite Extra Bytes value: ${name}`
3920
+ );
3921
+ } else if (name !== "POSITION") {
3922
+ const [minimum2, maximum2] = SCALAR_RANGES[name];
3923
+ for (const value of attribute.value) {
3924
+ if (typeof value !== "number" || !Number.isFinite(value) || value < minimum2 || value > maximum2 || name !== "gpsTime" && !Number.isInteger(value))
3925
+ throw new TileConversionError24(
3926
+ "POINT_CLOUD_ATTRIBUTE_RANGE",
3927
+ `COPC value outside the qualified range: ${name}`
3928
+ );
3929
+ }
3930
+ }
3931
+ }
3932
+ let maximumPositionError = 0;
3933
+ const minimum = [Infinity, Infinity, Infinity];
3934
+ const maximum = [-Infinity, -Infinity, -Infinity];
3935
+ const errors = [0, 0, 0];
3936
+ for (let row = 0; row < pointCount; row++) {
3937
+ for (let axis = 0; axis < 3; axis++) {
3938
+ const value = position.value[row * 3 + axis];
3939
+ const encoded = Math.round((value - options.offset[axis]) / options.scale[axis]);
3940
+ if (!Number.isFinite(value) || !Number.isSafeInteger(encoded) || encoded < -2147483648 || encoded > 2147483647)
3941
+ throw new TileConversionError24(
3942
+ "POINT_CLOUD_POSITION_RANGE",
3943
+ "COPC position exceeds signed int32 quantization"
3944
+ );
3945
+ minimum[axis] = Math.min(minimum[axis], value);
3946
+ maximum[axis] = Math.max(maximum[axis], value);
3947
+ errors[axis] = encoded * options.scale[axis] + options.offset[axis] - value;
3948
+ }
3949
+ maximumPositionError = Math.max(maximumPositionError, Math.hypot(...errors));
3950
+ if (maximumPositionError > options.maxPositionError)
3951
+ throw new TileConversionError24(
3952
+ "POINT_CLOUD_PRECISION_EXCEEDED",
3953
+ "COPC position rounding exceeds the declared error gate"
3954
+ );
3955
+ if (row % 16384 === 0) {
3956
+ await new Promise((resolve) => setTimeout(resolve, 0));
3957
+ options.signal?.throwIfAborted();
3958
+ }
3959
+ }
3960
+ const width = Math.max(...maximum.map((value, axis) => value - minimum[axis]), ...options.scale);
3961
+ const radius = width / 2;
3962
+ if (!Number.isFinite(Math.hypot(radius, radius, radius)) || minimum.some((value, axis) => {
3963
+ const center = value + (maximum[axis] - value) / 2;
3964
+ return !Number.isFinite(value + maximum[axis]) || !Number.isFinite(center - radius) || !Number.isFinite(center + radius);
3965
+ }))
3966
+ throw new TileConversionError24("POINT_CLOUD_BOUNDS_INVALID", "COPC coordinate extent overflow");
3967
+ options.signal?.throwIfAborted();
3968
+ const color = mesh.attributes.COLOR_0;
3969
+ const copc = COPCWriter.encodeSync(mesh, {
3970
+ copc: {
3971
+ ...options.organization,
3972
+ scale: [...options.scale],
3973
+ offset: [...options.offset],
3974
+ wkt: options.wkt,
3975
+ pointDataRecordFormat: mesh.attributes.nir ? 8 : color ? 7 : 6,
3976
+ colorDepth: color?.value instanceof Uint16Array ? 16 : 8,
3977
+ extraBytes: options.extraBytes
3978
+ }
3979
+ });
3980
+ options.signal?.throwIfAborted();
3981
+ if (copc.byteLength > maxOutputBytes)
3982
+ throw new TileConversionError24(
3983
+ "POINT_CLOUD_OUTPUT_BUDGET",
3984
+ "Completed COPC output exceeds the byte budget"
3985
+ );
3986
+ return { copc, pointCount, maximumPositionError, droppedAttributes };
3987
+ }
3988
+ function createCOPCConversionCodec(options) {
3989
+ return {
3990
+ async *convert(input, _inspection, signal) {
3991
+ const combinedSignal = options.signal && signal ? AbortSignal.any([options.signal, signal]) : options.signal || signal;
3992
+ yield await encodePointCloudCOPC(input, { ...options, signal: combinedSignal });
3993
+ }
3994
+ };
3995
+ }
3996
+
3997
+ // src/v5/point-cloud-tileset.ts
3998
+ import { PointCloudTileSource } from "@loaders.gl/tiles-3d/point-cloud-tile-source";
3999
+ import { TileConversionError as TileConversionError25 } from "@loaders.gl/tile-converter/v5/core";
3653
4000
  async function convertPointCloudToTileset(input, options) {
3654
4001
  const reference = options.spatialReference;
3655
4002
  if (!reference || reference.status !== "native" || reference.sourceCrs !== "EPSG:4978" || reference.targetCrs && reference.targetCrs !== "EPSG:4978" || reference.coordinateFrame !== "geocentric" || reference.axisOrder !== "xyz" || reference.heightReference !== "ellipsoidal" || reference.verticalUnitScale !== 1 || reference.units?.some((unit) => unit !== "meter")) {
3656
- throw new TileConversionError23(
4003
+ throw new TileConversionError25(
3657
4004
  "POINT_CLOUD_TILESET_FRAME_UNSUPPORTED",
3658
4005
  "Point authoring requires explicit native EPSG:4978 xyz meters and ellipsoidal heights"
3659
4006
  );
3660
4007
  }
3661
4008
  if (options.archive && (!Number.isSafeInteger(options.archive.maxArchiveBytes) || options.archive.maxArchiveBytes < 0)) {
3662
- throw new TileConversionError23(
4009
+ throw new TileConversionError25(
3663
4010
  "INVALID_ARCHIVE_BYTE_LIMIT",
3664
4011
  "Archive byte limit must be a non-negative safe integer"
3665
4012
  );
@@ -3710,10 +4057,10 @@ async function convertPointCloudToTileset(input, options) {
3710
4057
  }
3711
4058
 
3712
4059
  // src/v5/point-cloud-file.ts
3713
- import { COPCSourceLoader } from "@loaders.gl/copc";
3714
- import { PotreeSourceLoader, encodePotreeDataset } from "@loaders.gl/potree";
3715
- import { deduceMeshSchema } from "@loaders.gl/schema-utils";
3716
- import { encodeZip, openZipArchive } from "@loaders.gl/zip";
4060
+ import { COPCSourceLoader } from "@loaders.gl/tiles-3d/copc";
4061
+ import { PotreeSourceLoader, encodePotreeDataset } from "@loaders.gl/tiles-3d/potree";
4062
+ import { deduceMeshSchema } from "@loaders.gl/core/schema-utils";
4063
+ import { encodeZip, openZipArchive } from "@loaders.gl/archives/zip";
3717
4064
  async function encodePotreeArchive(input, options) {
3718
4065
  validateLimit(options.maxArchiveBytes);
3719
4066
  const dataset = await encodePotreeDataset(input, { potree: options.potree });
@@ -3867,8 +4214,8 @@ function validateLimit(value) {
3867
4214
  }
3868
4215
 
3869
4216
  // src/v5/point-cloud-potree.ts
3870
- import { PointCloudTiler } from "@loaders.gl/schema-utils";
3871
- import { encodePotreeDataset as encodePotreeDataset2 } from "@loaders.gl/potree";
4217
+ import { PointCloudTiler } from "@loaders.gl/core/schema-utils";
4218
+ import { encodePotreeDataset as encodePotreeDataset2 } from "@loaders.gl/tiles-3d/potree";
3872
4219
  async function convertPointCloudToPotree(input, options) {
3873
4220
  if (typeof options.projection !== "string" || !options.projection.trim()) {
3874
4221
  throw new Error("Potree authoring requires an explicit native CRS declaration");
@@ -3939,15 +4286,15 @@ async function convertPointCloudToPotree(input, options) {
3939
4286
 
3940
4287
  // src/v5/mesh-hierarchy.ts
3941
4288
  import { Matrix4 as Matrix43 } from "@math.gl/core";
3942
- import { get3DTilesSpatialReference } from "@loaders.gl/tiles";
4289
+ import { get3DTilesSpatialReference } from "@loaders.gl/tiles-3d";
3943
4290
  import {
3944
- TileConversionError as TileConversionError24,
4291
+ TileConversionError as TileConversionError26,
3945
4292
  createBoundedMemoryTileConversionSink as createBoundedMemoryTileConversionSink5,
3946
4293
  createTiles3DConversionSpatialContext
3947
4294
  } from "@loaders.gl/tile-converter/v5/core";
3948
4295
  function prepareMeshHierarchy(inspection, resourceIds) {
3949
4296
  const rejectHierarchy = (message) => {
3950
- throw new TileConversionError24("MESH_HIERARCHY_UNSUPPORTED", message);
4297
+ throw new TileConversionError26("MESH_HIERARCHY_UNSUPPORTED", message);
3951
4298
  };
3952
4299
  if (!inspection.documents?.length || inspection.documents.length > 32)
3953
4300
  rejectHierarchy(
@@ -4127,7 +4474,7 @@ function createMeshHierarchyTilesetSink(options) {
4127
4474
  );
4128
4475
  const { maxPositionError } = options;
4129
4476
  if (!Number.isFinite(maxPositionError) || maxPositionError < 0 || hierarchy.nodes.some((node) => !Number.isFinite(node.geometricError + maxPositionError)) || !Number.isFinite(hierarchy.geometricError + maxPositionError))
4130
- throw new TileConversionError24(
4477
+ throw new TileConversionError26(
4131
4478
  "MESH_GEOMETRIC_ERROR_INVALID",
4132
4479
  "Hierarchy position allowance and resulting errors must be finite nonnegative meters"
4133
4480
  );
@@ -4149,12 +4496,12 @@ function createMeshHierarchyTilesetSink(options) {
4149
4496
  last: true
4150
4497
  };
4151
4498
  if (state !== "open" || !identifier || completedContents.has(identifier) || primitiveIdentifiers.has(resource.id) || primitiveIdentifiers.size >= 64 || !Number.isSafeInteger(primitiveIndex) || primitiveIndex !== (placements.get(identifier)?.length ?? 0) || typeof lastPrimitive !== "boolean" || !hierarchy.nodes.some((node2) => node2.resourceIds.includes(identifier)))
4152
- throw new TileConversionError24(
4499
+ throw new TileConversionError26(
4153
4500
  "MESH_HIERARCHY_PLACEMENT_INVALID",
4154
4501
  "Hierarchy writes require ordered, distinct primitives for declared contents before close (at most 64)"
4155
4502
  );
4156
4503
  if (resource.spatialReference.coordinateEpoch !== hierarchy.coordinateEpoch)
4157
- throw new TileConversionError24(
4504
+ throw new TileConversionError26(
4158
4505
  "MESH_SOURCE_EPOCH_MISMATCH",
4159
4506
  "Hierarchy output coordinate epoch must match its source"
4160
4507
  );
@@ -4163,7 +4510,7 @@ function createMeshHierarchyTilesetSink(options) {
4163
4510
  const node = hierarchy.nodes[nodeIndex];
4164
4511
  const sibling = node.resourceIds.map((identifier2) => placements.get(identifier2)?.[0]).find(Boolean);
4165
4512
  if (sibling && sibling.origin.some((value, axis) => value !== resource.origin[axis]))
4166
- throw new TileConversionError24(
4513
+ throw new TileConversionError26(
4167
4514
  "MESH_HIERARCHY_PLACEMENT_INVALID",
4168
4515
  "Contents on one tile must share an encoded origin"
4169
4516
  );
@@ -4190,7 +4537,7 @@ function createMeshHierarchyTilesetSink(options) {
4190
4537
  /** Publishes original topology only after every source content has completed. */
4191
4538
  async finalize(report) {
4192
4539
  if (state !== "open" || completedContents.size !== hierarchy.nodes.reduce((count, node) => count + node.resourceIds.length, 0))
4193
- throw new TileConversionError24(
4540
+ throw new TileConversionError26(
4194
4541
  "MESH_HIERARCHY_INCOMPLETE",
4195
4542
  "Finalization requires every declared hierarchy content"
4196
4543
  );
@@ -4228,7 +4575,7 @@ function createMeshHierarchyTilesetSink(options) {
4228
4575
  maximum[axis] = Math.max(maximum[axis], upper[axis]);
4229
4576
  }
4230
4577
  if ([...minimum, ...maximum].some((value) => !Number.isFinite(value)))
4231
- throw new TileConversionError24(
4578
+ throw new TileConversionError26(
4232
4579
  "MESH_TILESET_BOUNDS_INVALID",
4233
4580
  "Hierarchy absolute bounds must remain finite"
4234
4581
  );
@@ -4246,7 +4593,7 @@ function createMeshHierarchyTilesetSink(options) {
4246
4593
  )
4247
4594
  );
4248
4595
  if ([...center, ...halfAxes].some((value) => !Number.isFinite(value)))
4249
- throw new TileConversionError24(
4596
+ throw new TileConversionError26(
4250
4597
  "MESH_TILESET_BOUNDS_INVALID",
4251
4598
  "Hierarchy local bounds must remain finite"
4252
4599
  );
@@ -4262,7 +4609,7 @@ function createMeshHierarchyTilesetSink(options) {
4262
4609
  new Matrix43(inverse).multiplyRight(transforms[child])
4263
4610
  );
4264
4611
  if (tiles[child].transform.some((value) => !Number.isFinite(value)))
4265
- throw new TileConversionError24(
4612
+ throw new TileConversionError26(
4266
4613
  "MESH_TILESET_BOUNDS_INVALID",
4267
4614
  "Relative hierarchy transforms must remain finite"
4268
4615
  );
@@ -4301,13 +4648,13 @@ function createMeshHierarchyTilesetSink(options) {
4301
4648
 
4302
4649
  // src/v5/selected-mesh-source.ts
4303
4650
  import { coreApi, parse } from "@loaders.gl/core";
4304
- import { Tiles3DLoader } from "@loaders.gl/3d-tiles";
4305
- import { Tiles3DSource, Tileset3D } from "@loaders.gl/tiles";
4651
+ import { Tiles3DLoader } from "@loaders.gl/tiles-3d/3d-tiles";
4652
+ import { Tiles3DSource, Tileset3D } from "@loaders.gl/tiles-3d";
4306
4653
  import {
4307
4654
  createBrowserTilesetConversionSource,
4308
4655
  selectBrowserTilesetContent,
4309
4656
  createTiles3DConversionSpatialContext as createTiles3DConversionSpatialContext2,
4310
- TileConversionError as TileConversionError25
4657
+ TileConversionError as TileConversionError27
4311
4658
  } from "@loaders.gl/tile-converter/v5/core";
4312
4659
  import {
4313
4660
  createMeshTilesetConversionSource as createMeshTilesetConversionSource2,
@@ -4340,7 +4687,7 @@ async function createSelectedMeshConversionSource(inspection, options) {
4340
4687
  } = options;
4341
4688
  const maxInputBytes = options.maxInputBytes ?? SELECTED_MESH_LIMITS.maxInputBytes;
4342
4689
  if (!Number.isSafeInteger(maxInputBytes) || maxInputBytes < 1 || maxInputBytes > SELECTED_MESH_LIMITS.maxInputBytes)
4343
- throw new TileConversionError25(
4690
+ throw new TileConversionError27(
4344
4691
  "INVALID_SELECTED_MESH_OPTIONS",
4345
4692
  "Selected mesh input budget must be a positive safe integer at most 16 MiB"
4346
4693
  );
@@ -4349,7 +4696,7 @@ async function createSelectedMeshConversionSource(inspection, options) {
4349
4696
  ))
4350
4697
  throw new Error("Select between 1 and 64 distinct content placements.");
4351
4698
  if (preserveHierarchy !== void 0 && typeof preserveHierarchy !== "boolean")
4352
- throw new TileConversionError25(
4699
+ throw new TileConversionError27(
4353
4700
  "MESH_HIERARCHY_UNSUPPORTED",
4354
4701
  "preserveHierarchy must be a boolean"
4355
4702
  );
@@ -4414,7 +4761,7 @@ async function createSelectedMeshConversionSource(inspection, options) {
4414
4761
  for await (const raw of rawSource.read(selectedInspection, signal2)) {
4415
4762
  const selection = selectedContents.get(raw.resourceId);
4416
4763
  if (!selection)
4417
- throw new TileConversionError25(
4764
+ throw new TileConversionError27(
4418
4765
  "INVALID_BROWSER_SELECTION",
4419
4766
  "Source yielded an uninspected content placement"
4420
4767
  );
@@ -4443,7 +4790,7 @@ async function createSelectedMeshConversionSource(inspection, options) {
4443
4790
  const metadata = await meshSource.inspect(signal2);
4444
4791
  const selectedContext = createTiles3DConversionSpatialContext2(metadata.spatialReference);
4445
4792
  if (selectedContext.spatialReference.coordinateEpoch !== spatialContext.spatialReference.coordinateEpoch)
4446
- throw new TileConversionError25(
4793
+ throw new TileConversionError27(
4447
4794
  "MESH_SOURCE_EPOCH_MISMATCH",
4448
4795
  "Selected content coordinate epoch must match the root; epoch transformations are unsupported"
4449
4796
  );
@@ -4454,7 +4801,7 @@ async function createSelectedMeshConversionSource(inspection, options) {
4454
4801
  throw new Error("Selected contents exceed the mesh primitive placement limit.");
4455
4802
  decodedBytes += measureMeshBytes(resource);
4456
4803
  if (decodedBytes > maxInputBytes)
4457
- throw new TileConversionError25(
4804
+ throw new TileConversionError27(
4458
4805
  "INPUT_RESOURCE_TOO_LARGE",
4459
4806
  "Selected decoded geometry and encoded images exceed the aggregate input byte limit."
4460
4807
  );
@@ -4569,14 +4916,14 @@ function measureMeshBytes(resource) {
4569
4916
  }
4570
4917
 
4571
4918
  // src/v5/archive-source.ts
4572
- import { BlobFile, HttpFile } from "@loaders.gl/loader-utils";
4573
- import { parseSLPKArchive } from "@loaders.gl/i3s";
4574
- import { parse3DTilesArchive } from "@loaders.gl/3d-tiles";
4575
- import { TileConversionError as TileConversionError26 } from "@loaders.gl/tile-converter/v5/core";
4919
+ import { BlobFile, HttpFile } from "@loaders.gl/core/loader-utils";
4920
+ import { parseSLPKArchive } from "@loaders.gl/tiles-3d/i3s";
4921
+ import { parse3DTilesArchive } from "@loaders.gl/tiles-3d/3d-tiles";
4922
+ import { TileConversionError as TileConversionError28 } from "@loaders.gl/tile-converter/v5/core";
4576
4923
  async function openTileConversionArchive(descriptor, maxInputBytes, signal, fetcher = fetch) {
4577
4924
  signal.throwIfAborted();
4578
4925
  if (!["slpk", "3tz"].includes(descriptor.format))
4579
- throw new TileConversionError26("INVALID_ARCHIVE_SOURCE", "Select SLPK or 3TZ archive input");
4926
+ throw new TileConversionError28("INVALID_ARCHIVE_SOURCE", "Select SLPK or 3TZ archive input");
4580
4927
  validateArchiveEntityTag(descriptor.identity?.etag);
4581
4928
  const transport = createTileConversionResourceFetcher({
4582
4929
  maxInputBytes,
@@ -4643,12 +4990,12 @@ async function createTileConversionArchiveTransport(file, format, maxInputBytes,
4643
4990
  const checkOpen = () => {
4644
4991
  signal.throwIfAborted();
4645
4992
  if (closed)
4646
- throw new TileConversionError26("ARCHIVE_SOURCE_CLOSED", "Conversion archive is closed");
4993
+ throw new TileConversionError28("ARCHIVE_SOURCE_CLOSED", "Conversion archive is closed");
4647
4994
  };
4648
4995
  try {
4649
4996
  checkOpen();
4650
4997
  if (!["slpk", "3tz"].includes(format) || !Number.isSafeInteger(maxInputBytes) || maxInputBytes < 1)
4651
- throw new TileConversionError26(
4998
+ throw new TileConversionError28(
4652
4999
  "INVALID_ARCHIVE_SOURCE",
4653
5000
  "Archive source requires a supported format and positive safe byte budget"
4654
5001
  );
@@ -4666,7 +5013,7 @@ async function createTileConversionArchiveTransport(file, format, maxInputBytes,
4666
5013
  read: async (start, length) => {
4667
5014
  checkOpen();
4668
5015
  if (!Number.isSafeInteger(length) || length < 0 || length > maxInputBytes - readBytes)
4669
- throw new TileConversionError26(
5016
+ throw new TileConversionError28(
4670
5017
  "INPUT_RESOURCE_TOO_LARGE",
4671
5018
  "Archive indexed reads exceed the input budget"
4672
5019
  );
@@ -4715,7 +5062,7 @@ async function createTileConversionArchiveTransport(file, format, maxInputBytes,
4715
5062
  }
4716
5063
  function validateArchiveEntityTag(entityTag) {
4717
5064
  if (entityTag !== void 0 && entityTag !== null && !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(entityTag))
4718
- throw new TileConversionError26(
5065
+ throw new TileConversionError28(
4719
5066
  "ARCHIVE_VALIDATOR_UNSUPPORTED",
4720
5067
  "Remote archives require a strong quoted ETag, or Last-Modified without an ETag; weak/malformed ETags are unsupported"
4721
5068
  );
@@ -4729,7 +5076,7 @@ function validateArchiveResponseValidator(headers) {
4729
5076
  const modifiedTime = parseArchiveHttpDate(lastModified);
4730
5077
  const responseTime = parseArchiveHttpDate(responseDate);
4731
5078
  if (!Number.isFinite(modifiedTime) || !Number.isFinite(responseTime) || responseTime - modifiedTime < 6e4)
4732
- throw new TileConversionError26(
5079
+ throw new TileConversionError28(
4733
5080
  "ARCHIVE_VALIDATOR_UNSUPPORTED",
4734
5081
  "Last-Modified archive identity requires a valid Date at least 60 seconds later; otherwise supply a strong ETag"
4735
5082
  );
@@ -4741,11 +5088,11 @@ function parseArchiveHttpDate(value) {
4741
5088
 
4742
5089
  // src/v5/i3s-selection-source.ts
4743
5090
  import { coreApi as coreApi2, parse as parse2 } from "@loaders.gl/core";
4744
- import { I3SLoader } from "@loaders.gl/i3s";
4745
- import { I3SSource, Tileset3D as Tileset3D2 } from "@loaders.gl/tiles";
5091
+ import { I3SLoader } from "@loaders.gl/tiles-3d/i3s";
5092
+ import { I3SSource, Tileset3D as Tileset3D2 } from "@loaders.gl/tiles-3d";
4746
5093
  import {
4747
5094
  createI3SConversionSpatialContext,
4748
- TileConversionError as TileConversionError27
5095
+ TileConversionError as TileConversionError29
4749
5096
  } from "@loaders.gl/tile-converter/v5/core";
4750
5097
  function createRuntime(transport, layer, header) {
4751
5098
  const libraries = transport.dracoLibraryOptions;
@@ -4857,7 +5204,7 @@ async function createSelectedI3SMeshConversionSource(inspection, options) {
4857
5204
  validateSelectionOptions(options);
4858
5205
  signal.throwIfAborted();
4859
5206
  if (inspection.rootUrl !== options.rootUrl)
4860
- throw new TileConversionError27(
5207
+ throw new TileConversionError29(
4861
5208
  "INVALID_I3S_SELECTION",
4862
5209
  "Selection transport must match the inspected layer"
4863
5210
  );
@@ -4907,7 +5254,7 @@ async function createSelectedI3SMeshConversionSource(inspection, options) {
4907
5254
  for await (const resource of meshSource.read(metadata, signal)) {
4908
5255
  decodedBytes += measureMeshBytes(resource);
4909
5256
  if (++meshCount > SELECTED_MESH_LIMITS.maxMeshResources || decodedBytes > (options.maxInputBytes ?? SELECTED_MESH_LIMITS.maxInputBytes))
4910
- throw new TileConversionError27(
5257
+ throw new TileConversionError29(
4911
5258
  "INPUT_RESOURCE_TOO_LARGE",
4912
5259
  "Selected I3S decoded resources exceed the input/mesh budget"
4913
5260
  );
@@ -4934,7 +5281,7 @@ function validateSelectionOptions(options) {
4934
5281
  ]
4935
5282
  ])
4936
5283
  if (!Number.isSafeInteger(value) || value < 1 || value > maximum)
4937
- throw new TileConversionError27(
5284
+ throw new TileConversionError29(
4938
5285
  "INVALID_I3S_SELECTION_LIMIT",
4939
5286
  "I3S selection limits must be positive safe integers within the bounded profile"
4940
5287
  );
@@ -4945,6 +5292,7 @@ export {
4945
5292
  convertPointCloudSource,
4946
5293
  convertPointCloudToPotree,
4947
5294
  convertPointCloudToTileset,
5295
+ createCOPCConversionCodec,
4948
5296
  createI3SMeshConversionCodec,
4949
5297
  createI3SMeshSink,
4950
5298
  createI3SMeshTilesetConversionSource,
@@ -4963,6 +5311,7 @@ export {
4963
5311
  createTileConversionArchiveTransport,
4964
5312
  createTileConversionResourceFetcher,
4965
5313
  encodeMeshTile,
5314
+ encodePointCloudCOPC,
4966
5315
  encodePointCloudSource,
4967
5316
  encodePointCloudSourceTile,
4968
5317
  encodePointCloudTile,
@@ -4971,6 +5320,7 @@ export {
4971
5320
  encodeTileConversionArchiveInBatches,
4972
5321
  extractMeshVertices,
4973
5322
  inspectI3SMeshConversionSource,
5323
+ mapPointCloudAttributes,
4974
5324
  measureMeshBytes,
4975
5325
  openPointCloudFile,
4976
5326
  openTileConversionArchive,