itowns 2.45.1-next.0 → 2.45.1-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/455.js +2 -0
- package/dist/455.js.map +1 -0
- package/dist/debug.js +3 -0
- package/dist/debug.js.LICENSE.txt +13 -0
- package/dist/debug.js.map +1 -0
- package/dist/itowns.js +3 -0
- package/dist/itowns.js.LICENSE.txt +5 -0
- package/dist/itowns.js.map +1 -0
- package/dist/itowns_lasparser.js +2 -0
- package/dist/itowns_lasparser.js.map +1 -0
- package/dist/itowns_lasworker.js +2 -0
- package/dist/itowns_lasworker.js.map +1 -0
- package/dist/itowns_potree2worker.js +2 -0
- package/dist/itowns_potree2worker.js.map +1 -0
- package/dist/itowns_widgets.js +2 -0
- package/dist/itowns_widgets.js.map +1 -0
- package/lib/Controls/FirstPersonControls.js +308 -0
- package/lib/Controls/FlyControls.js +175 -0
- package/lib/Controls/GlobeControls.js +1178 -0
- package/lib/Controls/PlanarControls.js +1025 -0
- package/lib/Controls/StateControl.js +432 -0
- package/lib/Controls/StreetControls.js +392 -0
- package/lib/Converter/Feature2Mesh.js +612 -0
- package/lib/Converter/Feature2Texture.js +174 -0
- package/lib/Converter/convertToTile.js +70 -0
- package/lib/Converter/textureConverter.js +43 -0
- package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
- package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
- package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
- package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
- package/lib/Core/3DTiles/C3DTFeature.js +110 -0
- package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
- package/lib/Core/3DTiles/C3DTileset.js +99 -0
- package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
- package/lib/Core/AnimationPlayer.js +142 -0
- package/lib/Core/CopcNode.js +174 -0
- package/lib/Core/Deprecated/Undeprecator.js +74 -0
- package/lib/Core/EntwinePointTileNode.js +126 -0
- package/lib/Core/Feature.js +488 -0
- package/lib/Core/Geographic/GeoidGrid.js +108 -0
- package/lib/Core/Label.js +222 -0
- package/lib/Core/MainLoop.js +209 -0
- package/lib/Core/Picking.js +255 -0
- package/lib/Core/PointCloudNode.js +42 -0
- package/lib/Core/Potree2Node.js +206 -0
- package/lib/Core/Potree2PointAttributes.js +139 -0
- package/lib/Core/PotreeNode.js +101 -0
- package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
- package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
- package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
- package/lib/Core/Prefab/GlobeView.js +155 -0
- package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
- package/lib/Core/Prefab/PlanarView.js +62 -0
- package/lib/Core/Prefab/TileBuilder.js +82 -0
- package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
- package/lib/Core/Scheduler/Cache.js +17 -0
- package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
- package/lib/Core/Scheduler/Scheduler.js +294 -0
- package/lib/Core/Style.js +660 -0
- package/lib/Core/StyleOptions.js +486 -0
- package/lib/Core/System/Capabilities.js +63 -0
- package/lib/Core/Tile/Tile.js +205 -0
- package/lib/Core/Tile/TileGrid.js +49 -0
- package/lib/Core/TileGeometry.js +124 -0
- package/lib/Core/TileMesh.js +108 -0
- package/lib/Core/View.js +1115 -0
- package/lib/Layer/C3DTilesLayer.js +459 -0
- package/lib/Layer/ColorLayer.js +154 -0
- package/lib/Layer/CopcLayer.js +63 -0
- package/lib/Layer/ElevationLayer.js +139 -0
- package/lib/Layer/EntwinePointTileLayer.js +71 -0
- package/lib/Layer/FeatureGeometryLayer.js +77 -0
- package/lib/Layer/GeoidLayer.js +80 -0
- package/lib/Layer/GeometryLayer.js +233 -0
- package/lib/Layer/InfoLayer.js +64 -0
- package/lib/Layer/LabelLayer.js +469 -0
- package/lib/Layer/Layer.js +335 -0
- package/lib/Layer/LayerUpdateState.js +89 -0
- package/lib/Layer/LayerUpdateStrategy.js +80 -0
- package/lib/Layer/OGC3DTilesLayer.js +543 -0
- package/lib/Layer/OrientedImageLayer.js +227 -0
- package/lib/Layer/PointCloudLayer.js +405 -0
- package/lib/Layer/Potree2Layer.js +171 -0
- package/lib/Layer/PotreeLayer.js +72 -0
- package/lib/Layer/RasterLayer.js +37 -0
- package/lib/Layer/ReferencingLayerProperties.js +62 -0
- package/lib/Layer/TiledGeometryLayer.js +459 -0
- package/lib/Loader/LASLoader.js +193 -0
- package/lib/Loader/Potree2BrotliLoader.js +261 -0
- package/lib/Loader/Potree2Loader.js +207 -0
- package/lib/Main.js +113 -0
- package/lib/MainBundle.js +4 -0
- package/lib/Parser/B3dmParser.js +174 -0
- package/lib/Parser/CameraCalibrationParser.js +94 -0
- package/lib/Parser/GDFParser.js +72 -0
- package/lib/Parser/GTXParser.js +75 -0
- package/lib/Parser/GeoJsonParser.js +212 -0
- package/lib/Parser/GpxParser.js +25 -0
- package/lib/Parser/ISGParser.js +71 -0
- package/lib/Parser/KMLParser.js +25 -0
- package/lib/Parser/LASParser.js +137 -0
- package/lib/Parser/MapBoxUrlParser.js +83 -0
- package/lib/Parser/PntsParser.js +131 -0
- package/lib/Parser/Potree2BinParser.js +92 -0
- package/lib/Parser/PotreeBinParser.js +106 -0
- package/lib/Parser/PotreeCinParser.js +29 -0
- package/lib/Parser/ShapefileParser.js +78 -0
- package/lib/Parser/VectorTileParser.js +215 -0
- package/lib/Parser/XbilParser.js +120 -0
- package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
- package/lib/Parser/iGLTFLoader.js +168 -0
- package/lib/Process/3dTilesProcessing.js +304 -0
- package/lib/Process/FeatureProcessing.js +76 -0
- package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
- package/lib/Process/ObjectRemovalHelper.js +97 -0
- package/lib/Process/handlerNodeError.js +23 -0
- package/lib/Provider/3dTilesProvider.js +149 -0
- package/lib/Provider/DataSourceProvider.js +24 -0
- package/lib/Provider/Fetcher.js +233 -0
- package/lib/Provider/PointCloudProvider.js +45 -0
- package/lib/Provider/TileProvider.js +16 -0
- package/lib/Provider/URLBuilder.js +116 -0
- package/lib/Renderer/Camera.js +281 -0
- package/lib/Renderer/Color.js +56 -0
- package/lib/Renderer/ColorLayersOrdering.js +115 -0
- package/lib/Renderer/CommonMaterial.js +31 -0
- package/lib/Renderer/Label2DRenderer.js +192 -0
- package/lib/Renderer/LayeredMaterial.js +243 -0
- package/lib/Renderer/OBB.js +150 -0
- package/lib/Renderer/OrientedImageCamera.js +118 -0
- package/lib/Renderer/OrientedImageMaterial.js +167 -0
- package/lib/Renderer/PointsMaterial.js +485 -0
- package/lib/Renderer/RasterTile.js +243 -0
- package/lib/Renderer/RenderMode.js +31 -0
- package/lib/Renderer/Shader/ShaderChunk.js +160 -0
- package/lib/Renderer/Shader/ShaderUtils.js +47 -0
- package/lib/Renderer/SphereHelper.js +17 -0
- package/lib/Renderer/WebXR.js +51 -0
- package/lib/Renderer/c3DEngine.js +214 -0
- package/lib/Source/C3DTilesGoogleSource.js +74 -0
- package/lib/Source/C3DTilesIonSource.js +54 -0
- package/lib/Source/C3DTilesSource.js +30 -0
- package/lib/Source/CopcSource.js +126 -0
- package/lib/Source/EntwinePointTileSource.js +72 -0
- package/lib/Source/FileSource.js +188 -0
- package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
- package/lib/Source/OGC3DTilesIonSource.js +34 -0
- package/lib/Source/OGC3DTilesSource.js +21 -0
- package/lib/Source/OrientedImageSource.js +59 -0
- package/lib/Source/Potree2Source.js +167 -0
- package/lib/Source/PotreeSource.js +82 -0
- package/lib/Source/Source.js +202 -0
- package/lib/Source/TMSSource.js +144 -0
- package/lib/Source/VectorTilesSource.js +182 -0
- package/lib/Source/WFSSource.js +170 -0
- package/lib/Source/WMSSource.js +167 -0
- package/lib/Source/WMTSSource.js +92 -0
- package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
- package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
- package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
- package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
- package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
- package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
- package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
- package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
- package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
- package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
- package/lib/Utils/CameraUtils.js +554 -0
- package/lib/Utils/DEMUtils.js +350 -0
- package/lib/Utils/FeaturesUtils.js +156 -0
- package/lib/Utils/Gradients.js +16 -0
- package/lib/Utils/ThreeUtils.js +115 -0
- package/lib/Utils/gui/C3DTilesStyle.js +218 -0
- package/lib/Utils/gui/Main.js +7 -0
- package/lib/Utils/gui/Minimap.js +152 -0
- package/lib/Utils/gui/Navigation.js +245 -0
- package/lib/Utils/gui/Scale.js +104 -0
- package/lib/Utils/gui/Searchbar.js +234 -0
- package/lib/Utils/gui/Widget.js +80 -0
- package/lib/Utils/placeObjectOnGround.js +136 -0
- package/lib/Worker/LASLoaderWorker.js +19 -0
- package/lib/Worker/Potree2Worker.js +21 -0
- package/package.json +2 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { Extent } from '@itowns/geographic';
|
|
3
|
+
import GeoidGrid from "../Core/Geographic/GeoidGrid.js";
|
|
4
|
+
import { getHeaderAttribute } from "./GDFParser.js";
|
|
5
|
+
import { BYTES_PER_DOUBLE } from "./GTXParser.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The `ISGParser` module provides a `[parse]{@link module:ISGParser.parse}` method. This method takes the content of a
|
|
9
|
+
* ISG file in, and returns a `{@link GeoidGrid}`. the `{@link GeoidGrid}` contains all the necessary attributes and
|
|
10
|
+
* methods to access the ISG data in iTowns.
|
|
11
|
+
*
|
|
12
|
+
* @module ISGParser
|
|
13
|
+
*/
|
|
14
|
+
export default {
|
|
15
|
+
/**
|
|
16
|
+
* Parses an ISG file content and returns a corresponding {@link GeoidGrid}.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} isg The content of the ISG file to parse.
|
|
19
|
+
* @param {Object} options An object gathering the optional parameters to pass to
|
|
20
|
+
* the parser.
|
|
21
|
+
* @param {Object} [options.in={}] Information on the ISG data.
|
|
22
|
+
* @param {string} [options.in.crs='EPSG:4326'] The Coordinates Reference System (CRS) of the ISG data.
|
|
23
|
+
* It must be a geographic CRS, and must be given as an EPSG
|
|
24
|
+
* code.
|
|
25
|
+
*
|
|
26
|
+
* @returns {Promise<GeoidGrid>} A promise resolving with a {@link GeoidGrid}, which contains all the necessary
|
|
27
|
+
* attributes and methods to access ISG file data.
|
|
28
|
+
*/
|
|
29
|
+
parse(isg) {
|
|
30
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
31
|
+
in: {}
|
|
32
|
+
};
|
|
33
|
+
const rows = isg.split('\n');
|
|
34
|
+
const firstMeasureLine = rows.indexOf(rows.find(row => row.includes('end_of_head'))) + 1;
|
|
35
|
+
const rawHeaderData = rows.slice(0, firstMeasureLine);
|
|
36
|
+
|
|
37
|
+
// ---------- GET METADATA FROM THE FILE : ----------
|
|
38
|
+
|
|
39
|
+
const metadata = {
|
|
40
|
+
minX: getHeaderAttribute(rawHeaderData, 'lon min'),
|
|
41
|
+
maxX: getHeaderAttribute(rawHeaderData, 'lon max'),
|
|
42
|
+
minY: getHeaderAttribute(rawHeaderData, 'lat min'),
|
|
43
|
+
maxY: getHeaderAttribute(rawHeaderData, 'lat max'),
|
|
44
|
+
stepX: getHeaderAttribute(rawHeaderData, 'delta lon'),
|
|
45
|
+
stepY: getHeaderAttribute(rawHeaderData, 'delta lat'),
|
|
46
|
+
nRows: getHeaderAttribute(rawHeaderData, 'nrows'),
|
|
47
|
+
nColumns: getHeaderAttribute(rawHeaderData, 'ncols')
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ---------- BUILD A DATA VIEWER FROM THE TEXT DATA : ----------
|
|
51
|
+
|
|
52
|
+
const data = new DataView(new ArrayBuffer(BYTES_PER_DOUBLE * metadata.nRows * metadata.nColumns));
|
|
53
|
+
let index = 0;
|
|
54
|
+
for (let row of rows.slice(firstMeasureLine, rows.length)) {
|
|
55
|
+
row = row.split(' ').filter(value => value !== '');
|
|
56
|
+
if (!row.length) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const value of row) {
|
|
60
|
+
data.setFloat64(index * BYTES_PER_DOUBLE, parseFloat(value));
|
|
61
|
+
index++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------- CREATE A GeoidGrid FOR THE GIVEN FILE DATA : ----------
|
|
66
|
+
|
|
67
|
+
const dataExtent = new Extent(options.in.crs || 'EPSG:4326', metadata.minX + metadata.stepX / 2, metadata.maxX - metadata.stepX / 2, metadata.minY + metadata.stepY / 2, metadata.maxY - metadata.stepY / 2);
|
|
68
|
+
const dataStep = new THREE.Vector2(metadata.stepX, metadata.stepY);
|
|
69
|
+
return Promise.resolve(new GeoidGrid(dataExtent, dataStep, (verticalIndex, horizontalIndex) => data.getFloat64((metadata.nColumns * verticalIndex + horizontalIndex) * BYTES_PER_DOUBLE)));
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { kml } from '@tmcw/togeojson';
|
|
2
|
+
import GeoJsonParser from "./GeoJsonParser.js";
|
|
3
|
+
import { deprecatedParsingOptionsToNewOne } from "../Core/Deprecated/Undeprecator.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The KMLParser module provides a [parse]{@link module:KMLParser.parse}
|
|
7
|
+
* method that takes a KML in and gives an object formatted for iTowns
|
|
8
|
+
* containing all necessary informations to display this KML.
|
|
9
|
+
*
|
|
10
|
+
* @module KMLParser
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
/**
|
|
14
|
+
* Parse a KML file content and return a {@link FeatureCollection}.
|
|
15
|
+
*
|
|
16
|
+
* @param {XMLDocument} kmlFile - The KML file content to parse.
|
|
17
|
+
* @param {ParsingOptions} options - Options controlling the parsing.
|
|
18
|
+
*
|
|
19
|
+
* @return {Promise} A promise resolving with a {@link FeatureCollection}.
|
|
20
|
+
*/
|
|
21
|
+
parse(kmlFile, options) {
|
|
22
|
+
options = deprecatedParsingOptionsToNewOne(options);
|
|
23
|
+
return GeoJsonParser.parse(kml(kmlFile), options);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { spawn, Thread, Transfer } from 'threads';
|
|
3
|
+
let _lazPerf;
|
|
4
|
+
let _thread;
|
|
5
|
+
function workerInstance() {
|
|
6
|
+
return new Worker(/* webpackChunkName: "itowns_lasparser" */
|
|
7
|
+
new URL('../Worker/LASLoaderWorker.js', import.meta.url), {
|
|
8
|
+
type: 'module'
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async function loader() {
|
|
12
|
+
if (_thread) {
|
|
13
|
+
return _thread;
|
|
14
|
+
}
|
|
15
|
+
_thread = await spawn(workerInstance());
|
|
16
|
+
if (_lazPerf) {
|
|
17
|
+
_thread.lazPerf(_lazPerf);
|
|
18
|
+
}
|
|
19
|
+
return _thread;
|
|
20
|
+
}
|
|
21
|
+
function buildBufferGeometry(attributes) {
|
|
22
|
+
const geometry = new THREE.BufferGeometry();
|
|
23
|
+
const positionBuffer = new THREE.BufferAttribute(attributes.position, 3);
|
|
24
|
+
geometry.setAttribute('position', positionBuffer);
|
|
25
|
+
const intensityBuffer = new THREE.BufferAttribute(attributes.intensity, 1);
|
|
26
|
+
geometry.setAttribute('intensity', intensityBuffer);
|
|
27
|
+
const returnNumber = new THREE.BufferAttribute(attributes.returnNumber, 1);
|
|
28
|
+
geometry.setAttribute('returnNumber', returnNumber);
|
|
29
|
+
const numberOfReturns = new THREE.BufferAttribute(attributes.numberOfReturns, 1);
|
|
30
|
+
geometry.setAttribute('numberOfReturns', numberOfReturns);
|
|
31
|
+
const classBuffer = new THREE.BufferAttribute(attributes.classification, 1);
|
|
32
|
+
geometry.setAttribute('classification', classBuffer);
|
|
33
|
+
const pointSourceID = new THREE.BufferAttribute(attributes.pointSourceID, 1);
|
|
34
|
+
geometry.setAttribute('pointSourceID', pointSourceID);
|
|
35
|
+
if (attributes.color) {
|
|
36
|
+
const colorBuffer = new THREE.BufferAttribute(attributes.color, 4, true);
|
|
37
|
+
geometry.setAttribute('color', colorBuffer);
|
|
38
|
+
}
|
|
39
|
+
const scanAngle = new THREE.BufferAttribute(attributes.scanAngle, 1);
|
|
40
|
+
geometry.setAttribute('scanAngle', scanAngle);
|
|
41
|
+
geometry.userData.origin = new THREE.Vector3().fromArray(attributes.origin);
|
|
42
|
+
return geometry;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The LASParser module provides a [parse]{@link
|
|
46
|
+
* module:LASParser.parse} method that takes a LAS or LAZ (LASZip) file in, and
|
|
47
|
+
* gives a `THREE.BufferGeometry` containing all the necessary attributes to be
|
|
48
|
+
* displayed in iTowns. It uses the
|
|
49
|
+
* [copc.js](https://github.com/connormanning/copc.js/) library.
|
|
50
|
+
*
|
|
51
|
+
* @module LASParser
|
|
52
|
+
*/
|
|
53
|
+
export default {
|
|
54
|
+
/*
|
|
55
|
+
* Set the laz-perf decoder path.
|
|
56
|
+
* @param {string} path - path to `laz-perf.wasm` folder.
|
|
57
|
+
*/
|
|
58
|
+
enableLazPerf(path) {
|
|
59
|
+
if (!path) {
|
|
60
|
+
throw new Error('Path to laz-perf is mandatory');
|
|
61
|
+
}
|
|
62
|
+
_lazPerf = path;
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* Terminate all worker instances.
|
|
66
|
+
* @returns {Promise<void>}
|
|
67
|
+
*/
|
|
68
|
+
terminate() {
|
|
69
|
+
const currentThread = _thread;
|
|
70
|
+
_thread = undefined;
|
|
71
|
+
return Thread.terminate(currentThread);
|
|
72
|
+
},
|
|
73
|
+
/**
|
|
74
|
+
* Parses a chunk of a LAS or LAZ (LASZip) and returns the corresponding
|
|
75
|
+
* `THREE.BufferGeometry`.
|
|
76
|
+
*
|
|
77
|
+
* @param {ArrayBuffer} data - The file content to parse.
|
|
78
|
+
* @param {Object} options
|
|
79
|
+
* @param {Object} options.in - Options to give to the parser.
|
|
80
|
+
* @param {number} options.in.pointCount - Number of points encoded in this
|
|
81
|
+
* data chunk.
|
|
82
|
+
* @param {Object} options.in.header - Partial LAS file header.
|
|
83
|
+
* @param {number} options.in.header.pointDataRecordFormat - Type of Point
|
|
84
|
+
* Data Record contained in the LAS file.
|
|
85
|
+
* @param {number} options.in.header.pointDataRecordLength - Size (in bytes)
|
|
86
|
+
* of the Point Data Record.
|
|
87
|
+
* @param {Object} [options.eb] - Extra bytes LAS VLRs headers.
|
|
88
|
+
* @param { 8 | 16 } [options.in.colorDepth] - Color depth (in bits).
|
|
89
|
+
* Defaults to 8 bits for LAS 1.2 and 16 bits for later versions
|
|
90
|
+
* (as mandatory by the specification)
|
|
91
|
+
*
|
|
92
|
+
* @return {Promise<THREE.BufferGeometry>} A promise resolving with a
|
|
93
|
+
* `THREE.BufferGeometry`.
|
|
94
|
+
*/
|
|
95
|
+
async parseChunk(data) {
|
|
96
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
97
|
+
const lasLoader = await loader();
|
|
98
|
+
const parsedData = await lasLoader.parseChunk(Transfer(data), {
|
|
99
|
+
pointCount: options.in.pointCount,
|
|
100
|
+
header: options.in.header,
|
|
101
|
+
eb: options.eb,
|
|
102
|
+
colorDepth: options.in.colorDepth
|
|
103
|
+
});
|
|
104
|
+
const geometry = buildBufferGeometry(parsedData.attributes);
|
|
105
|
+
geometry.computeBoundingBox();
|
|
106
|
+
return geometry;
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* Parses a LAS file or a LAZ (LASZip) file and return the corresponding
|
|
110
|
+
* `THREE.BufferGeometry`.
|
|
111
|
+
*
|
|
112
|
+
* @param {ArrayBuffer} data - The file content to parse.
|
|
113
|
+
* @param {Object} [options]
|
|
114
|
+
* @param {Object} [options.in] - Options to give to the parser.
|
|
115
|
+
* @param { 8 | 16 } [options.in.colorDepth] - Color depth (in bits).
|
|
116
|
+
* Defaults to 8 bits for LAS 1.2 and 16 bits for later versions
|
|
117
|
+
* (as mandatory by the specification)
|
|
118
|
+
*
|
|
119
|
+
* @return {Promise} A promise resolving with a `THREE.BufferGeometry`. The
|
|
120
|
+
* header of the file is contained in `userData`.
|
|
121
|
+
*/
|
|
122
|
+
async parse(data) {
|
|
123
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
124
|
+
if (options.out?.skip) {
|
|
125
|
+
console.warn("Warning: options 'skip' not supported anymore");
|
|
126
|
+
}
|
|
127
|
+
const input = options.in;
|
|
128
|
+
const lasLoader = await loader();
|
|
129
|
+
const parsedData = await lasLoader.parseFile(Transfer(data), {
|
|
130
|
+
colorDepth: input?.colorDepth
|
|
131
|
+
});
|
|
132
|
+
const geometry = buildBufferGeometry(parsedData.attributes);
|
|
133
|
+
geometry.userData.header = parsedData.header;
|
|
134
|
+
geometry.computeBoundingBox();
|
|
135
|
+
return geometry;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const urlRe = /^(\w+):\/\/([^/?]*)(\/[^?]+)?\??(.+)?/;
|
|
2
|
+
const config = {
|
|
3
|
+
API_URL: 'https://api.mapbox.com',
|
|
4
|
+
REQUIRE_ACCESS_TOKEN: true,
|
|
5
|
+
ACCESS_TOKEN: null
|
|
6
|
+
};
|
|
7
|
+
function formatUrl(obj) {
|
|
8
|
+
const params = obj.params.length ? `?${obj.params.join('&')}` : '';
|
|
9
|
+
return `${obj.protocol}://${obj.authority}${obj.path}${params}`;
|
|
10
|
+
}
|
|
11
|
+
function makeAPIURL(urlObject, accessToken) {
|
|
12
|
+
const apiUrlObject = parseUrl(config.API_URL);
|
|
13
|
+
urlObject.protocol = apiUrlObject.protocol;
|
|
14
|
+
urlObject.authority = apiUrlObject.authority;
|
|
15
|
+
if (urlObject.protocol === 'http') {
|
|
16
|
+
const i = urlObject.params.indexOf('secure');
|
|
17
|
+
if (i >= 0) {
|
|
18
|
+
urlObject.params.splice(i, 1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (apiUrlObject.path !== '/') {
|
|
22
|
+
urlObject.path = `${apiUrlObject.path}${urlObject.path}`;
|
|
23
|
+
}
|
|
24
|
+
if (!config.REQUIRE_ACCESS_TOKEN) {
|
|
25
|
+
return formatUrl(urlObject);
|
|
26
|
+
}
|
|
27
|
+
accessToken = accessToken || config.ACCESS_TOKEN;
|
|
28
|
+
if (!accessToken) {
|
|
29
|
+
throw new Error('An API access token is required');
|
|
30
|
+
}
|
|
31
|
+
if (accessToken[0] === 's') {
|
|
32
|
+
throw new Error('Use a public access token (pk.*), not a secret access token (sk.*).');
|
|
33
|
+
}
|
|
34
|
+
urlObject.params = urlObject.params.filter(d => d.indexOf('access_token') === -1);
|
|
35
|
+
urlObject.params.push(`access_token=${accessToken}`);
|
|
36
|
+
return formatUrl(urlObject);
|
|
37
|
+
}
|
|
38
|
+
function isMapboxURL(url) {
|
|
39
|
+
return url.indexOf('mapbox:') === 0;
|
|
40
|
+
}
|
|
41
|
+
function parseUrl(url) {
|
|
42
|
+
const parts = url.match(urlRe);
|
|
43
|
+
if (!parts) {
|
|
44
|
+
throw new Error('Unable to parse URL object');
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
protocol: parts[1],
|
|
48
|
+
authority: parts[2],
|
|
49
|
+
path: parts[3] || '/',
|
|
50
|
+
params: parts[4] ? parts[4].split('&') : []
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function normalizeSpriteURL(url, format, extension, accessToken) {
|
|
54
|
+
const urlObject = parseUrl(url);
|
|
55
|
+
if (!isMapboxURL(url)) {
|
|
56
|
+
urlObject.path += `${format}${extension}`;
|
|
57
|
+
return formatUrl(urlObject);
|
|
58
|
+
}
|
|
59
|
+
urlObject.path = `/styles/v1${urlObject.path}/sprite${format}${extension}`;
|
|
60
|
+
return makeAPIURL(urlObject, accessToken);
|
|
61
|
+
}
|
|
62
|
+
function normalizeSourceURL(url, accessToken) {
|
|
63
|
+
if (!isMapboxURL(url)) {
|
|
64
|
+
return url;
|
|
65
|
+
}
|
|
66
|
+
const urlObject = parseUrl(url);
|
|
67
|
+
urlObject.path = `/v4/${urlObject.authority}.json`;
|
|
68
|
+
urlObject.params.push('secure');
|
|
69
|
+
return makeAPIURL(urlObject, accessToken);
|
|
70
|
+
}
|
|
71
|
+
function normalizeStyleURL(url, accessToken) {
|
|
72
|
+
if (!isMapboxURL(url)) {
|
|
73
|
+
return url;
|
|
74
|
+
}
|
|
75
|
+
const urlObject = parseUrl(url);
|
|
76
|
+
urlObject.path = `/styles/v1${urlObject.path}`;
|
|
77
|
+
return makeAPIURL(urlObject, accessToken);
|
|
78
|
+
}
|
|
79
|
+
export default {
|
|
80
|
+
normalizeStyleURL,
|
|
81
|
+
normalizeSourceURL,
|
|
82
|
+
normalizeSpriteURL
|
|
83
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import C3DTBatchTable from "../Core/3DTiles/C3DTBatchTable.js";
|
|
3
|
+
const utf8Decoder = new TextDecoder();
|
|
4
|
+
export default {
|
|
5
|
+
/** @module PntsParser */
|
|
6
|
+
/** Parse pnts buffer and extract THREE.Points and batch table
|
|
7
|
+
* @function parse
|
|
8
|
+
* @param {ArrayBuffer} buffer - the pnts buffer.
|
|
9
|
+
* @param {Object} registeredExtensions - 3D Tiles extensions registered
|
|
10
|
+
* in the layer
|
|
11
|
+
* @return {Promise} - a promise that resolves with an object containig a THREE.Points (point) and a batch table (batchTable).
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
parse: function (buffer, registeredExtensions) {
|
|
15
|
+
if (!buffer) {
|
|
16
|
+
throw new Error('No array buffer provided.');
|
|
17
|
+
}
|
|
18
|
+
const view = new DataView(buffer);
|
|
19
|
+
let byteOffset = 0;
|
|
20
|
+
const pntsHeader = {};
|
|
21
|
+
let batchTable = {};
|
|
22
|
+
let point = {};
|
|
23
|
+
|
|
24
|
+
// Magic type is unsigned char [4]
|
|
25
|
+
pntsHeader.magic = utf8Decoder.decode(new Uint8Array(buffer, byteOffset, 4));
|
|
26
|
+
byteOffset += 4;
|
|
27
|
+
if (pntsHeader.magic) {
|
|
28
|
+
// Version, byteLength, batchTableJSONByteLength, batchTableBinaryByteLength and batchTable types are uint32
|
|
29
|
+
pntsHeader.version = view.getUint32(byteOffset, true);
|
|
30
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
31
|
+
pntsHeader.byteLength = view.getUint32(byteOffset, true);
|
|
32
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
33
|
+
pntsHeader.FTJSONLength = view.getUint32(byteOffset, true);
|
|
34
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
35
|
+
pntsHeader.FTBinaryLength = view.getUint32(byteOffset, true);
|
|
36
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
37
|
+
pntsHeader.BTJSONLength = view.getUint32(byteOffset, true);
|
|
38
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
39
|
+
pntsHeader.BTBinaryLength = view.getUint32(byteOffset, true);
|
|
40
|
+
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
|
|
41
|
+
|
|
42
|
+
// feature table
|
|
43
|
+
let FTJSON = {};
|
|
44
|
+
if (pntsHeader.FTJSONLength > 0) {
|
|
45
|
+
const sizeBegin = byteOffset;
|
|
46
|
+
const jsonBuffer = buffer.slice(sizeBegin, pntsHeader.FTJSONLength + sizeBegin);
|
|
47
|
+
const content = utf8Decoder.decode(new Uint8Array(jsonBuffer));
|
|
48
|
+
FTJSON = JSON.parse(content);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// binary table
|
|
52
|
+
if (pntsHeader.FTBinaryLength > 0) {
|
|
53
|
+
point = parseFeatureBinary(buffer, byteOffset, pntsHeader.FTJSONLength);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// batch table
|
|
57
|
+
if (pntsHeader.BTJSONLength > 0) {
|
|
58
|
+
// parse batch table
|
|
59
|
+
const sizeBegin = byteOffset + pntsHeader.FTJSONLength + pntsHeader.FTBinaryLength;
|
|
60
|
+
const BTBuffer = buffer.slice(sizeBegin, pntsHeader.BTJSONLength + pntsHeader.BTBinaryLength + sizeBegin);
|
|
61
|
+
|
|
62
|
+
// If the BATCH_ID semantic is not defined, then the Batch Table stores per-point metadata, and the length of the Batch Table arrays will equal POINTS_LENGTH.
|
|
63
|
+
batchTable = new C3DTBatchTable(BTBuffer, pntsHeader.BTJSONLength, pntsHeader.BTBinaryLength, FTJSON.BATCH_ID && FTJSON.BATCH_LENGTH ? FTJSON.BATCH_LENGTH : FTJSON.POINTS_LENGTH, registeredExtensions);
|
|
64
|
+
point = setClassification(point, batchTable);
|
|
65
|
+
}
|
|
66
|
+
const pnts = {
|
|
67
|
+
point,
|
|
68
|
+
batchTable
|
|
69
|
+
};
|
|
70
|
+
return Promise.resolve(pnts);
|
|
71
|
+
} else {
|
|
72
|
+
throw new Error('Invalid pnts file.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function parseFeatureBinary(array, byteOffset, FTJSONLength) {
|
|
77
|
+
// Init geometry
|
|
78
|
+
const geometry = new THREE.BufferGeometry();
|
|
79
|
+
|
|
80
|
+
// init Array feature binary
|
|
81
|
+
const subArrayJson = utf8Decoder.decode(new Uint8Array(array, byteOffset, FTJSONLength));
|
|
82
|
+
const parseJSON = JSON.parse(subArrayJson);
|
|
83
|
+
let lengthFeature;
|
|
84
|
+
if (parseJSON.POINTS_LENGTH) {
|
|
85
|
+
lengthFeature = parseJSON.POINTS_LENGTH;
|
|
86
|
+
}
|
|
87
|
+
if (parseJSON.POSITION) {
|
|
88
|
+
const byteOffsetPos = parseJSON.POSITION.byteOffset + subArrayJson.length + byteOffset;
|
|
89
|
+
const positionArray = new Float32Array(array, byteOffsetPos, lengthFeature * 3);
|
|
90
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3));
|
|
91
|
+
}
|
|
92
|
+
if (parseJSON.RGB) {
|
|
93
|
+
const byteOffsetCol = parseJSON.RGB.byteOffset + subArrayJson.length + byteOffset;
|
|
94
|
+
const colorArray = new Uint8Array(array, byteOffsetCol, lengthFeature * 3);
|
|
95
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3, true));
|
|
96
|
+
}
|
|
97
|
+
if (parseJSON.POSITION_QUANTIZED) {
|
|
98
|
+
throw new Error('For pnts loader, POSITION_QUANTIZED: not yet managed');
|
|
99
|
+
}
|
|
100
|
+
if (parseJSON.RGBA) {
|
|
101
|
+
throw new Error('For pnts loader, RGBA: not yet managed');
|
|
102
|
+
}
|
|
103
|
+
if (parseJSON.RGB565) {
|
|
104
|
+
throw new Error('For pnts loader, RGB565: not yet managed');
|
|
105
|
+
}
|
|
106
|
+
if (parseJSON.NORMAL) {
|
|
107
|
+
throw new Error('For pnts loader, NORMAL: not yet managed');
|
|
108
|
+
}
|
|
109
|
+
if (parseJSON.NORMAL_OCT16P) {
|
|
110
|
+
throw new Error('For pnts loader, NORMAL_OCT16P: not yet managed');
|
|
111
|
+
}
|
|
112
|
+
if (parseJSON.BATCH_ID) {
|
|
113
|
+
throw new Error('For pnts loader, BATCH_ID: not yet managed');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Add RTC feature
|
|
117
|
+
const offset = parseJSON.RTC_CENTER ? new THREE.Vector3().fromArray(parseJSON.RTC_CENTER) : undefined;
|
|
118
|
+
return {
|
|
119
|
+
geometry,
|
|
120
|
+
offset
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function setClassification(point, batchTable) {
|
|
124
|
+
if (!point.geometry) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (batchTable.content && batchTable.content.Classification) {
|
|
128
|
+
point.geometry.setAttribute('classification', new THREE.BufferAttribute(new Uint8Array(batchTable.content.Classification), 1));
|
|
129
|
+
}
|
|
130
|
+
return point;
|
|
131
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { spawn, Thread, Transfer } from 'threads';
|
|
3
|
+
let _thread;
|
|
4
|
+
function workerInstance() {
|
|
5
|
+
return new Worker(/* webpackChunkName: "itowns_potree2worker" */
|
|
6
|
+
new URL('../Worker/Potree2Worker.js', import.meta.url), {
|
|
7
|
+
type: 'module'
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
async function loader() {
|
|
11
|
+
if (_thread) {
|
|
12
|
+
return _thread;
|
|
13
|
+
}
|
|
14
|
+
_thread = await spawn(workerInstance());
|
|
15
|
+
return _thread;
|
|
16
|
+
}
|
|
17
|
+
function decoder(w, metadata) {
|
|
18
|
+
return metadata.encoding === 'BROTLI' ? w.parseBrotli : w.parse;
|
|
19
|
+
}
|
|
20
|
+
export default {
|
|
21
|
+
/**
|
|
22
|
+
* @return {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
terminate() {
|
|
25
|
+
const currentThread = _thread;
|
|
26
|
+
_thread = undefined;
|
|
27
|
+
return Thread.terminate(currentThread);
|
|
28
|
+
},
|
|
29
|
+
/** @module Potree2BinParser */
|
|
30
|
+
/** Parse .bin PotreeConverter 2.0 format and convert to a THREE.BufferGeometry
|
|
31
|
+
* @function parse
|
|
32
|
+
* @param {ArrayBuffer} buffer - the bin buffer.
|
|
33
|
+
* @param {Object} options
|
|
34
|
+
* @param {string[]} options.in.pointAttributes - the point attributes information contained in metadata.js
|
|
35
|
+
* @return {Promise} - a promise that resolves with a THREE.BufferGeometry.
|
|
36
|
+
*
|
|
37
|
+
*/
|
|
38
|
+
parse: async function (buffer, options) {
|
|
39
|
+
const metadata = options.in.source.metadata;
|
|
40
|
+
const layer = options.out;
|
|
41
|
+
const pointAttributes = layer.pointAttributes;
|
|
42
|
+
const scale = metadata.scale;
|
|
43
|
+
const box = options.in.bbox;
|
|
44
|
+
const min = box.min;
|
|
45
|
+
const size = box.max.clone().sub(box.min);
|
|
46
|
+
const max = box.max;
|
|
47
|
+
const offset = metadata.offset;
|
|
48
|
+
const numPoints = options.in.numPoints;
|
|
49
|
+
const potreeLoader = await loader();
|
|
50
|
+
const decode = decoder(potreeLoader, metadata);
|
|
51
|
+
const data = await decode(Transfer(buffer), {
|
|
52
|
+
pointAttributes,
|
|
53
|
+
scale,
|
|
54
|
+
min,
|
|
55
|
+
max,
|
|
56
|
+
size,
|
|
57
|
+
offset,
|
|
58
|
+
numPoints
|
|
59
|
+
});
|
|
60
|
+
const buffers = data.attributeBuffers;
|
|
61
|
+
const geometry = new THREE.BufferGeometry();
|
|
62
|
+
Object.keys(buffers).forEach(property => {
|
|
63
|
+
const buffer = buffers[property].buffer;
|
|
64
|
+
if (property === 'position') {
|
|
65
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(buffer), 3));
|
|
66
|
+
} else if (property === 'rgba') {
|
|
67
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(new Uint8Array(buffer), 4, true));
|
|
68
|
+
} else if (property === 'NORMAL') {
|
|
69
|
+
geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(buffer), 3));
|
|
70
|
+
} else if (property === 'INDICES') {
|
|
71
|
+
const bufferAttribute = new THREE.BufferAttribute(new Uint8Array(buffer), 4);
|
|
72
|
+
bufferAttribute.normalized = true;
|
|
73
|
+
geometry.setAttribute('indices', bufferAttribute);
|
|
74
|
+
} else {
|
|
75
|
+
const bufferAttribute = new THREE.BufferAttribute(new Float32Array(buffer), 1);
|
|
76
|
+
const batchAttribute = buffers[property].attribute;
|
|
77
|
+
bufferAttribute.potree = {
|
|
78
|
+
offset: buffers[property].offset,
|
|
79
|
+
scale: buffers[property].scale,
|
|
80
|
+
preciseBuffer: buffers[property].preciseBuffer,
|
|
81
|
+
range: batchAttribute.range
|
|
82
|
+
};
|
|
83
|
+
geometry.setAttribute(property, bufferAttribute);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
geometry.computeBoundingBox();
|
|
87
|
+
return {
|
|
88
|
+
geometry,
|
|
89
|
+
density: data.density
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
// See the different constants holding ordinal, name, numElements, byteSize in PointAttributes.cpp in PotreeConverter
|
|
4
|
+
// elementByteSize is byteSize / numElements
|
|
5
|
+
const POINT_ATTRIBUTES = {
|
|
6
|
+
POSITION_CARTESIAN: {
|
|
7
|
+
numElements: 3,
|
|
8
|
+
arrayType: Float32Array,
|
|
9
|
+
attributeName: 'position'
|
|
10
|
+
},
|
|
11
|
+
COLOR_PACKED: {
|
|
12
|
+
numElements: 4,
|
|
13
|
+
arrayType: Uint8Array,
|
|
14
|
+
attributeName: 'color',
|
|
15
|
+
normalized: true
|
|
16
|
+
},
|
|
17
|
+
INTENSITY: {
|
|
18
|
+
numElements: 1,
|
|
19
|
+
numByte: 2,
|
|
20
|
+
// using Float32Array because Float16Array doesn't exist
|
|
21
|
+
arrayType: Uint16Array,
|
|
22
|
+
attributeName: 'intensity',
|
|
23
|
+
normalized: true
|
|
24
|
+
},
|
|
25
|
+
CLASSIFICATION: {
|
|
26
|
+
numElements: 1,
|
|
27
|
+
arrayType: Uint8Array,
|
|
28
|
+
attributeName: 'classification',
|
|
29
|
+
normalized: true
|
|
30
|
+
},
|
|
31
|
+
// Note: at the time of writing, PotreeConverter will only generate normals in Oct16 format
|
|
32
|
+
// see PotreeConverter.cpp:121
|
|
33
|
+
// we keep all the historical value to still supports old conversion
|
|
34
|
+
NORMAL_SPHEREMAPPED: {
|
|
35
|
+
numElements: 2,
|
|
36
|
+
arrayType: Uint8Array,
|
|
37
|
+
attributeName: 'sphereMappedNormal'
|
|
38
|
+
},
|
|
39
|
+
// see https://web.archive.org/web/20150303053317/http://lgdv.cs.fau.de/get/1602
|
|
40
|
+
NORMAL_OCT16: {
|
|
41
|
+
numElements: 2,
|
|
42
|
+
arrayType: Uint8Array,
|
|
43
|
+
attributeName: 'oct16Normal'
|
|
44
|
+
},
|
|
45
|
+
NORMAL: {
|
|
46
|
+
numElements: 3,
|
|
47
|
+
arrayType: Float32Array,
|
|
48
|
+
attributeName: 'normal'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
for (const potreeName of Object.keys(POINT_ATTRIBUTES)) {
|
|
52
|
+
const attr = POINT_ATTRIBUTES[potreeName];
|
|
53
|
+
attr.potreeName = potreeName;
|
|
54
|
+
attr.numByte = attr.numByte || attr.arrayType.BYTES_PER_ELEMENT;
|
|
55
|
+
attr.byteSize = attr.numElements * attr.numByte;
|
|
56
|
+
attr.normalized = attr.normalized || false;
|
|
57
|
+
// chrome is known to perform badly when we call a method without respecting its arity
|
|
58
|
+
const fnName = `getUint${attr.numByte * 8}`;
|
|
59
|
+
attr.getValue = attr.numByte === 1 ? function (view, offset) {
|
|
60
|
+
return view[fnName](offset);
|
|
61
|
+
} : function (view, offset) {
|
|
62
|
+
return view[fnName](offset, true);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export default {
|
|
66
|
+
/** @module PotreeBinParser */
|
|
67
|
+
/** Parse .bin PotreeConverter format and convert to a THREE.BufferGeometry
|
|
68
|
+
* @function parse
|
|
69
|
+
* @param {ArrayBuffer} buffer - the bin buffer.
|
|
70
|
+
* @param {Object} options
|
|
71
|
+
* @param {string[]} options.in.pointAttributes - the point attributes information contained in cloud.js
|
|
72
|
+
* @return {Promise} - a promise that resolves with a THREE.BufferGeometry.
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
75
|
+
parse: function (buffer, options) {
|
|
76
|
+
if (!buffer) {
|
|
77
|
+
throw new Error('No array buffer provided.');
|
|
78
|
+
}
|
|
79
|
+
const view = new DataView(buffer);
|
|
80
|
+
// Format: X1,Y1,Z1,R1,G1,B1,A1,[...],XN,YN,ZN,RN,GN,BN,AN
|
|
81
|
+
let pointByteSize = 0;
|
|
82
|
+
for (const potreeName of options.in.pointAttributes) {
|
|
83
|
+
pointByteSize += POINT_ATTRIBUTES[potreeName].byteSize;
|
|
84
|
+
}
|
|
85
|
+
const numPoints = Math.floor(buffer.byteLength / pointByteSize);
|
|
86
|
+
const geometry = new THREE.BufferGeometry();
|
|
87
|
+
let elemOffset = 0;
|
|
88
|
+
let attrOffset = 0;
|
|
89
|
+
for (const potreeName of options.in.pointAttributes) {
|
|
90
|
+
const attr = POINT_ATTRIBUTES[potreeName];
|
|
91
|
+
const arrayLength = attr.numElements * numPoints;
|
|
92
|
+
const array = new attr.arrayType(arrayLength);
|
|
93
|
+
for (let arrayOffset = 0; arrayOffset < arrayLength; arrayOffset += attr.numElements) {
|
|
94
|
+
for (let elemIdx = 0; elemIdx < attr.numElements; elemIdx++) {
|
|
95
|
+
array[arrayOffset + elemIdx] = attr.getValue(view, attrOffset + elemIdx * attr.numByte);
|
|
96
|
+
}
|
|
97
|
+
attrOffset += pointByteSize;
|
|
98
|
+
}
|
|
99
|
+
elemOffset += attr.byteSize;
|
|
100
|
+
attrOffset = elemOffset;
|
|
101
|
+
geometry.setAttribute(attr.attributeName, new THREE.BufferAttribute(array, attr.numElements, attr.normalized));
|
|
102
|
+
}
|
|
103
|
+
geometry.computeBoundingBox();
|
|
104
|
+
return Promise.resolve(geometry);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
export default {
|
|
3
|
+
/** @module PotreeCinParser */
|
|
4
|
+
/** Parse .cin PotreeConverter format (see {@link https://github.com/peppsac/PotreeConverter/tree/custom_bin}) and convert to a THREE.BufferGeometry
|
|
5
|
+
* @function parse
|
|
6
|
+
* @param {ArrayBuffer} buffer - the cin buffer.
|
|
7
|
+
* @return {Promise} - a promise that resolves with a THREE.BufferGeometry.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
parse: function (buffer) {
|
|
11
|
+
if (!buffer) {
|
|
12
|
+
throw new Error('No array buffer provided.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Format: MinX,MinY,MinZ,MaxX,MaxY,MaxZ,X1,Y1,Z1,[...],XN,YN,ZN,R1,G1,B1,A1,[...],RN,GN,BN,AN
|
|
16
|
+
const view = new DataView(buffer, 0, 6 * 4);
|
|
17
|
+
const min = new THREE.Vector3(view.getFloat32(0, true), view.getFloat32(4, true), view.getFloat32(8, true));
|
|
18
|
+
const max = new THREE.Vector3(view.getFloat32(12, true), view.getFloat32(16, true), view.getFloat32(20, true));
|
|
19
|
+
const box = new THREE.Box3(min, max);
|
|
20
|
+
const numPoints = Math.floor((buffer.byteLength - 24) / 16);
|
|
21
|
+
const positions = new Float32Array(buffer, 24, 3 * numPoints);
|
|
22
|
+
const colors = new Uint8Array(buffer, 24 + 3 * 4 * numPoints, 4 * numPoints);
|
|
23
|
+
const geometry = new THREE.BufferGeometry();
|
|
24
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
25
|
+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 4, true));
|
|
26
|
+
geometry.boundingBox = box;
|
|
27
|
+
return Promise.resolve(geometry);
|
|
28
|
+
}
|
|
29
|
+
};
|