├── .gitignore
├── LICENSE
├── README.md
├── eslint.config.js
├── example
├── data
│ ├── lion_takanawa
│ │ ├── cloud.js
│ │ └── data
│ │ │ └── r
│ │ │ ├── r.bin
│ │ │ ├── r.hrc
│ │ │ ├── r0.bin
│ │ │ ├── r00.bin
│ │ │ ├── r001.bin
│ │ │ ├── r003.bin
│ │ │ ├── r004.bin
│ │ │ ├── r005.bin
│ │ │ ├── r007.bin
│ │ │ ├── r01.bin
│ │ │ ├── r012.bin
│ │ │ ├── r013.bin
│ │ │ ├── r016.bin
│ │ │ ├── r017.bin
│ │ │ ├── r02.bin
│ │ │ ├── r021.bin
│ │ │ ├── r023.bin
│ │ │ ├── r03.bin
│ │ │ ├── r030.bin
│ │ │ ├── r031.bin
│ │ │ ├── r032.bin
│ │ │ ├── r034.bin
│ │ │ ├── r035.bin
│ │ │ ├── r036.bin
│ │ │ ├── r037.bin
│ │ │ ├── r04.bin
│ │ │ ├── r041.bin
│ │ │ ├── r043.bin
│ │ │ ├── r045.bin
│ │ │ ├── r047.bin
│ │ │ ├── r05.bin
│ │ │ ├── r052.bin
│ │ │ ├── r053.bin
│ │ │ ├── r056.bin
│ │ │ ├── r057.bin
│ │ │ ├── r06.bin
│ │ │ ├── r065.bin
│ │ │ ├── r067.bin
│ │ │ ├── r07.bin
│ │ │ ├── r070.bin
│ │ │ ├── r071.bin
│ │ │ ├── r072.bin
│ │ │ ├── r073.bin
│ │ │ ├── r074.bin
│ │ │ ├── r075.bin
│ │ │ ├── r076.bin
│ │ │ ├── r077.bin
│ │ │ ├── r1.bin
│ │ │ ├── r11.bin
│ │ │ ├── r116.bin
│ │ │ ├── r117.bin
│ │ │ ├── r12.bin
│ │ │ ├── r124.bin
│ │ │ ├── r125.bin
│ │ │ ├── r126.bin
│ │ │ ├── r127.bin
│ │ │ ├── r13.bin
│ │ │ ├── r134.bin
│ │ │ ├── r135.bin
│ │ │ ├── r136.bin
│ │ │ ├── r137.bin
│ │ │ ├── r14.bin
│ │ │ ├── r146.bin
│ │ │ ├── r15.bin
│ │ │ ├── r152.bin
│ │ │ ├── r153.bin
│ │ │ ├── r156.bin
│ │ │ ├── r157.bin
│ │ │ ├── r16.bin
│ │ │ ├── r160.bin
│ │ │ ├── r161.bin
│ │ │ ├── r162.bin
│ │ │ ├── r164.bin
│ │ │ ├── r165.bin
│ │ │ ├── r166.bin
│ │ │ ├── r167.bin
│ │ │ ├── r17.bin
│ │ │ ├── r170.bin
│ │ │ ├── r171.bin
│ │ │ ├── r173.bin
│ │ │ ├── r174.bin
│ │ │ ├── r175.bin
│ │ │ ├── r176.bin
│ │ │ ├── r177.bin
│ │ │ ├── r2.bin
│ │ │ ├── r21.bin
│ │ │ ├── r210.bin
│ │ │ ├── r214.bin
│ │ │ ├── r215.bin
│ │ │ ├── r23.bin
│ │ │ ├── r235.bin
│ │ │ ├── r24.bin
│ │ │ ├── r243.bin
│ │ │ ├── r245.bin
│ │ │ ├── r247.bin
│ │ │ ├── r25.bin
│ │ │ ├── r250.bin
│ │ │ ├── r251.bin
│ │ │ ├── r254.bin
│ │ │ ├── r255.bin
│ │ │ ├── r256.bin
│ │ │ ├── r257.bin
│ │ │ ├── r26.bin
│ │ │ ├── r261.bin
│ │ │ ├── r265.bin
│ │ │ ├── r27.bin
│ │ │ ├── r270.bin
│ │ │ ├── r271.bin
│ │ │ ├── r274.bin
│ │ │ ├── r275.bin
│ │ │ ├── r3.bin
│ │ │ ├── r30.bin
│ │ │ ├── r304.bin
│ │ │ ├── r306.bin
│ │ │ ├── r31.bin
│ │ │ ├── r314.bin
│ │ │ ├── r315.bin
│ │ │ ├── r32.bin
│ │ │ ├── r324.bin
│ │ │ ├── r34.bin
│ │ │ ├── r340.bin
│ │ │ ├── r341.bin
│ │ │ ├── r342.bin
│ │ │ ├── r343.bin
│ │ │ ├── r344.bin
│ │ │ ├── r345.bin
│ │ │ ├── r346.bin
│ │ │ ├── r347.bin
│ │ │ ├── r35.bin
│ │ │ ├── r350.bin
│ │ │ ├── r351.bin
│ │ │ ├── r354.bin
│ │ │ ├── r355.bin
│ │ │ ├── r36.bin
│ │ │ ├── r360.bin
│ │ │ ├── r364.bin
│ │ │ ├── r4.bin
│ │ │ ├── r40.bin
│ │ │ ├── r400.bin
│ │ │ ├── r401.bin
│ │ │ ├── r402.bin
│ │ │ ├── r403.bin
│ │ │ ├── r406.bin
│ │ │ ├── r41.bin
│ │ │ ├── r413.bin
│ │ │ ├── r42.bin
│ │ │ ├── r420.bin
│ │ │ ├── r421.bin
│ │ │ ├── r422.bin
│ │ │ ├── r423.bin
│ │ │ ├── r424.bin
│ │ │ ├── r426.bin
│ │ │ ├── r43.bin
│ │ │ ├── r431.bin
│ │ │ ├── r6.bin
│ │ │ ├── r60.bin
│ │ │ ├── r600.bin
│ │ │ ├── r601.bin
│ │ │ ├── r602.bin
│ │ │ ├── r603.bin
│ │ │ ├── r604.bin
│ │ │ ├── r606.bin
│ │ │ ├── r62.bin
│ │ │ ├── r620.bin
│ │ │ ├── r621.bin
│ │ │ ├── r622.bin
│ │ │ ├── r624.bin
│ │ │ └── r626.bin
│ └── pump
│ │ ├── hierarchy.bin
│ │ ├── log.txt
│ │ ├── metadata.json
│ │ └── octree.bin
├── main.ts
└── tsconfig.json
├── package.json
├── screenshot.png
├── source
├── constants.ts
├── dem-node.ts
├── features.ts
├── index.ts
├── loading
│ ├── binary-loader.ts
│ ├── index.ts
│ ├── load-poc.ts
│ └── types.ts
├── loading2
│ ├── OctreeGeometry.ts
│ ├── OctreeGeometryNode.ts
│ ├── OctreeLoader.ts
│ ├── PointAttributes.ts
│ ├── WorkerPool.ts
│ ├── brotli-decoder.worker.js
│ ├── decoder.worker.js
│ ├── libs
│ │ └── brotli
│ │ │ ├── BUILD
│ │ │ ├── LICENSE
│ │ │ ├── WORKSPACE
│ │ │ ├── decode.js
│ │ │ ├── decode.min.js
│ │ │ ├── decode_test.js
│ │ │ └── polyfill.js
│ └── load-octree.ts
├── materials
│ ├── blur-material.ts
│ ├── classification.ts
│ ├── clipping.ts
│ ├── color-encoding.ts
│ ├── enums.ts
│ ├── gradients
│ │ ├── grayscale.ts
│ │ ├── index.ts
│ │ ├── inferno.ts
│ │ ├── plasma.ts
│ │ ├── rainbow.ts
│ │ ├── spectral.ts
│ │ ├── vidris.ts
│ │ └── yellow-green.ts
│ ├── index.ts
│ ├── point-cloud-material.ts
│ ├── shaders
│ │ ├── blur.fs
│ │ ├── blur.vs
│ │ ├── edl.fs
│ │ ├── edl.vs
│ │ ├── normalize.fs
│ │ ├── normalize.vs
│ │ ├── pointcloud.fs
│ │ └── pointcloud.vs
│ ├── texture-generation.ts
│ └── types.ts
├── point-attributes.ts
├── point-cloud-octree-geometry-node.ts
├── point-cloud-octree-geometry.ts
├── point-cloud-octree-node.ts
├── point-cloud-octree-picker.ts
├── point-cloud-octree.ts
├── point-cloud-tree.ts
├── potree.ts
├── type-predicates.ts
├── types.ts
├── utils
│ ├── binary-heap.d.ts
│ ├── binary-heap.js
│ ├── bounds.ts
│ ├── box3-helper.ts
│ ├── lru.ts
│ ├── math.ts
│ └── utils.ts
├── version.ts
└── workers
│ ├── GreyhoundBinaryDecoderWorker.js
│ ├── LASDecoderWorker.js
│ ├── LazLoaderWorker.js
│ ├── binary-decoder-worker-internal.ts
│ ├── binary-decoder.worker.js
│ └── custom-array-view.ts
├── tsconfig.json
├── webpack.config.js
├── webpack.example.js
└── webpack.prod.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 | dist/
4 |
5 | package-lock.json
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Tentone
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ============
24 | == POTREE ==
25 | ============
26 |
27 | http://potree.org
28 |
29 | Copyright (c) 2011-2017, Markus Schütz
30 | All rights reserved.
31 |
32 | Redistribution and use in source and binary forms, with or without
33 | modification, are permitted provided that the following conditions are met:
34 |
35 | 1. Redistributions of source code must retain the above copyright notice, this
36 | list of conditions and the following disclaimer.
37 | 2. Redistributions in binary form must reproduce the above copyright notice,
38 | this list of conditions and the following disclaimer in the documentation
39 | and/or other materials provided with the distribution.
40 |
41 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
42 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
45 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 |
52 | The views and conclusions contained in the software and documentation are those
53 | of the authors and should not be interpreted as representing official policies,
54 | either expressed or implied, of the FreeBSD Project.
55 |
56 |
57 | =====================
58 | == PLASIO / LASLAZ ==
59 | =====================
60 |
61 | http://plas.io/
62 |
63 | The MIT License (MIT)
64 |
65 | Copyright (c) 2014 Uday Verma, uday.karan@gmail.com
66 |
67 | Permission is hereby granted, free of charge, to any person obtaining a copy
68 | of this software and associated documentation files (the "Software"), to deal
69 | in the Software without restriction, including without limitation the rights
70 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
71 | copies of the Software, and to permit persons to whom the Software is
72 | furnished to do so, subject to the following conditions:
73 |
74 | The above copyright notice and this permission notice shall be included in
75 | all copies or substantial portions of the Software.
76 |
77 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
78 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
79 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
80 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
81 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
82 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
83 | THE SOFTWARE.
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Potree Core 2.0
2 |
3 | [](https://badge.fury.io/js/potree-core)
4 | [](https://badge.fury.io/gh/tentone%2Fpotree-core)
5 |
6 | - This project was originally based on [Potree Viewer 1.6](https://github.com/potree/potree) and is now since version 2.0 based on the [shiukaheng fork](https://github.com/shiukaheng/potree-loader) of the [Potree-Loader](https://github.com/pnext/three-loader).
7 | - Potree is a web based pouint cloud visualizer project created by Markus Schütz.
8 | - This project contains only the main parts of the potree project adapted to be more easily used as a independent library, the code was adapted from the original repositorys.
9 | - Support for pointclouds from LAS, LAZ, Binary files.
10 | - Some features require support for the following GL extensions
11 | - EXT_frag_depth, WEBGL_depth_texture, OES_vertex_array_object
12 |
13 | ## Demo
14 | - Live demo at https://tentone.github.io/potree-core/
15 | - Double click the models to raycast the scene and create marker points.
16 |
17 |
18 |
19 |
20 | ## Example
21 | - The project can be build running the commands `npm install` and `npm run build`.
22 | - Download the potree build from the build folder or add it to your project using NPM.
23 | - Include it alonside the worker folder in your project (can be found on the source folder).
24 | - The build is a ES module, that can be imported to other projects, threejs should be available as a peer dependency.
25 | - Bellow its a fully functional example of how to use this wrapper to load potree point clouds to a three.js project
26 |
27 | ```javascript
28 | const scene = new Scene();
29 | const camera = new PerspectiveCamera(60, 1, 0.1, 10000);
30 |
31 | const canvas = document.getElementById("canvas");
32 |
33 | const renderer = new WebGLRenderer({canvas:canvas});
34 |
35 | const geometry = new BoxGeometry(1, 1, 1);
36 | const material = new MeshBasicMaterial({color: 0x00ff00});
37 | const cube = new Mesh(geometry, material);
38 | scene.add(cube);
39 |
40 | const controls = new OrbitControls(camera, canvas);
41 | camera.position.z = 10;
42 |
43 | const pointClouds = [];
44 |
45 | const baseUrl = "data/test/";
46 | const potree = new Potree();
47 | potree.loadPointCloud("cloud.js", url => `${baseUrl}${url}`,).then(function(pco) {
48 | scene.add(pco);
49 | pointClouds.push(pco);
50 | });
51 |
52 | function loop()
53 | {
54 | potree.updatePointClouds(pointClouds, camera, renderer);
55 |
56 | controls.update();
57 | renderer.render(scene, camera);
58 |
59 | requestAnimationFrame(loop);
60 | };
61 | loop();
62 | ```
63 |
64 | ## Notes
65 | - Since potree-core is meant to be used as library and not as a full software as potree some features are not available.
66 | - EDL shading is not supported by potree core.
67 | - Removed classification and clipping functionality.
68 | - Removed Arena 4D point cloud support.
69 | - Removed Entwine Point Tile file support.
70 | - GUI elements were removed from the library
71 | - PotreeViewer
72 | - Controls, Input, GUI, Tools
73 | - Anotations, Actions, ProfileRequest
74 | - Potree.startQuery, Potree.endQuery and Potree.resolveQueries
75 | - Potree.timerQueries
76 | - Potree.MOUSE, Potree.CameraMode
77 | - PotreeRenderer, RepRenderer, Potree.Renderer
78 | - JQuery, TWEEN and Proj4 dependencies
79 |
80 |
81 | ## Potree Converter
82 | - Use the (Potree Converter)[https://github.com/potree/PotreeConverter/releases] tool to create point cloud data from LAS, ZLAS or BIN point cloud files
83 | - Potree Converter 1.8 creates a multi file structure with each node as an individual file.
84 | - Potree Converter 2.1 creates a single file for all points and separates files for hierarchy index, its faster to create files. Requires a HTTP server configured for file streaming.
85 | - Tool to create hierarquical structure used for point-cloud rendering using potree-core.
86 | - There are two main versions 2.1 witch generates 4 contained files with point data, hierarchy,
87 | - To generate a folder output from a input file run the command `.\PotreeConverter '..\input.laz' -o ../output`
88 |
89 |
90 | ### TXT2LAS
91 | - The potree converter tool only supports las and laz files, so textural file formats such as .pts, .xyz, have to be first converted into a supported format.
92 | - The TXT2LAS tool from the (LASTools)[https://github.com/LAStools/LAStools] repository can be used for this effect.
93 | - To run the tool use the command `.\txt2las64 -i input.pts -ipts -parse xyziRGB -set_scale 0.001 0.001 0.001 -set_version 1.4 -o output.laz`
94 |
95 |
96 | ### To Do
97 | - Supports logarithmic depth buffer (just by enabling it on the threejs renderer), useful for large scale visualization.
98 | - Point clouds are automatically updated, frustum culling is used to avoid unnecessary updates (better update performance for multiple point clouds).
99 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-undef
2 | export default {
3 | languageOptions: {
4 | ecmaVersion: 2020,
5 | sourceType: "module"
6 | },
7 | ignores: ['source/loading2/**'],
8 | rules: {
9 | 'arrow-body-style': ['error', 'always'],
10 | 'arrow-parens': 'error',
11 | 'brace-style': ['error', 'allman', {allowSingleLine: true}],
12 | camelcase: 'off',
13 | indent: ['error', 'tab'],
14 | complexity: 'off',
15 | semi: ['error', 'always'],
16 | 'constructor-super': 'error',
17 | curly: 'error',
18 | eqeqeq: ['error', 'smart'],
19 | 'guard-for-in': 'off',
20 | 'id-blacklist': 'off',
21 | 'id-match': 'off',
22 | // 'sort-imports': ['off', {
23 | // ignoreCase: false,
24 | // ignoreDeclarationSort: false,
25 | // ignoreMemberSort: false,
26 | // memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single']
27 | // }],
28 | // 'import/no-deprecated': 'off',
29 | // 'import/no-unassigned-import': 'off',
30 | // 'import/order': 'error',
31 | // 'jsdoc/check-alignment': 'error',
32 | // 'jsdoc/check-indentation': 'off',
33 | // 'jsdoc/newline-after-description': 'error',
34 | // 'jsdoc/no-types': 'off',
35 | 'max-classes-per-file': 'off',
36 | 'max-len': 'off',
37 | 'lines-between-class-members': 'error',
38 | 'space-before-blocks': ['error', 'always'],
39 | 'no-multi-spaces': 'error',
40 | 'keyword-spacing': ['error', {after: true, before: true}],
41 | 'new-parens': 'error',
42 | 'no-bitwise': 'off',
43 | 'no-caller': 'error',
44 | 'no-cond-assign': 'error',
45 | 'no-console': 'off',
46 | 'no-debugger': 'off',
47 | 'no-duplicate-imports': 'error',
48 | 'no-empty': 'off',
49 | 'no-eval': 'off',
50 | 'no-fallthrough': 'error',
51 | 'no-invalid-this': 'off',
52 | 'no-multiple-empty-lines': 'error',
53 | 'no-new-wrappers': 'error',
54 | 'no-redeclare': 'off',
55 | 'no-shadow': 'off',
56 | 'no-restricted-imports': 'error',
57 | 'no-sequences': 'error',
58 | 'no-sparse-arrays': 'error',
59 | 'no-throw-literal': 'error',
60 | 'no-trailing-spaces': 'off',
61 | 'no-undef-init': 'error',
62 | 'no-underscore-dangle': 'off',
63 | 'no-unsafe-finally': 'error',
64 | 'no-unused-labels': 'error',
65 | 'no-dupe-args': 'error',
66 | 'no-dupe-keys': 'error',
67 | 'no-dupe-else-if': 'error',
68 | 'no-duplicate-case': 'error',
69 | 'no-invalid-regexp': 'error',
70 | 'no-inner-declarations': 'off',
71 | 'no-with': 'error',
72 | 'no-useless-catch': 'error',
73 | 'no-setter-return': 'error',
74 | 'no-unexpected-multiline': 'error',
75 | 'no-unreachable': 'error',
76 | 'no-irregular-whitespace': 'error',
77 | 'no-implicit-coercion': 'error',
78 | 'no-var': 'off',
79 | 'no-extra-parens': 'error',
80 | 'arrow-spacing': 'error',
81 | 'object-shorthand': ['error', 'never'],
82 | 'one-var': ['off', 'never'],
83 | 'prefer-arrow/prefer-arrow-functions': 'off',
84 | 'prefer-const': 'error',
85 | 'prefer-object-spread': 'off',
86 | 'rest-spread-spacing': ['error', 'never'],
87 | 'no-useless-call': 'error',
88 | 'quote-props': 'off',
89 | quotes: ['error', 'single'],
90 | radix: 'off',
91 | 'use-isnan': 'error',
92 | 'valid-typeof': 'error',
93 | 'spaced-comment': [
94 | 'error',
95 | 'always',
96 | {markers: ['/']}
97 | ],
98 | 'capitalized-comments': 'off',
99 | 'eol-last': ['error', 'always'],
100 | 'no-unneeded-ternary': 'error',
101 | 'object-curly-newline': ['error', {multiline: true}],
102 | 'object-property-newline': ['error', {allowAllPropertiesOnSameLine: true}],
103 | 'object-curly-spacing': ['error', 'never'],
104 | 'array-bracket-spacing': ['error', 'never'],
105 | 'space-before-function-paren': ['error', 'never'],
106 | 'computed-property-spacing': ['error', 'never'],
107 | 'comma-spacing': ['error', {'before': false, 'after': true}],
108 | 'key-spacing': ['error', {'beforeColon': false, 'afterColon': true}],
109 | 'comma-dangle': ['error', 'never']
110 | }
111 | };
112 |
--------------------------------------------------------------------------------
/example/data/lion_takanawa/cloud.js:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.7",
3 | "octreeDir": "data",
4 | "boundingBox": {
5 | "lx": -0.748212993144989,
6 | "ly": -2.78040599822998,
7 | "lz": 2.54782128334045,
8 | "ux": 3.89967638254166,
9 | "uy": 1.86748337745667,
10 | "uz": 7.1957106590271
11 | },
12 | "tightBoundingBox": {
13 | "lx": -0.748212993144989,
14 | "ly": -2.78040599822998,
15 | "lz": 2.55100011825562,
16 | "ux": 2.4497377872467,
17 | "uy": 1.48934376239777,
18 | "uz": 7.1957106590271
19 | },
20 | "pointAttributes": [
21 | "POSITION_CARTESIAN",
22 | "COLOR_PACKED",
23 | "NORMAL_SPHEREMAPPED"
24 | ],
25 | "spacing": 0.0750000029802322,
26 | "scale": 0.001,
27 | "hierarchyStepSize": 6
28 | }
29 |
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r.hrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r.hrc
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r0.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r0.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r00.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r00.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r001.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r001.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r003.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r003.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r004.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r004.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r005.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r005.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r007.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r007.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r01.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r01.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r012.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r012.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r013.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r013.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r016.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r016.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r017.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r017.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r02.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r02.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r021.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r021.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r023.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r023.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r03.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r03.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r030.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r030.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r031.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r031.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r032.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r032.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r034.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r034.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r035.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r035.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r036.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r036.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r037.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r037.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r04.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r04.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r041.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r041.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r043.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r043.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r045.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r045.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r047.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r047.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r05.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r05.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r052.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r052.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r053.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r053.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r056.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r056.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r057.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r057.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r06.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r06.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r065.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r065.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r067.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r067.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r07.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r07.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r070.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r070.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r071.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r071.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r072.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r072.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r073.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r073.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r074.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r074.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r075.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r075.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r076.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r076.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r077.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r077.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r1.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r1.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r11.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r11.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r116.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r116.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r117.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r117.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r12.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r12.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r124.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r124.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r125.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r125.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r126.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r126.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r127.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r127.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r13.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r13.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r134.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r134.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r135.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r135.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r136.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r136.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r137.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r137.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r14.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r14.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r146.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r146.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r15.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r15.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r152.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r152.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r153.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r153.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r156.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r156.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r157.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r157.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r16.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r16.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r160.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r160.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r161.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r161.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r162.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r162.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r164.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r164.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r165.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r165.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r166.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r166.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r167.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r167.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r17.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r17.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r170.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r170.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r171.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r171.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r173.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r173.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r174.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r174.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r175.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r175.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r176.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r176.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r177.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r177.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r2.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r2.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r21.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r21.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r210.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r210.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r214.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r214.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r215.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r215.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r23.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r23.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r235.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r235.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r24.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r24.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r243.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r243.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r245.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r245.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r247.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r247.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r25.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r25.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r250.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r250.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r251.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r251.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r254.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r254.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r255.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r255.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r256.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r256.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r257.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r257.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r26.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r26.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r261.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r261.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r265.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r265.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r27.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r27.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r270.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r270.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r271.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r271.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r274.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r274.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r275.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r275.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r3.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r3.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r30.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r30.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r304.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r304.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r306.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r306.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r31.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r31.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r314.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r314.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r315.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r315.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r32.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r32.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r324.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r324.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r34.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r34.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r340.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r340.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r341.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r341.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r342.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r342.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r343.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r343.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r344.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r344.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r345.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r345.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r346.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r346.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r347.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r347.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r35.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r35.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r350.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r350.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r351.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r351.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r354.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r354.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r355.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r355.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r36.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r36.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r360.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r360.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r364.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r364.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r4.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r4.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r40.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r40.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r400.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r400.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r401.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r401.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r402.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r402.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r403.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r403.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r406.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r406.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r41.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r41.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r413.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r413.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r42.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r42.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r420.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r420.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r421.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r421.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r422.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r422.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r423.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r423.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r424.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r424.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r426.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r426.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r43.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r43.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r431.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r431.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r6.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r6.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r60.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r60.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r600.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r600.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r601.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r601.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r602.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r602.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r603.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r603.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r604.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r604.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r606.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r606.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r62.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r62.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r620.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r620.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r621.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r621.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r622.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r622.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r624.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r624.bin
--------------------------------------------------------------------------------
/example/data/lion_takanawa/data/r/r626.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/lion_takanawa/data/r/r626.bin
--------------------------------------------------------------------------------
/example/data/pump/hierarchy.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/pump/hierarchy.bin
--------------------------------------------------------------------------------
/example/data/pump/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "name": "11ee6a64-18e9-48dd-81e2-c83cfa56058a",
4 | "description": "",
5 | "points": 1213990,
6 | "projection": "",
7 | "hierarchy": {
8 | "firstChunkSize": 9702,
9 | "stepSize": 4,
10 | "depth": 6
11 | },
12 | "offset": [-3.0340000000000003, -5.8260000000000005, -1.982],
13 | "scale": [0.001, 0.001, 0.001],
14 | "spacing": 0.039320312500000003,
15 | "boundingBox": {
16 | "min": [-3.0340000000000003, -5.8260000000000005, -1.982],
17 | "max": [1.9990000000000001, -0.79300000000000015, 3.0510000000000002]
18 | },
19 | "encoding": "DEFAULT",
20 | "attributes": [
21 | {
22 | "name": "position",
23 | "description": "",
24 | "size": 12,
25 | "numElements": 3,
26 | "elementSize": 4,
27 | "type": "int32",
28 | "min": [-3.0340000000000003, -5.8260000000000005, -1.982],
29 | "max": [1.73, -0.79300000000000004, 1.8480000000000001],
30 | "scale": [1, 1, 1],
31 | "offset": [0, 0, 0]
32 | },{
33 | "name": "intensity",
34 | "description": "",
35 | "size": 2,
36 | "numElements": 1,
37 | "elementSize": 2,
38 | "type": "uint16",
39 | "min": [25088],
40 | "max": [45568],
41 | "scale": [1],
42 | "offset": [0]
43 | },{
44 | "name": "return number",
45 | "description": "",
46 | "size": 1,
47 | "numElements": 1,
48 | "elementSize": 1,
49 | "type": "uint8",
50 | "min": [1],
51 | "max": [1],
52 | "scale": [1],
53 | "offset": [0]
54 | },{
55 | "name": "number of returns",
56 | "description": "",
57 | "size": 1,
58 | "numElements": 1,
59 | "elementSize": 1,
60 | "type": "uint8",
61 | "min": [1],
62 | "max": [1],
63 | "scale": [1],
64 | "offset": [0]
65 | },{
66 | "name": "classification",
67 | "description": "",
68 | "size": 1,
69 | "numElements": 1,
70 | "elementSize": 1,
71 | "type": "uint8",
72 | "histogram": [1213990, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
73 | "min": [0],
74 | "max": [0],
75 | "scale": [1],
76 | "offset": [0]
77 | },{
78 | "name": "scan angle rank",
79 | "description": "",
80 | "size": 1,
81 | "numElements": 1,
82 | "elementSize": 1,
83 | "type": "uint8",
84 | "min": [0],
85 | "max": [0],
86 | "scale": [1],
87 | "offset": [0]
88 | },{
89 | "name": "user data",
90 | "description": "",
91 | "size": 1,
92 | "numElements": 1,
93 | "elementSize": 1,
94 | "type": "uint8",
95 | "min": [0],
96 | "max": [0],
97 | "scale": [1],
98 | "offset": [0]
99 | },{
100 | "name": "point source id",
101 | "description": "",
102 | "size": 2,
103 | "numElements": 1,
104 | "elementSize": 2,
105 | "type": "uint16",
106 | "min": [1],
107 | "max": [5],
108 | "scale": [1],
109 | "offset": [0]
110 | },{
111 | "name": "rgb",
112 | "description": "",
113 | "size": 6,
114 | "numElements": 3,
115 | "elementSize": 2,
116 | "type": "uint16",
117 | "min": [0, 0, 0],
118 | "max": [65280, 65280, 65280],
119 | "scale": [1, 1, 1],
120 | "offset": [0, 0, 0]
121 | }
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/example/data/pump/octree.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/example/data/pump/octree.bin
--------------------------------------------------------------------------------
/example/main.ts:
--------------------------------------------------------------------------------
1 | import { AmbientLight, BoxGeometry, Euler, Mesh, MeshBasicMaterial, PerspectiveCamera, Raycaster, Scene, SphereGeometry, Vector2, Vector3, WebGLRenderer } from 'three';
2 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
3 | import { PointCloudOctree, Potree } from '../source';
4 |
5 | document.body.onload = function()
6 | {
7 | const potree = new Potree();
8 | let pointClouds: PointCloudOctree[] = [];
9 |
10 | // three.js
11 | const scene = new Scene();
12 | const camera = new PerspectiveCamera(60, 1, 0.1, 1000);
13 |
14 | const canvas = document.createElement('canvas');
15 | canvas.style.position = 'absolute';
16 | canvas.style.top = '0px';
17 | canvas.style.left = '0px';
18 | canvas.style.width = '100%';
19 | canvas.style.height = '100%';
20 | document.body.appendChild(canvas);
21 |
22 | const renderer = new WebGLRenderer(
23 | {
24 | canvas: canvas,
25 | alpha: true,
26 | logarithmicDepthBuffer: false,
27 | precision: 'highp',
28 | premultipliedAlpha: true,
29 | antialias: true,
30 | preserveDrawingBuffer: false,
31 | powerPreference: 'high-performance'
32 | });
33 |
34 | const geometry = new BoxGeometry(25, 1, 25);
35 | const material = new MeshBasicMaterial({color: 0x44AA44});
36 | const cube = new Mesh(geometry, material);
37 | cube.position.y = -2;
38 | scene.add(cube);
39 |
40 | scene.add(new AmbientLight(0xffffff));
41 |
42 | const controls = new OrbitControls(camera, canvas);
43 | camera.position.z = 10;
44 |
45 | const raycaster = new Raycaster();
46 | // @ts-ignore
47 | raycaster.params.Points.threshold = 1e-2;
48 | const normalized = new Vector2();
49 |
50 | canvas.onmousemove = function(event)
51 | {
52 | normalized.set(event.clientX / canvas.width * 2 - 1, -(event.clientY / canvas.height) * 2 + 1);
53 | raycaster.setFromCamera(normalized, camera);
54 | };
55 |
56 | canvas.ondblclick = function()
57 | {
58 | const intesects = raycaster.intersectObject(scene, true);
59 |
60 |
61 | if (intesects.length > 0)
62 | {
63 | const geometry = new SphereGeometry(0.2, 32, 32);
64 | const material = new MeshBasicMaterial({color: Math.random() * 0xAA4444});
65 | const sphere = new Mesh(geometry, material);
66 | sphere.position.copy(intesects[0].point);
67 | scene.add(sphere);
68 | }
69 | };
70 |
71 | loadPointCloud('/data/lion_takanawa/', 'cloud.js', new Vector3(-4, -2, 5), new Euler(-Math.PI / 2, 0, 0));
72 | loadPointCloud('/data/pump/', 'metadata.json', new Vector3(0, -1.5, 3), new Euler(-Math.PI / 2, 0, 0), new Vector3(2, 2, 2));
73 |
74 | function loadPointCloud(baseUrl: string, url: string, position?: Vector3, rotation?: Euler, scale?: Vector3)
75 | {
76 | potree.loadPointCloud(url, url => `${baseUrl}${url}`,).then(function(pco: PointCloudOctree)
77 | {
78 | pco.material.size = 1.0;
79 | pco.material.shape = 2;
80 | pco.material.inputColorEncoding = 1;
81 | pco.material.outputColorEncoding = 1;
82 |
83 | if (position){pco.position.copy(position);}
84 | if (rotation){pco.rotation.copy(rotation);}
85 | if (scale){pco.scale.copy(scale);}
86 |
87 | console.log('Pointcloud file loaded', pco);
88 | pco.showBoundingBox = false;
89 |
90 | const box = pco.pcoGeometry.boundingBox;
91 | const size = box.getSize(new Vector3());
92 |
93 | const geometry = new BoxGeometry(size.x, size.y, size.z);
94 | const material = new MeshBasicMaterial({color:0xFF0000, wireframe: true});
95 | const mesh = new Mesh(geometry, material);
96 | mesh.position.copy(pco.position);
97 | mesh.scale.copy(pco.scale);
98 | mesh.rotation.copy(pco.rotation);
99 | mesh.raycast = () => false;
100 |
101 | size.multiplyScalar(0.5);
102 | mesh.position.add(new Vector3(size.x, size.y, -size.z));
103 |
104 | scene.add(mesh);
105 |
106 | add(pco);
107 | });
108 | }
109 |
110 | function add(pco: PointCloudOctree): void {
111 | scene.add(pco);
112 | pointClouds.push(pco);
113 | }
114 |
115 | function unload(): void {
116 | pointClouds.forEach(pco => {
117 | scene.remove(pco);
118 | pco.dispose();
119 | });
120 |
121 | pointClouds = [];
122 | }
123 |
124 | function loop()
125 | {
126 | cube.rotation.y += 0.01;
127 |
128 | potree.updatePointClouds(pointClouds, camera, renderer);
129 |
130 | controls.update();
131 | renderer.render(scene, camera);
132 |
133 | requestAnimationFrame(loop);
134 | }
135 |
136 | loop();
137 |
138 | document.body.onresize = function()
139 | {
140 | const width = window.innerWidth;
141 | const height = window.innerHeight;
142 |
143 | renderer.setSize(width, height);
144 | camera.aspect = width / height;
145 | camera.updateProjectionMatrix();
146 | };
147 |
148 | // @ts-ignore
149 | document.body.onresize();
150 | };
151 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "sourceMap": true,
6 | "module": "es2015",
7 | "moduleResolution": "node",
8 | "strict": true,
9 | "noImplicitAny": true,
10 | "noImplicitReturns": true,
11 | "noImplicitThis": true,
12 | "noUnusedLocals": false,
13 | "noUnusedParameters": false,
14 | "alwaysStrict": true,
15 | "target": "es6",
16 | "experimentalDecorators": true,
17 | "typeRoots": ["../node_modules/@types"],
18 | "lib": ["es2017", "dom"],
19 | "plugins": []
20 | },
21 | "include": ["**/*.ts"],
22 | "exclude": ["build", "node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "potree-core",
3 | "version": "2.0.10",
4 | "description": "Potree wrapper for three.js applications",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tentone/potree-core.git"
8 | },
9 | "files": [
10 | "dist"
11 | ],
12 | "type": "module",
13 | "main": "dist/index.js",
14 | "scripts": {
15 | "dev": "webpack --mode development --watch --progress --stats-children",
16 | "build": "webpack --mode production --config webpack.prod.js",
17 | "start": "npm run build && webpack-dev-server --config webpack.example.js --mode development --progress --port 5200",
18 | "docs": "jsdoc -d docs source",
19 | "pub": "npm run build && npm publish --access public .",
20 | "lint": "eslint source",
21 | "lint-fix": "eslint --fix source"
22 | },
23 | "keywords": [
24 | "three",
25 | "potree",
26 | "3d",
27 | "webgl"
28 | ],
29 | "author": "Tentone",
30 | "license": "MIT",
31 | "peerDependencies": {
32 | "three": ">0.125.0"
33 | },
34 | "dependencies": {
35 | "brotli": "1.3.3"
36 | },
37 | "devDependencies": {
38 | "@types/node": "20.8.4",
39 | "@types/three": "^0.152.1",
40 | "@typescript-eslint/eslint-plugin": "8.0.1",
41 | "@typescript-eslint/parser": "8.0.1",
42 | "copy-webpack-plugin": "11.0.0",
43 | "css-loader": "6.7.3",
44 | "eslint": "9.9.0",
45 | "eslint-plugin-typescript": "0.14.0",
46 | "html-loader": "4.2.0",
47 | "html-webpack-plugin": "5.5.0",
48 | "raw-loader": "4.0.2",
49 | "style-loader": "3.3.1",
50 | "three": "^0.154.0",
51 | "ts-loader": "9.5.1",
52 | "typescript": "5.5.4",
53 | "webpack": "5.94.0",
54 | "webpack-bundle-analyzer": "4.10.2",
55 | "webpack-cli": "5.1.4",
56 | "webpack-dev-server": "5.0.4",
57 | "worker-loader": "3.0.8"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tentone/potree-core/8edeff8818b1a8ddf7ab2ed503297e33ef283365/screenshot.png
--------------------------------------------------------------------------------
/source/constants.ts:
--------------------------------------------------------------------------------
1 | import {Color, Vector4} from 'three';
2 |
3 | export const DEFAULT_RGB_BRIGHTNESS = 0;
4 | export const DEFAULT_RGB_CONTRAST = 0;
5 | export const DEFAULT_RGB_GAMMA = 1;
6 | export const DEFAULT_MAX_POINT_SIZE = 50;
7 | export const DEFAULT_MIN_NODE_PIXEL_SIZE = 50;
8 | export const DEFAULT_MIN_POINT_SIZE = 2;
9 | export const DEFAULT_PICK_WINDOW_SIZE = 15;
10 | export const DEFAULT_POINT_BUDGET = 1_000_000;
11 | export const MAX_LOADS_TO_GPU = 2;
12 | export const MAX_NUM_NODES_LOADING = 4;
13 | export const PERSPECTIVE_CAMERA = 'PerspectiveCamera';
14 | export const COLOR_BLACK = new Color(0, 0, 0);
15 | export const DEFAULT_HIGHLIGHT_COLOR = new Vector4(1, 0, 0, 1);
16 |
--------------------------------------------------------------------------------
/source/dem-node.ts:
--------------------------------------------------------------------------------
1 | import {Box3, Vector2, Vector3} from 'three';
2 |
3 | export class DEMNode
4 | {
5 | level: number;
6 |
7 | data: Float32Array;
8 |
9 | children: DEMNode[];
10 |
11 | mipMap: Float32Array[];
12 |
13 | mipMapNeedsUpdate: boolean = true;
14 |
15 | constructor(public name: string, public box: Box3, public tileSize: number)
16 | {
17 | this.level = this.name.length - 1;
18 | this.data = new Float32Array(tileSize * tileSize);
19 | this.data.fill(-Infinity);
20 | this.children = [];
21 |
22 | this.mipMap = [this.data];
23 | }
24 |
25 | createMipMap()
26 | {
27 | this.mipMap = [this.data];
28 |
29 | let sourceSize = this.tileSize;
30 | let mipSize = sourceSize / 2;
31 | let mipSource = this.data;
32 | while (mipSize > 1)
33 | {
34 | const mipData = new Float32Array(mipSize * mipSize);
35 |
36 | for (let i = 0; i < mipSize; i++)
37 | {
38 | for (let j = 0; j < mipSize; j++)
39 | {
40 | const h00 = mipSource[2 * i + 0 + 2 * j * sourceSize];
41 | const h01 = mipSource[2 * i + 0 + 2 * j * sourceSize + sourceSize];
42 | const h10 = mipSource[2 * i + 1 + 2 * j * sourceSize];
43 | const h11 = mipSource[2 * i + 1 + 2 * j * sourceSize + sourceSize];
44 |
45 | let [height, weight] = [0, 0];
46 |
47 | if (isFinite(h00))
48 | {
49 | height += h00;
50 | weight += 1;
51 | }
52 | if (isFinite(h01))
53 | {
54 | height += h01;
55 | weight += 1;
56 | }
57 | if (isFinite(h10))
58 | {
59 | height += h10;
60 | weight += 1;
61 | }
62 | if (isFinite(h11))
63 | {
64 | height += h11;
65 | weight += 1;
66 | }
67 |
68 | height = height / weight;
69 |
70 | // let hs = [h00, h01, h10, h11].filter(h => isFinite(h));
71 | // let height = hs.reduce( (a, v, i) => a + v, 0) / hs.length;
72 |
73 | mipData[i + j * mipSize] = height;
74 | }
75 | }
76 |
77 | this.mipMap.push(mipData);
78 |
79 | mipSource = mipData;
80 | sourceSize = mipSize;
81 | mipSize = Math.floor(mipSize / 2);
82 | }
83 |
84 | this.mipMapNeedsUpdate = false;
85 | }
86 |
87 | uv(position: Vector2): [number, number]
88 | {
89 | const boxSize = new Vector3();
90 | this.box.getSize(boxSize);
91 |
92 | const u = (position.x - this.box.min.x) / boxSize.x;
93 | const v = (position.y - this.box.min.y) / boxSize.y;
94 |
95 | return [u, v];
96 | }
97 |
98 | heightAtMipMapLevel(position: Vector2, mipMapLevel: number)
99 | {
100 | const uv = this.uv(position);
101 |
102 | const tileSize = Math.floor(this.tileSize / Math.floor(2 ** mipMapLevel));
103 | const data = this.mipMap[mipMapLevel];
104 |
105 | const i = Math.min(uv[0] * tileSize, tileSize - 1);
106 | const j = Math.min(uv[1] * tileSize, tileSize - 1);
107 |
108 | const a = i % 1;
109 | const b = j % 1;
110 |
111 | const [i0, i1] = [Math.floor(i), Math.ceil(i)];
112 | const [j0, j1] = [Math.floor(j), Math.ceil(j)];
113 |
114 | const h00 = data[i0 + tileSize * j0];
115 | const h01 = data[i0 + tileSize * j1];
116 | const h10 = data[i1 + tileSize * j0];
117 | const h11 = data[i1 + tileSize * j1];
118 |
119 | let wh00 = isFinite(h00) ? (1 - a) * (1 - b) : 0;
120 | let wh01 = isFinite(h01) ? (1 - a) * b : 0;
121 | let wh10 = isFinite(h10) ? a * (1 - b) : 0;
122 | let wh11 = isFinite(h11) ? a * b : 0;
123 |
124 | const wsum = wh00 + wh01 + wh10 + wh11;
125 | wh00 = wh00 / wsum;
126 | wh01 = wh01 / wsum;
127 | wh10 = wh10 / wsum;
128 | wh11 = wh11 / wsum;
129 |
130 | if (wsum === 0)
131 | {
132 | return null;
133 | }
134 |
135 | let h = 0;
136 |
137 | if (isFinite(h00))
138 | {
139 | h += h00 * wh00;
140 | }
141 | if (isFinite(h01))
142 | {
143 | h += h01 * wh01;
144 | }
145 | if (isFinite(h10))
146 | {
147 | h += h10 * wh10;
148 | }
149 | if (isFinite(h11))
150 | {
151 | h += h11 * wh11;
152 | }
153 |
154 | return h;
155 | }
156 |
157 | height(position: Vector2)
158 | {
159 | let h = null;
160 |
161 | for (let i = 0; i < this.mipMap.length; i++)
162 | {
163 | h = this.heightAtMipMapLevel(position, i);
164 |
165 | if (h !== null)
166 | {
167 | return h;
168 | }
169 | }
170 |
171 | return h;
172 | }
173 |
174 | traverse(handler: (node: DEMNode, level: number)=> void, level = 0)
175 | {
176 | handler(this, level);
177 |
178 | this.children.filter((c) => {return c !== undefined;}).forEach((child) => {return child.traverse(handler, level + 1);});
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/source/features.ts:
--------------------------------------------------------------------------------
1 | let context: WebGLRenderingContext | null = null;
2 |
3 | export const getFeatures = () => ({
4 | SHADER_INTERPOLATION: hasExtension('EXT_frag_depth') && hasMinVaryingVectors(8),
5 | SHADER_SPLATS:
6 | hasExtension('EXT_frag_depth') && hasExtension('OES_texture_float') && hasMinVaryingVectors(8),
7 | SHADER_EDL: hasExtension('OES_texture_float') && hasMinVaryingVectors(8),
8 | precision: getPrecision()
9 | });
10 |
11 | function renderer() {
12 | if (context) return context; // cache
13 | if (typeof document === 'undefined') return null; // server side
14 | // create a new context
15 | const canvas = document.createElement('canvas');
16 | context = canvas.getContext('webgl');
17 | return context;
18 | }
19 |
20 | function hasExtension(ext: string)
21 | {
22 | const gl = renderer();
23 | return gl !== null && Boolean(gl.getExtension(ext));
24 | }
25 |
26 | function hasMinVaryingVectors(value: number)
27 | {
28 | const gl = renderer();
29 | return gl !== null && gl.getParameter(gl.MAX_VARYING_VECTORS) >= value;
30 | }
31 |
32 | function getPrecision()
33 | {
34 | const gl = renderer();
35 | if (gl === null)
36 | {
37 | return '';
38 | }
39 |
40 | const vsHighpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT);
41 | const vsMediumpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT);
42 |
43 | const fsHighpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
44 | const fsMediumpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);
45 |
46 | const highpAvailable =
47 | vsHighpFloat && fsHighpFloat && vsHighpFloat.precision > 0 && fsHighpFloat.precision > 0;
48 |
49 | const mediumpAvailable =
50 | vsMediumpFloat &&
51 | fsMediumpFloat &&
52 | vsMediumpFloat.precision > 0 &&
53 | fsMediumpFloat.precision > 0;
54 |
55 | return highpAvailable ? 'highp' : mediumpAvailable ? 'mediump' : 'lowp';
56 | }
57 |
--------------------------------------------------------------------------------
/source/index.ts:
--------------------------------------------------------------------------------
1 | export * from './materials';
2 | export * from './point-attributes';
3 | export * from './point-cloud-octree-geometry-node';
4 | export * from './point-cloud-octree-geometry';
5 | export * from './point-cloud-octree-node';
6 | export * from './point-cloud-octree-picker';
7 | export * from './point-cloud-octree';
8 | export * from './point-cloud-tree';
9 | export * from './potree';
10 | export * from './types';
11 | export * from './version';
12 |
--------------------------------------------------------------------------------
/source/loading/binary-loader.ts:
--------------------------------------------------------------------------------
1 |
2 | import {Box3, BufferAttribute, BufferGeometry, Uint8BufferAttribute, Vector3} from 'three';
3 | import {PointAttributeName, PointAttributeType} from '../point-attributes';
4 | import {PointCloudOctreeGeometryNode} from '../point-cloud-octree-geometry-node';
5 | import {Version} from '../version';
6 | import {GetUrlFn, XhrRequest} from './types';
7 |
8 | const ClassicWorker = require('../workers/binary-decoder.worker.js').default;
9 |
10 | interface AttributeData {
11 | attribute: {
12 | name: PointAttributeName;
13 | type: PointAttributeType;
14 | byteSize: number;
15 | numElements: number;
16 | };
17 | buffer: ArrayBuffer;
18 | }
19 |
20 | interface WorkerResponse {
21 | data: {
22 | attributeBuffers: { [name: string]: AttributeData };
23 | indices: ArrayBuffer;
24 | tightBoundingBox: { min: number[]; max: number[] };
25 | mean: number[];
26 | };
27 | }
28 |
29 | interface BinaryLoaderOptions {
30 | getUrl?: GetUrlFn;
31 | version: string;
32 | boundingBox: Box3;
33 | scale: number;
34 | xhrRequest: XhrRequest;
35 | }
36 |
37 | type Callback = (node: PointCloudOctreeGeometryNode)=> void;
38 |
39 | export class BinaryLoader
40 | {
41 | version: Version;
42 |
43 | boundingBox: Box3;
44 |
45 | scale: number;
46 |
47 | getUrl: GetUrlFn;
48 |
49 | disposed: boolean = false;
50 |
51 | xhrRequest: XhrRequest;
52 |
53 | callbacks: Callback[];
54 |
55 | private workers: Worker[] = [];
56 |
57 | constructor({
58 | getUrl = (s) => {return Promise.resolve(s);},
59 | version,
60 | boundingBox,
61 | scale,
62 | xhrRequest
63 | }: BinaryLoaderOptions)
64 | {
65 | if (typeof version === 'string')
66 | {
67 | this.version = new Version(version);
68 | }
69 | else
70 | {
71 | this.version = version;
72 | }
73 |
74 | this.xhrRequest = xhrRequest;
75 | this.getUrl = getUrl;
76 | this.boundingBox = boundingBox;
77 | this.scale = scale;
78 | this.callbacks = [];
79 | }
80 |
81 | dispose(): void
82 | {
83 | this.workers.forEach((worker) => {return worker.terminate();});
84 | this.workers = [];
85 |
86 | this.disposed = true;
87 | }
88 |
89 | load(node: PointCloudOctreeGeometryNode): Promise
90 | {
91 | if (node.loaded || this.disposed)
92 | {
93 | return Promise.resolve();
94 | }
95 |
96 | return Promise.resolve(this.getUrl(this.getNodeUrl(node)))
97 | .then((url) => {return this.xhrRequest(url, {mode: 'cors'});})
98 | .then((res) => {return res.arrayBuffer();})
99 | .then((buffer) =>
100 | {
101 | return new Promise((resolve) => {return this.parse(node, buffer, resolve);});
102 | });
103 | }
104 |
105 | private getNodeUrl(node: PointCloudOctreeGeometryNode): string
106 | {
107 | let url = node.getUrl();
108 | if (this.version.equalOrHigher('1.4'))
109 | {
110 | url += '.bin';
111 | }
112 |
113 | return url;
114 | }
115 |
116 | private parse(
117 | node: PointCloudOctreeGeometryNode,
118 | buffer: ArrayBuffer,
119 | resolve: ()=> void,
120 | ): void
121 | {
122 | if (this.disposed)
123 | {
124 | resolve();
125 | return;
126 | }
127 |
128 | const worker = this.getWorker();
129 |
130 | const pointAttributes = node.pcoGeometry.pointAttributes;
131 | const numPoints = buffer.byteLength / pointAttributes.byteSize;
132 |
133 | if (this.version.upTo('1.5'))
134 | {
135 | node.numPoints = numPoints;
136 | }
137 |
138 | worker.onmessage = (e: WorkerResponse) =>
139 | {
140 | if (this.disposed)
141 | {
142 | resolve();
143 | return;
144 | }
145 |
146 | const data = e.data;
147 |
148 | const geometry = node.geometry = node.geometry || new BufferGeometry();
149 | geometry.boundingBox = node.boundingBox;
150 |
151 | this.addBufferAttributes(geometry, data.attributeBuffers);
152 | this.addIndices(geometry, data.indices);
153 | this.addNormalAttribute(geometry, numPoints);
154 |
155 | node.mean = new Vector3().fromArray(data.mean);
156 | node.tightBoundingBox = this.getTightBoundingBox(data.tightBoundingBox);
157 | node.loaded = true;
158 | node.loading = false;
159 | node.failed = false;
160 | node.pcoGeometry.numNodesLoading--;
161 | node.pcoGeometry.needsUpdate = true;
162 |
163 | this.releaseWorker(worker);
164 |
165 | this.callbacks.forEach((callback) => {return callback(node);});
166 | resolve();
167 | };
168 |
169 | const message = {
170 | buffer: buffer,
171 | pointAttributes: pointAttributes,
172 | version: this.version.version,
173 | min: node.boundingBox.min.toArray(),
174 | offset: node.pcoGeometry.offset.toArray(),
175 | scale: this.scale,
176 | spacing: node.spacing,
177 | hasChildren: node.hasChildren
178 | };
179 |
180 | worker.postMessage(message, [message.buffer]);
181 | }
182 |
183 | private getWorker(): Worker
184 | {
185 | const worker = this.workers.pop();
186 | if (worker)
187 | {
188 | return worker;
189 | }
190 |
191 | // return new Worker(
192 | // new URL('../workers/binary-decoder.worker.js', import.meta.url),
193 | // { type: 'module' },
194 | // )
195 | return new ClassicWorker();
196 | }
197 |
198 | private releaseWorker(worker: Worker): void
199 | {
200 | this.workers.push(worker);
201 | }
202 |
203 | private getTightBoundingBox({min, max}: { min: number[]; max: number[] }): Box3
204 | {
205 | const box = new Box3(new Vector3().fromArray(min), new Vector3().fromArray(max));
206 | box.max.sub(box.min);
207 | box.min.set(0, 0, 0);
208 |
209 | return box;
210 | }
211 |
212 | private addBufferAttributes(
213 | geometry: BufferGeometry,
214 | buffers: { [name: string]: { buffer: ArrayBuffer } },
215 | ): void
216 | {
217 | Object.keys(buffers).forEach((property) =>
218 | {
219 | const buffer = buffers[property].buffer;
220 |
221 | if (this.isAttribute(property, PointAttributeName.POSITION_CARTESIAN))
222 | {
223 | geometry.setAttribute('position', new BufferAttribute(new Float32Array(buffer), 3));
224 | }
225 | else if (this.isAttribute(property, PointAttributeName.COLOR_PACKED))
226 | {
227 | geometry.setAttribute('color', new BufferAttribute(new Uint8Array(buffer), 3, true));
228 | }
229 | else if (this.isAttribute(property, PointAttributeName.INTENSITY))
230 | {
231 | geometry.setAttribute('intensity', new BufferAttribute(new Float32Array(buffer), 1));
232 | }
233 | else if (this.isAttribute(property, PointAttributeName.CLASSIFICATION))
234 | {
235 | geometry.setAttribute('classification', new BufferAttribute(new Uint8Array(buffer), 1));
236 | }
237 | else if (this.isAttribute(property, PointAttributeName.NORMAL_SPHEREMAPPED))
238 | {
239 | geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
240 | }
241 | else if (this.isAttribute(property, PointAttributeName.NORMAL_OCT16))
242 | {
243 | geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
244 | }
245 | else if (this.isAttribute(property, PointAttributeName.NORMAL))
246 | {
247 | geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
248 | }
249 | });
250 | }
251 |
252 | private addIndices(geometry: BufferGeometry, indices: ArrayBuffer): void
253 | {
254 | const indicesAttribute = new Uint8BufferAttribute(indices, 4);
255 | indicesAttribute.normalized = true;
256 | geometry.setAttribute('indices', indicesAttribute);
257 | }
258 |
259 | private addNormalAttribute(geometry: BufferGeometry, numPoints: number): void
260 | {
261 | if (!geometry.getAttribute('normal'))
262 | {
263 | const buffer = new Float32Array(numPoints * 3);
264 | geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
265 | }
266 | }
267 |
268 | private isAttribute(property: string, name: PointAttributeName): boolean
269 | {
270 | return parseInt(property, 10) === name;
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/source/loading/index.ts:
--------------------------------------------------------------------------------
1 | export * from './binary-loader';
2 | export * from './load-poc';
3 | export * from './types';
4 |
--------------------------------------------------------------------------------
/source/loading/load-poc.ts:
--------------------------------------------------------------------------------
1 | import {Box3, Vector3} from 'three';
2 | import {PointAttributes, PointAttributeStringName} from '../point-attributes';
3 | import {PointCloudOctreeGeometry} from '../point-cloud-octree-geometry';
4 | import {PointCloudOctreeGeometryNode} from '../point-cloud-octree-geometry-node';
5 | import {createChildAABB} from '../utils/bounds';
6 | import {getIndexFromName} from '../utils/utils';
7 | import {Version} from '../version';
8 | import {BinaryLoader} from './binary-loader';
9 | import {GetUrlFn, XhrRequest} from './types';
10 |
11 | interface BoundingBoxData {
12 | lx: number;
13 | ly: number;
14 | lz: number;
15 | ux: number;
16 | uy: number;
17 | uz: number;
18 | }
19 |
20 | interface POCJson {
21 | version: string;
22 | octreeDir: string;
23 | projection: string;
24 | points: number;
25 | boundingBox: BoundingBoxData;
26 | tightBoundingBox?: BoundingBoxData;
27 | pointAttributes: PointAttributeStringName[];
28 | spacing: number;
29 | scale: number;
30 | hierarchyStepSize: number;
31 | hierarchy: [string, number][]; // [name, numPoints][]
32 | }
33 |
34 | /**
35 | *
36 | * @param url
37 | * The url of the point cloud file (usually cloud.js).
38 | * @param getUrl
39 | * Function which receives the relative URL of a point cloud chunk file which is to be loaded
40 | * and shoud return a new url (e.g. signed) in the form of a string or a promise.
41 | * @param xhrRequest An arrow function for a fetch request
42 | * @returns
43 | * An observable which emits once when the first LOD of the point cloud is loaded.
44 | */
45 | export function loadPOC(
46 | url: string,
47 | getUrl: GetUrlFn,
48 | xhrRequest: XhrRequest,
49 | ): Promise
50 | {
51 | return Promise.resolve(getUrl(url)).then((transformedUrl) =>
52 | { // 1. Make a request to the URL
53 | return xhrRequest(transformedUrl, {mode: 'cors'})
54 | .then((res) => {return res.json();})
55 | .then(parse(transformedUrl, getUrl, xhrRequest)); // 2. Parse the response
56 | });
57 | }
58 |
59 | function parse(url: string, getUrl: GetUrlFn, xhrRequest: XhrRequest)
60 | {
61 | return (data: POCJson): Promise =>
62 | { // Note: The response gets passed from loadPOC()
63 | const {offset, boundingBox, tightBoundingBox} = getBoundingBoxes(data);
64 | const loader = new BinaryLoader({
65 | getUrl: getUrl,
66 | version: data.version,
67 | boundingBox: boundingBox,
68 | scale: data.scale,
69 | xhrRequest: xhrRequest
70 | }); // 3. Create a BinaryLoader with the bounding box and scale
71 |
72 | const pco = new PointCloudOctreeGeometry(
73 | loader,
74 | boundingBox,
75 | tightBoundingBox,
76 | offset,
77 | xhrRequest,
78 | ); // 4. Create a PointCloudOctreeGeometry
79 |
80 | // 5. Fill in Geometry with the data from the POCJson
81 | pco.url = url;
82 | pco.octreeDir = data.octreeDir;
83 | pco.needsUpdate = true;
84 | pco.spacing = data.spacing;
85 | pco.hierarchyStepSize = data.hierarchyStepSize;
86 | pco.projection = data.projection;
87 | pco.offset = offset;
88 | pco.pointAttributes = new PointAttributes(data.pointAttributes);
89 |
90 | const nodes: Record = {}; // HMM! Juicy! 6. Create a map of nodes
91 |
92 | const version = new Version(data.version);
93 |
94 | return loadRoot(pco, data, nodes, version).then(() =>
95 | { // 7. Load the root node
96 | if (version.upTo('1.4'))
97 | {
98 | loadRemainingHierarchy(pco, data, nodes);
99 | }
100 |
101 | pco.nodes = nodes;
102 | return pco;
103 | });
104 | };
105 | }
106 |
107 | function getBoundingBoxes(
108 | data: POCJson,
109 | ): { offset: Vector3; boundingBox: Box3; tightBoundingBox: Box3 }
110 | {
111 | const min = new Vector3(data.boundingBox.lx, data.boundingBox.ly, data.boundingBox.lz);
112 | const max = new Vector3(data.boundingBox.ux, data.boundingBox.uy, data.boundingBox.uz);
113 | const boundingBox = new Box3(min, max);
114 | const tightBoundingBox = boundingBox.clone();
115 |
116 | const offset = min.clone();
117 |
118 | if (data.tightBoundingBox)
119 | {
120 | const {lx, ly, lz, ux, uy, uz} = data.tightBoundingBox;
121 | tightBoundingBox.min.set(lx, ly, lz);
122 | tightBoundingBox.max.set(ux, uy, uz);
123 | }
124 |
125 | boundingBox.min.sub(offset);
126 | boundingBox.max.sub(offset);
127 | tightBoundingBox.min.sub(offset);
128 | tightBoundingBox.max.sub(offset);
129 |
130 | return {offset: offset, boundingBox: boundingBox, tightBoundingBox: tightBoundingBox};
131 | }
132 |
133 | function loadRoot(
134 | pco: PointCloudOctreeGeometry,
135 | data: POCJson,
136 | nodes: Record,
137 | version: Version,
138 | ): Promise
139 | {
140 | const name = 'r';
141 |
142 | const root = new PointCloudOctreeGeometryNode(name, pco, pco.boundingBox);
143 | root.hasChildren = true;
144 | root.spacing = pco.spacing; // Fill in root info from the POCJson
145 |
146 | if (version.upTo('1.5'))
147 | {
148 | root.numPoints = data.hierarchy[0][1];
149 | }
150 | else
151 | {
152 | root.numPoints = 0;
153 | }
154 |
155 | pco.root = root;
156 | nodes[name] = root;
157 | return pco.root.load();
158 | }
159 |
160 | function loadRemainingHierarchy(
161 | pco: PointCloudOctreeGeometry,
162 | data: POCJson,
163 | nodes: Record,
164 | ): void
165 | {
166 | for (let i = 1; i < data.hierarchy.length; i++)
167 | {
168 | const [name, numPoints] = data.hierarchy[i];
169 | const {index, parentName, level} = parseName(name);
170 | const parentNode = nodes[parentName];
171 |
172 | const boundingBox = createChildAABB(parentNode.boundingBox, index);
173 | const node = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
174 | node.level = level;
175 | node.numPoints = numPoints;
176 | node.spacing = pco.spacing / Math.pow(2, node.level);
177 |
178 | nodes[name] = node;
179 | parentNode.addChild(node);
180 | }
181 | }
182 |
183 | function parseName(name: string): { index: number; parentName: string; level: number }
184 | {
185 | return {
186 | index: getIndexFromName(name),
187 | parentName: name.substring(0, name.length - 1),
188 | level: name.length - 1
189 | };
190 | }
191 |
--------------------------------------------------------------------------------
/source/loading/types.ts:
--------------------------------------------------------------------------------
1 | export type GetUrlFn = (url: string)=> string | Promise;
2 | export type XhrRequest = (input: RequestInfo, init?: RequestInit)=> Promise;
3 |
--------------------------------------------------------------------------------
/source/loading2/OctreeGeometry.ts:
--------------------------------------------------------------------------------
1 | import {NodeLoader, Metadata} from './OctreeLoader';
2 | import {Box3, Sphere, Vector3} from 'three';
3 | import {PointAttributes} from './PointAttributes';
4 | import {OctreeGeometryNode} from './OctreeGeometryNode';
5 |
6 | export class OctreeGeometry
7 | {
8 | root!: OctreeGeometryNode;
9 |
10 | url: string | null = null;
11 |
12 | pointAttributes: PointAttributes | null = null;
13 |
14 | spacing: number = 0;
15 |
16 | tightBoundingBox: Box3;
17 |
18 | numNodesLoading: number = 0;
19 |
20 | maxNumNodesLoading: number = 3; // I don't understand why this is also a property of IPotree then. Duplicate functionality?
21 |
22 | boundingSphere: Sphere;
23 |
24 | tightBoundingSphere: Sphere;
25 |
26 | offset!: Vector3;
27 |
28 | scale!: [number, number, number];
29 |
30 | disposed: boolean = false;
31 |
32 | projection?: Metadata['projection'];
33 |
34 | constructor(
35 | public loader: NodeLoader,
36 | public boundingBox: Box3, // Need to be get from metadata.json
37 | )
38 | {
39 | this.tightBoundingBox = this.boundingBox.clone();
40 | this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
41 | this.tightBoundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
42 | }
43 |
44 | dispose(): void
45 | {
46 | // this.loader.dispose();
47 | this.root.traverse((node) => {return node.dispose();});
48 | this.disposed = true;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/source/loading2/OctreeGeometryNode.ts:
--------------------------------------------------------------------------------
1 | import {IPointCloudTreeNode} from './../types';
2 | import {Box3, Sphere, BufferGeometry} from 'three';
3 | import {OctreeGeometry} from './OctreeGeometry';
4 |
5 | export class OctreeGeometryNode implements IPointCloudTreeNode
6 | {
7 |
8 | constructor(public name: string, public octreeGeometry: OctreeGeometry, public boundingBox: Box3)
9 | {
10 | this.id = OctreeGeometryNode.IDCount++;
11 | this.index = parseInt(name.charAt(name.length - 1));
12 | this.boundingSphere = boundingBox.getBoundingSphere(new Sphere());
13 | this.numPoints = 0;
14 | this.oneTimeDisposeHandlers = [];
15 | }
16 |
17 | loaded: boolean = false;
18 |
19 | loading: boolean = false;
20 |
21 | parent: OctreeGeometryNode | null = null;
22 |
23 | geometry: BufferGeometry | null = null;
24 |
25 | nodeType?: number;
26 |
27 | byteOffset?: bigint ;
28 |
29 | byteSize?: bigint;
30 |
31 | hierarchyByteOffset?: bigint;
32 |
33 | hierarchyByteSize?: bigint;
34 |
35 | hasChildren: boolean = false;
36 |
37 | spacing!: number;
38 |
39 | density?: number;
40 |
41 | isLeafNode: boolean = true;
42 |
43 | readonly isTreeNode: boolean = false;
44 |
45 | readonly isGeometryNode: boolean = true;
46 |
47 | readonly children: ReadonlyArray = [
48 | null,
49 | null,
50 | null,
51 | null,
52 | null,
53 | null,
54 | null,
55 | null
56 | ];
57 |
58 | // create static IDCount variable
59 | static IDCount = 0;
60 |
61 | id: number;
62 |
63 | index: number;
64 |
65 | boundingSphere: Sphere;
66 |
67 | numPoints: number;
68 |
69 | level!: number;
70 |
71 | oneTimeDisposeHandlers: Function[];
72 |
73 | // isGeometryNode(){
74 | // return true;
75 | // }
76 |
77 | getLevel()
78 | {
79 | return this.level;
80 | }
81 |
82 | // isTreeNode(){
83 | // return false;
84 | // } // Converted to property
85 |
86 | isLoaded()
87 | {
88 | return this.loaded;
89 | }
90 |
91 | getBoundingSphere()
92 | {
93 | return this.boundingSphere;
94 | }
95 |
96 | // getChildren(){
97 | // let children = [];
98 |
99 | // for (let i = 0; i < 8; i++) {
100 | // if (this.children[i]) {
101 | // children.push(this.children[i]);
102 | // }
103 | // }
104 |
105 | // return children;
106 | // }
107 |
108 | getBoundingBox()
109 | {
110 | return this.boundingBox;
111 | }
112 |
113 | load()
114 | {
115 |
116 | if (this.octreeGeometry.numNodesLoading >= this.octreeGeometry.maxNumNodesLoading)
117 | {
118 | return;
119 | }
120 |
121 | if (this.octreeGeometry.loader)
122 | {
123 | this.octreeGeometry.loader.load(this);
124 | }
125 | }
126 |
127 | getNumPoints()
128 | {
129 | return this.numPoints;
130 | }
131 |
132 | dispose(): void
133 | {
134 | if (this.geometry && this.parent != null)
135 | {
136 | this.geometry.dispose();
137 | this.geometry = null;
138 | this.loaded = false;
139 |
140 | // this.dispatchEvent( { type: 'dispose' } );
141 | for (let i = 0; i < this.oneTimeDisposeHandlers.length; i++)
142 | {
143 | let handler = this.oneTimeDisposeHandlers[i];
144 | handler();
145 | }
146 | this.oneTimeDisposeHandlers = [];
147 | }
148 | }
149 |
150 | traverse(cb: (node: OctreeGeometryNode)=> void, includeSelf = true): void
151 | {
152 | const stack: OctreeGeometryNode[] = includeSelf ? [this] : [];
153 |
154 | let current: OctreeGeometryNode | undefined;
155 |
156 | while ((current = stack.pop()) !== undefined)
157 | {
158 | cb(current);
159 |
160 | for (const child of current.children)
161 | {
162 | if (child !== null)
163 | {
164 | stack.push(child);
165 | }
166 | }
167 | }
168 | }
169 |
170 |
171 | }
172 |
173 | OctreeGeometryNode.IDCount = 0;
174 |
--------------------------------------------------------------------------------
/source/loading2/PointAttributes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Some types of possible point attribute data formats
3 | *
4 | * @class
5 | */
6 | const PointAttributeTypes: PointAttributeTypesType = {
7 | DATA_TYPE_DOUBLE: {ordinal: 0, name: 'double', size: 8},
8 | DATA_TYPE_FLOAT: {ordinal: 1, name: 'float', size: 4},
9 | DATA_TYPE_INT8: {ordinal: 2, name: 'int8', size: 1},
10 | DATA_TYPE_UINT8: {ordinal: 3, name: 'uint8', size: 1},
11 | DATA_TYPE_INT16: {ordinal: 4, name: 'int16', size: 2},
12 | DATA_TYPE_UINT16: {ordinal: 5, name: 'uint16', size: 2},
13 | DATA_TYPE_INT32: {ordinal: 6, name: 'int32', size: 4},
14 | DATA_TYPE_UINT32: {ordinal: 7, name: 'uint32', size: 4},
15 | DATA_TYPE_INT64: {ordinal: 8, name: 'int64', size: 8},
16 | DATA_TYPE_UINT64: {ordinal: 9, name: 'uint64', size: 8}
17 | };
18 |
19 | type PointAttributeTypesType = {
20 | [key: string]: PointAttributeTypeType;
21 | };
22 |
23 | type PointAttributeTypeType = {
24 | ordinal: number;
25 | name: string;
26 | size: number;
27 | };
28 |
29 | let i = 0;
30 | for (let obj in PointAttributeTypes)
31 | {
32 | PointAttributeTypes[i] = PointAttributeTypes[obj];
33 | i++;
34 | }
35 |
36 | export {PointAttributeTypes};
37 |
38 | type RangeType = number[] | [number[], number[]]
39 |
40 | // Class that represents a certain point attribute
41 | class PointAttribute
42 | {
43 | byteSize: number;
44 |
45 | description: string;
46 |
47 | public initialRange?: RangeType;
48 |
49 | constructor(
50 | public name: string,
51 | public type: PointAttributeTypeType,
52 | public numElements: number,
53 | public range: RangeType = [Infinity, -Infinity]
54 | )
55 | {
56 | this.byteSize = this.numElements * this.type.size;
57 | this.description = '';
58 | }
59 | }
60 |
61 | export {PointAttribute};
62 |
63 | // Map that represents all point attributes, these were previoiusly properties of the PointAttribute class
64 | export const POINT_ATTRIBUTES: {[key: string]: PointAttribute} = {
65 | POSITION_CARTESIAN: new PointAttribute('POSITION_CARTESIAN', PointAttributeTypes.DATA_TYPE_FLOAT, 3),
66 | RGBA_PACKED: new PointAttribute('COLOR_PACKED', PointAttributeTypes.DATA_TYPE_INT8, 4),
67 | COLOR_PACKED: new PointAttribute('COLOR_PACKED', PointAttributeTypes.DATA_TYPE_INT8, 4),
68 | RGB_PACKED: new PointAttribute('COLOR_PACKED', PointAttributeTypes.DATA_TYPE_INT8, 3),
69 | NORMAL_FLOATS: new PointAttribute('NORMAL_FLOATS', PointAttributeTypes.DATA_TYPE_FLOAT, 3),
70 | INTENSITY: new PointAttribute('INTENSITY', PointAttributeTypes.DATA_TYPE_UINT16, 1),
71 | CLASSIFICATION: new PointAttribute('CLASSIFICATION', PointAttributeTypes.DATA_TYPE_UINT8, 1),
72 | NORMAL_SPHEREMAPPED: new PointAttribute('NORMAL_SPHEREMAPPED', PointAttributeTypes.DATA_TYPE_UINT8, 2),
73 | NORMAL_OCT16: new PointAttribute('NORMAL_OCT16', PointAttributeTypes.DATA_TYPE_UINT8, 2),
74 | NORMAL: new PointAttribute('NORMAL', PointAttributeTypes.DATA_TYPE_FLOAT, 3),
75 | RETURN_NUMBER: new PointAttribute('RETURN_NUMBER', PointAttributeTypes.DATA_TYPE_UINT8, 1),
76 | NUMBER_OF_RETURNS: new PointAttribute('NUMBER_OF_RETURNS', PointAttributeTypes.DATA_TYPE_UINT8, 1),
77 | SOURCE_ID: new PointAttribute('SOURCE_ID', PointAttributeTypes.DATA_TYPE_UINT16, 1),
78 | INDICES: new PointAttribute('INDICES', PointAttributeTypes.DATA_TYPE_UINT32, 1),
79 | SPACING: new PointAttribute('SPACING', PointAttributeTypes.DATA_TYPE_FLOAT, 1),
80 | GPS_TIME: new PointAttribute('GPS_TIME', PointAttributeTypes.DATA_TYPE_DOUBLE, 1)
81 | };
82 |
83 | type PAVectorType = {
84 | name: string;
85 | attributes: string[];
86 | }
87 |
88 | // Instantiated during loading
89 | export class PointAttributes
90 | {
91 |
92 |
93 | // pointAttributes will be a list of strings
94 | constructor(pointAttributes?: string[],
95 | public attributes: PointAttribute[] = [],
96 | public byteSize: number = 0,
97 | public size: number = 0,
98 | public vectors: PAVectorType[]=[]
99 | )
100 | {
101 |
102 | if (pointAttributes != null)
103 | {
104 | for (let i = 0; i < pointAttributes.length; i++)
105 | {
106 | let pointAttributeName = pointAttributes[i];
107 | let pointAttribute = POINT_ATTRIBUTES[pointAttributeName];
108 | this.attributes.push(pointAttribute);
109 | this.byteSize += pointAttribute.byteSize;
110 | this.size++;
111 | }
112 | }
113 | }
114 |
115 | // I hate these argument names that are so similar to each other but have completely different types
116 | add(pointAttribute: PointAttribute)
117 | {
118 | this.attributes.push(pointAttribute);
119 | this.byteSize += pointAttribute.byteSize;
120 | this.size++;
121 | }
122 |
123 | addVector(vector: PAVectorType)
124 | {
125 | this.vectors.push(vector);
126 | }
127 |
128 | hasNormals()
129 | {
130 | for (let name in this.attributes)
131 | {
132 | let pointAttribute = this.attributes[name];
133 | if (
134 | pointAttribute === POINT_ATTRIBUTES.NORMAL_SPHEREMAPPED ||
135 | pointAttribute === POINT_ATTRIBUTES.NORMAL_FLOATS ||
136 | pointAttribute === POINT_ATTRIBUTES.NORMAL ||
137 | pointAttribute === POINT_ATTRIBUTES.NORMAL_OCT16)
138 | {
139 | return true;
140 | }
141 | }
142 |
143 | return false;
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/source/loading2/WorkerPool.ts:
--------------------------------------------------------------------------------
1 | const BrotliDecoderWorker = require('./brotli-decoder.worker.js').default;
2 | const DecoderWorker = require('./decoder.worker.js').default;
3 |
4 | // Create enums for different types of workers
5 | export enum WorkerType {
6 | DECODER_WORKER_BROTLI = 'DECODER_WORKER_BROTLI',
7 | DECODER_WORKER = 'DECODER_WORKER',
8 | }
9 |
10 | // Worker JS names: 'BinaryDecoderWorker.js', 'DEMWorker.js', 'EptBinaryDecoderWorker.js', 'EptLaszipDecoderWorker.js',
11 | // EptZstandardDecoder_preamble.js', 'EptZstandardDecoderWorker.js', 'LASDecoderWorker.js', 'LASLAZWorker.js', 'LazLoaderWorker.js'
12 |
13 | function createWorker(type: WorkerType): Worker
14 | {
15 | // console.log(type)
16 | switch (type)
17 | {
18 | case WorkerType.DECODER_WORKER_BROTLI: {
19 | // const worker = require('./brotli-decoder.worker.js');
20 | // return new worker();
21 | // return new Worker(
22 | // new URL('./brotli-decoder.worker.js', import.meta.url),
23 | // { type: 'module' },
24 | // )
25 | return new BrotliDecoderWorker();
26 | }
27 | case WorkerType.DECODER_WORKER: {
28 | // let ctor = require('./decoder.worker.js');
29 | // return new ctor();
30 | // return new Worker(
31 | // new URL('./decoder.worker.js', import.meta.url),
32 | // { type: 'module' },
33 | // )
34 | return new DecoderWorker();
35 | }
36 | default:
37 | throw new Error('Unknown worker type');
38 | }
39 | }
40 |
41 |
42 | export class WorkerPool
43 | {
44 | // Workers will be an object that has a key for each worker type and the value is an array of Workers that can be empty
45 | private workers: { [key in WorkerType]: Worker[] } = {DECODER_WORKER: [], DECODER_WORKER_BROTLI: []};
46 |
47 | getWorker(workerType: WorkerType): Worker
48 | {
49 | // Throw error if workerType is not recognized
50 | if (this.workers[workerType] === undefined)
51 | {
52 | throw new Error('Unknown worker type');
53 | }
54 | // Given a worker URL, if URL does not exist in the worker object, create a new array with the URL as a key
55 | if (this.workers[workerType].length === 0)
56 | {
57 | let worker = createWorker(workerType);
58 | this.workers[workerType].push(worker);
59 | }
60 | let worker = this.workers[workerType].pop();
61 | if (worker === undefined)
62 | { // Typescript needs this
63 | throw new Error('No workers available');
64 | }
65 | // Return the last worker in the array and remove it from the array
66 | return worker;
67 | }
68 |
69 | returnWorker(workerType: WorkerType, worker: Worker)
70 | {
71 | this.workers[workerType].push(worker);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/source/loading2/decoder.worker.js:
--------------------------------------------------------------------------------
1 | import {PointAttribute, PointAttributeTypes} from './PointAttributes.ts';
2 |
3 | const typedArrayMapping = {
4 | 'int8': Int8Array,
5 | 'int16': Int16Array,
6 | 'int32': Int32Array,
7 | 'int64': Float64Array,
8 | 'uint8': Uint8Array,
9 | 'uint16': Uint16Array,
10 | 'uint32': Uint32Array,
11 | 'uint64': Float64Array,
12 | 'float': Float32Array,
13 | 'double': Float64Array
14 | };
15 |
16 | // Potree = {};
17 |
18 | onmessage = function(event)
19 | {
20 |
21 | const {buffer, pointAttributes, scale, name, min, max, size, offset, numPoints} = event.data;
22 |
23 | const tStart = performance.now();
24 |
25 | const view = new DataView(buffer);
26 |
27 | const attributeBuffers = {};
28 | let attributeOffset = 0;
29 |
30 | let bytesPerPoint = 0;
31 | for (const pointAttribute of pointAttributes.attributes)
32 | {
33 | bytesPerPoint += pointAttribute.byteSize;
34 | }
35 |
36 | const gridSize = 32;
37 | const grid = new Uint32Array(gridSize ** 3);
38 | const toIndex = (x, y, z) =>
39 | {
40 | // let dx = gridSize * (x - min.x) / size.x;
41 | // let dy = gridSize * (y - min.y) / size.y;
42 | // let dz = gridSize * (z - min.z) / size.z;
43 |
44 | // min is already subtracted
45 | const dx = gridSize * x / size.x;
46 | const dy = gridSize * y / size.y;
47 | const dz = gridSize * z / size.z;
48 |
49 | const ix = Math.min(parseInt(dx), gridSize - 1);
50 | const iy = Math.min(parseInt(dy), gridSize - 1);
51 | const iz = Math.min(parseInt(dz), gridSize - 1);
52 |
53 | const index = ix + iy * gridSize + iz * gridSize * gridSize;
54 |
55 | return index;
56 | };
57 |
58 | let numOccupiedCells = 0;
59 | for (const pointAttribute of pointAttributes.attributes)
60 | {
61 |
62 | if (['POSITION_CARTESIAN', 'position'].includes(pointAttribute.name))
63 | {
64 | const buff = new ArrayBuffer(numPoints * 4 * 3);
65 | const positions = new Float32Array(buff);
66 |
67 | for (let j = 0; j < numPoints; j++)
68 | {
69 |
70 | const pointOffset = j * bytesPerPoint;
71 |
72 | const x = view.getInt32(pointOffset + attributeOffset + 0, true) * scale[0] + offset[0] - min.x;
73 | const y = view.getInt32(pointOffset + attributeOffset + 4, true) * scale[1] + offset[1] - min.y;
74 | const z = view.getInt32(pointOffset + attributeOffset + 8, true) * scale[2] + offset[2] - min.z;
75 |
76 | const index = toIndex(x, y, z);
77 | const count = grid[index]++;
78 | if (count === 0)
79 | {
80 | numOccupiedCells++;
81 | }
82 |
83 | positions[3 * j + 0] = x;
84 | positions[3 * j + 1] = y;
85 | positions[3 * j + 2] = z;
86 | }
87 |
88 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
89 | }
90 | else if (['RGBA', 'rgba'].includes(pointAttribute.name))
91 | {
92 | const buff = new ArrayBuffer(numPoints * 4);
93 | const colors = new Uint8Array(buff);
94 |
95 | for (let j = 0; j < numPoints; j++)
96 | {
97 | const pointOffset = j * bytesPerPoint;
98 |
99 | const r = view.getUint16(pointOffset + attributeOffset + 0, true);
100 | const g = view.getUint16(pointOffset + attributeOffset + 2, true);
101 | const b = view.getUint16(pointOffset + attributeOffset + 4, true);
102 |
103 | colors[4 * j + 0] = r > 255 ? r / 256 : r;
104 | colors[4 * j + 1] = g > 255 ? g / 256 : g;
105 | colors[4 * j + 2] = b > 255 ? b / 256 : b;
106 | }
107 |
108 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
109 | }
110 | else
111 | {
112 | const buff = new ArrayBuffer(numPoints * 4);
113 | const f32 = new Float32Array(buff);
114 |
115 | const TypedArray = typedArrayMapping[pointAttribute.type.name];
116 | const preciseBuffer = new TypedArray(numPoints);
117 |
118 | let [offset, scale] = [0, 1];
119 |
120 | const getterMap = {
121 | 'int8': view.getInt8,
122 | 'int16': view.getInt16,
123 | 'int32': view.getInt32,
124 | // 'int64': view.getInt64,
125 | 'uint8': view.getUint8,
126 | 'uint16': view.getUint16,
127 | 'uint32': view.getUint32,
128 | // 'uint64': view.getUint64,
129 | 'float': view.getFloat32,
130 | 'double': view.getFloat64
131 | };
132 | const getter = getterMap[pointAttribute.type.name].bind(view);
133 |
134 | // compute offset and scale to pack larger types into 32 bit floats
135 | if (pointAttribute.type.size > 4)
136 | {
137 | const [amin, amax] = pointAttribute.range;
138 | offset = amin;
139 | scale = 1 / (amax - amin);
140 | }
141 |
142 | for (let j = 0; j < numPoints; j++)
143 | {
144 | const pointOffset = j * bytesPerPoint;
145 | const value = getter(pointOffset + attributeOffset, true);
146 |
147 | f32[j] = (value - offset) * scale;
148 | preciseBuffer[j] = value;
149 | }
150 |
151 | attributeBuffers[pointAttribute.name] = {
152 | buffer: buff,
153 | preciseBuffer: preciseBuffer,
154 | attribute: pointAttribute,
155 | offset: offset,
156 | scale: scale
157 | };
158 | }
159 |
160 | attributeOffset += pointAttribute.byteSize;
161 |
162 |
163 | }
164 |
165 | const occupancy = parseInt(numPoints / numOccupiedCells);
166 | // console.log(`${name}: #points: ${numPoints}: #occupiedCells: ${numOccupiedCells}, occupancy: ${occupancy} points/cell`);
167 |
168 | { // add indices
169 | const buff = new ArrayBuffer(numPoints * 4);
170 | const indices = new Uint32Array(buff);
171 |
172 | for (let i = 0; i < numPoints; i++)
173 | {
174 | indices[i] = i;
175 | }
176 |
177 | attributeBuffers['INDICES'] = {buffer: buff, attribute: PointAttribute.INDICES};
178 | }
179 |
180 |
181 | { // handle attribute vectors
182 | const vectors = pointAttributes.vectors;
183 |
184 | for (const vector of vectors)
185 | {
186 |
187 | const {name, attributes} = vector;
188 | const numVectorElements = attributes.length;
189 | const buffer = new ArrayBuffer(numVectorElements * numPoints * 4);
190 | const f32 = new Float32Array(buffer);
191 |
192 | let iElement = 0;
193 | for (const sourceName of attributes)
194 | {
195 | const sourceBuffer = attributeBuffers[sourceName];
196 | const {offset, scale} = sourceBuffer;
197 | const view = new DataView(sourceBuffer.buffer);
198 |
199 | const getter = view.getFloat32.bind(view);
200 |
201 | for (let j = 0; j < numPoints; j++)
202 | {
203 | const value = getter(j * 4, true);
204 |
205 | f32[j * numVectorElements + iElement] = value / scale + offset;
206 | }
207 |
208 | iElement++;
209 | }
210 |
211 | const vecAttribute = new PointAttribute(name, PointAttributeTypes.DATA_TYPE_FLOAT, 3);
212 |
213 | attributeBuffers[name] = {
214 | buffer: buffer,
215 | attribute: vecAttribute
216 | };
217 |
218 | }
219 |
220 | }
221 |
222 | // let duration = performance.now() - tStart;
223 | // let pointsPerMs = numPoints / duration;
224 | // console.log(`duration: ${duration.toFixed(1)}ms, #points: ${numPoints}, points/ms: ${pointsPerMs.toFixed(1)}`);
225 |
226 | const message = {
227 | buffer: buffer,
228 | attributeBuffers: attributeBuffers,
229 | density: occupancy
230 | };
231 |
232 | const transferables = [];
233 | for (const property in message.attributeBuffers)
234 | {
235 | transferables.push(message.attributeBuffers[property].buffer);
236 | }
237 | transferables.push(buffer);
238 | // console.log('new', message)
239 |
240 | postMessage(message, transferables);
241 | };
242 |
--------------------------------------------------------------------------------
/source/loading2/libs/brotli/BUILD:
--------------------------------------------------------------------------------
1 | package(
2 | default_visibility = ["//visibility:public"],
3 | )
4 |
5 | licenses(["notice"]) # MIT
6 |
7 | load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
8 |
9 | # Not a real polyfill. Do NOT use for anything, but tests.
10 | closure_js_library(
11 | name = "polyfill",
12 | srcs = ["polyfill.js"],
13 | suppress = [
14 | "JSC_INVALID_OPERAND_TYPE",
15 | "JSC_MISSING_JSDOC",
16 | "JSC_STRICT_INEXISTENT_PROPERTY",
17 | "JSC_TYPE_MISMATCH",
18 | "JSC_UNKNOWN_EXPR_TYPE",
19 | ],
20 | )
21 |
22 | # Do NOT use this artifact; it is for test purposes only.
23 | closure_js_library(
24 | name = "decode",
25 | srcs = ["decode.js"],
26 | suppress = [
27 | "JSC_DUP_VAR_DECLARATION",
28 | "JSC_USELESS_BLOCK",
29 | ],
30 | deps = [":polyfill"],
31 | )
32 |
33 | load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_test")
34 |
35 | closure_js_test(
36 | name = "all_tests",
37 | srcs = ["decode_test.js"],
38 | deps = [
39 | ":decode",
40 | ":polyfill",
41 | "@io_bazel_rules_closure//closure/library:testing",
42 | ],
43 | )
44 |
--------------------------------------------------------------------------------
/source/loading2/libs/brotli/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/source/loading2/libs/brotli/WORKSPACE:
--------------------------------------------------------------------------------
1 | workspace(name = "org_brotli_js")
2 |
3 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
4 |
5 | git_repository(
6 | name = "io_bazel_rules_closure",
7 | commit = "29ec97e7c85d607ba9e41cab3993fbb13f812c4b",
8 | remote = "https://github.com/bazelbuild/rules_closure.git",
9 | )
10 |
11 | load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
12 | closure_repositories()
13 |
--------------------------------------------------------------------------------
/source/loading2/libs/brotli/decode_test.js:
--------------------------------------------------------------------------------
1 | goog.require('goog.testing.asserts');
2 | goog.require('goog.testing.jsunit');
3 |
4 | /**
5 | * @param {!Int8Array} bytes
6 | * @return {string}
7 | */
8 | function bytesToString(bytes)
9 | {
10 | return String.fromCharCode.apply(null, new Uint16Array(bytes));
11 | }
12 |
13 | function testMetadata()
14 | {
15 | assertEquals('', bytesToString(BrotliDecode(Int8Array.from([1, 11, 0, 42, 3]))));
16 | }
17 |
18 | function testEmpty()
19 | {
20 | assertEquals('', bytesToString(BrotliDecode(Int8Array.from([6]))));
21 | assertEquals('', bytesToString(BrotliDecode(Int8Array.from([0x81, 1]))));
22 | }
23 |
24 | function testBaseDictWord()
25 | {
26 | var input = Int8Array.from([
27 | 0x1b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe3, 0xb4, 0x0d, 0x00, 0x00,
28 | 0x07, 0x5b, 0x26, 0x31, 0x40, 0x02, 0x00, 0xe0, 0x4e, 0x1b, 0x41, 0x02
29 | ]);
30 | /** @type {!Int8Array} */
31 | var output = BrotliDecode(input);
32 | assertEquals('time', bytesToString(output));
33 | }
34 |
35 | function testBlockCountMessage()
36 | {
37 | var input = Int8Array.from([
38 | 0x1b, 0x0b, 0x00, 0x11, 0x01, 0x8c, 0xc1, 0xc5, 0x0d, 0x08, 0x00, 0x22,
39 | 0x65, 0xe1, 0xfc, 0xfd, 0x22, 0x2c, 0xc4, 0x00, 0x00, 0x38, 0xd8, 0x32,
40 | 0x89, 0x01, 0x12, 0x00, 0x00, 0x77, 0xda, 0x04, 0x10, 0x42, 0x00, 0x00, 0x00
41 | ]);
42 | /** @type {!Int8Array} */
43 | var output = BrotliDecode(input);
44 | assertEquals('aabbaaaaabab', bytesToString(output));
45 | }
46 |
47 | function testCompressedUncompressedShortCompressedSmallWindow()
48 | {
49 | var input = Int8Array.from([
50 | 0x21, 0xf4, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xa7, 0x6d, 0x00, 0x00,
51 | 0x38, 0xd8, 0x32, 0x89, 0x01, 0x12, 0x00, 0x00, 0x77, 0xda, 0x34, 0x7b,
52 | 0xdb, 0x50, 0x80, 0x02, 0x80, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x31,
53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x4e, 0xdb, 0x00, 0x00, 0x70, 0xb0,
54 | 0x65, 0x12, 0x03, 0x24, 0x00, 0x00, 0xee, 0xb4, 0x11, 0x24, 0x00
55 | ]);
56 | /** @type {!Int8Array} */
57 | var output = BrotliDecode(input);
58 | assertEquals(
59 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
60 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
61 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
62 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
63 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
64 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
65 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
66 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
67 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
68 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
69 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
70 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
71 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
72 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +
73 | 'aaaaaaaaaaaaaabbbbbbbbbb', bytesToString(output));
74 | }
75 |
76 | function testIntactDistanceRingBuffer0()
77 | {
78 | var input = Int8Array.from([
79 | 0x1b, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe3, 0xb4, 0x0d, 0x00, 0x00,
80 | 0x07, 0x5b, 0x26, 0x31, 0x40, 0x02, 0x00, 0xe0, 0x4e, 0x1b, 0xa1, 0x80,
81 | 0x20, 0x00
82 | ]);
83 | /** @type {!Int8Array} */
84 | var output = BrotliDecode(input);
85 | assertEquals('himselfself', bytesToString(output));
86 | }
87 |
--------------------------------------------------------------------------------
/source/loading2/libs/brotli/polyfill.js:
--------------------------------------------------------------------------------
1 | if (!Int32Array.__proto__.from)
2 | {
3 | Object.defineProperty(Int32Array.__proto__, 'from', {
4 | value: function(obj)
5 | {
6 | obj = Object(obj);
7 | if (!obj['length'])
8 | {
9 | return new this(0);
10 | }
11 | var typed_array = new this(obj.length);
12 | for (var i = 0; i < typed_array.length; i++)
13 | {
14 | typed_array[i] = obj[i];
15 | }
16 | return typed_array;
17 | }
18 | });
19 | }
20 |
21 | if (!Array.prototype.copyWithin)
22 | {
23 | Array.prototype.copyWithin = function(target, start, end)
24 | {
25 | var O = Object(this);
26 | var len = O.length >>> 0;
27 | var to = target | 0;
28 | var from = start | 0;
29 | var count = Math.min(Math.min(end | 0, len) - from, len - to);
30 | var direction = 1;
31 | if (from < to && to < from + count)
32 | {
33 | direction = -1;
34 | from += count - 1;
35 | to += count - 1;
36 | }
37 | while (count > 0)
38 | {
39 | O[to] = O[from];
40 | from += direction;
41 | to += direction;
42 | count--;
43 | }
44 | return O;
45 | };
46 | }
47 |
48 | if (!Array.prototype.fill)
49 | {
50 | Object.defineProperty(Array.prototype, 'fill', {
51 | value: function(value, start, end)
52 | {
53 | end = end | 0;
54 | var O = Object(this);
55 | var k = start | 0;
56 | while (k < end)
57 | {
58 | O[k] = value;
59 | k++;
60 | }
61 | return O;
62 | }
63 | });
64 | }
65 |
66 | if (!Int8Array.prototype.copyWithin)
67 | {
68 | Int8Array.prototype.copyWithin = Array.prototype.copyWithin;
69 | }
70 |
71 | if (!Int8Array.prototype.fill)
72 | {
73 | Int8Array.prototype.fill = Array.prototype.fill;
74 | }
75 |
76 | if (!Int32Array.prototype.fill)
77 | {
78 | Int32Array.prototype.fill = Array.prototype.fill;
79 | }
80 |
--------------------------------------------------------------------------------
/source/loading2/load-octree.ts:
--------------------------------------------------------------------------------
1 | import {OctreeLoader} from './OctreeLoader';
2 | import {GetUrlFn, XhrRequest} from '../loading/types';
3 |
4 | export async function loadOctree(
5 | url: string,
6 | getUrl: GetUrlFn,
7 | xhrRequest: XhrRequest,
8 | )
9 | {
10 | const trueUrl = await getUrl(url);
11 | const loader = new OctreeLoader();
12 | const {geometry} = await loader.load(trueUrl, xhrRequest);
13 | return geometry;
14 | }
15 |
--------------------------------------------------------------------------------
/source/materials/blur-material.ts:
--------------------------------------------------------------------------------
1 | import {ShaderMaterial, Texture} from 'three';
2 | import {IUniform} from './types';
3 |
4 | // see http://john-chapman-graphics.blogspot.co.at/2013/01/ssao-tutorial.html
5 |
6 | export interface IBlurMaterialUniforms {
7 | [name: string]: IUniform;
8 | screenWidth: IUniform;
9 | screenHeight: IUniform;
10 | map: IUniform;
11 | }
12 |
13 | export class BlurMaterial extends ShaderMaterial
14 | {
15 | // vertexShader = require('./shaders/blur.vert');
16 | // fragmentShader = require('./shaders/blur.frag');
17 | uniforms: IBlurMaterialUniforms = {
18 | screenWidth: {type: 'f', value: 0},
19 | screenHeight: {type: 'f', value: 0},
20 | map: {type: 't', value: null}
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/source/materials/classification.ts:
--------------------------------------------------------------------------------
1 | import {Vector4} from 'three';
2 | import {IClassification} from './types';
3 |
4 | export const DEFAULT_CLASSIFICATION: IClassification = {
5 | 0: new Vector4(0.5, 0.5, 0.5, 1.0),
6 | 1: new Vector4(0.5, 0.5, 0.5, 1.0),
7 | 2: new Vector4(0.63, 0.32, 0.18, 1.0),
8 | 3: new Vector4(0.0, 1.0, 0.0, 1.0),
9 | 4: new Vector4(0.0, 0.8, 0.0, 1.0),
10 | 5: new Vector4(0.0, 0.6, 0.0, 1.0),
11 | 6: new Vector4(1.0, 0.66, 0.0, 1.0),
12 | 7: new Vector4(1.0, 0, 1.0, 1.0),
13 | 8: new Vector4(1.0, 0, 0.0, 1.0),
14 | 9: new Vector4(0.0, 0.0, 1.0, 1.0),
15 | 12: new Vector4(1.0, 1.0, 0.0, 1.0),
16 | DEFAULT: new Vector4(0.3, 0.6, 0.6, 0.5)
17 | };
18 |
--------------------------------------------------------------------------------
/source/materials/clipping.ts:
--------------------------------------------------------------------------------
1 | import {Box3, Matrix4, Vector3} from 'three';
2 |
3 | export enum ClipMode {
4 | DISABLED = 0,
5 | CLIP_OUTSIDE = 1,
6 | CLIP_INSIDE = 2,
7 | HIGHLIGHT_INSIDE = 3,
8 | }
9 |
10 | export interface IClipBox {
11 | box: Box3;
12 | inverse: Matrix4;
13 | matrix: Matrix4;
14 | position: Vector3;
15 | }
16 |
--------------------------------------------------------------------------------
/source/materials/color-encoding.ts:
--------------------------------------------------------------------------------
1 | export enum ColorEncoding {
2 | LINEAR = 0,
3 | SRGB = 1,
4 | }
5 |
--------------------------------------------------------------------------------
/source/materials/enums.ts:
--------------------------------------------------------------------------------
1 | export enum PointSizeType {
2 | FIXED = 0,
3 | ATTENUATED = 1,
4 | ADAPTIVE = 2,
5 | }
6 |
7 | export enum PointShape {
8 | SQUARE = 0,
9 | CIRCLE = 1,
10 | PARABOLOID = 2,
11 | }
12 |
13 | export enum TreeType {
14 | OCTREE = 0,
15 | KDTREE = 1,
16 | }
17 |
18 | export enum PointOpacityType {
19 | FIXED = 0,
20 | ATTENUATED = 1,
21 | }
22 |
23 | export enum PointColorType {
24 | RGB = 0,
25 | COLOR = 1,
26 | DEPTH = 2,
27 | HEIGHT = 3,
28 | ELEVATION = 3,
29 | INTENSITY = 4,
30 | INTENSITY_GRADIENT = 5,
31 | LOD = 6,
32 | LEVEL_OF_DETAIL = 6,
33 | POINT_INDEX = 7,
34 | CLASSIFICATION = 8,
35 | RETURN_NUMBER = 9,
36 | SOURCE = 10,
37 | NORMAL = 11,
38 | PHONG = 12,
39 | RGB_HEIGHT = 13,
40 | COMPOSITE = 50,
41 | }
42 |
--------------------------------------------------------------------------------
/source/materials/gradients/grayscale.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const GRAYSCALE: IGradient = [
5 | [0, new Color(0, 0, 0)],
6 | [1, new Color(1, 1, 1)]
7 | ];
8 |
--------------------------------------------------------------------------------
/source/materials/gradients/index.ts:
--------------------------------------------------------------------------------
1 | export * from './grayscale';
2 | export * from './inferno';
3 | export * from './plasma';
4 | export * from './rainbow';
5 | export * from './spectral';
6 | export * from './vidris';
7 | export * from './yellow-green';
8 |
--------------------------------------------------------------------------------
/source/materials/gradients/inferno.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const INFERNO: IGradient = [
5 | [0.0, new Color(0.077, 0.042, 0.206)],
6 | [0.1, new Color(0.225, 0.036, 0.388)],
7 | [0.2, new Color(0.373, 0.074, 0.432)],
8 | [0.3, new Color(0.522, 0.128, 0.42)],
9 | [0.4, new Color(0.665, 0.182, 0.37)],
10 | [0.5, new Color(0.797, 0.255, 0.287)],
11 | [0.6, new Color(0.902, 0.364, 0.184)],
12 | [0.7, new Color(0.969, 0.516, 0.063)],
13 | [0.8, new Color(0.988, 0.683, 0.072)],
14 | [0.9, new Color(0.961, 0.859, 0.298)],
15 | [1.0, new Color(0.988, 0.998, 0.645)]
16 | ];
17 |
--------------------------------------------------------------------------------
/source/materials/gradients/plasma.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const PLASMA: IGradient = [
5 | [0.0, new Color(0.241, 0.015, 0.61)],
6 | [0.1, new Color(0.387, 0.001, 0.654)],
7 | [0.2, new Color(0.524, 0.025, 0.653)],
8 | [0.3, new Color(0.651, 0.125, 0.596)],
9 | [0.4, new Color(0.752, 0.227, 0.513)],
10 | [0.5, new Color(0.837, 0.329, 0.431)],
11 | [0.6, new Color(0.907, 0.435, 0.353)],
12 | [0.7, new Color(0.963, 0.554, 0.272)],
13 | [0.8, new Color(0.992, 0.681, 0.195)],
14 | [0.9, new Color(0.987, 0.822, 0.144)],
15 | [1.0, new Color(0.94, 0.975, 0.131)]
16 | ];
17 |
--------------------------------------------------------------------------------
/source/materials/gradients/rainbow.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const RAINBOW: IGradient = [
5 | [0, new Color(0.278, 0, 0.714)],
6 | [1 / 6, new Color(0, 0, 1)],
7 | [2 / 6, new Color(0, 1, 1)],
8 | [3 / 6, new Color(0, 1, 0)],
9 | [4 / 6, new Color(1, 1, 0)],
10 | [5 / 6, new Color(1, 0.64, 0)],
11 | [1, new Color(1, 0, 0)]
12 | ];
13 |
--------------------------------------------------------------------------------
/source/materials/gradients/spectral.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | // From chroma spectral http://gka.github.io/chroma.js/
5 | export const SPECTRAL: IGradient = [
6 | [0, new Color(0.3686, 0.3098, 0.6353)],
7 | [0.1, new Color(0.1961, 0.5333, 0.7412)],
8 | [0.2, new Color(0.4, 0.7608, 0.6471)],
9 | [0.3, new Color(0.6706, 0.8667, 0.6431)],
10 | [0.4, new Color(0.902, 0.9608, 0.5961)],
11 | [0.5, new Color(1.0, 1.0, 0.749)],
12 | [0.6, new Color(0.9961, 0.8784, 0.5451)],
13 | [0.7, new Color(0.9922, 0.6824, 0.3804)],
14 | [0.8, new Color(0.9569, 0.4275, 0.2627)],
15 | [0.9, new Color(0.8353, 0.2431, 0.3098)],
16 | [1, new Color(0.6196, 0.0039, 0.2588)]
17 | ];
18 |
--------------------------------------------------------------------------------
/source/materials/gradients/vidris.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const VIRIDIS: IGradient = [
5 | [0.0, new Color(0.267, 0.005, 0.329)],
6 | [0.1, new Color(0.283, 0.141, 0.458)],
7 | [0.2, new Color(0.254, 0.265, 0.53)],
8 | [0.3, new Color(0.207, 0.372, 0.553)],
9 | [0.4, new Color(0.164, 0.471, 0.558)],
10 | [0.5, new Color(0.128, 0.567, 0.551)],
11 | [0.6, new Color(0.135, 0.659, 0.518)],
12 | [0.7, new Color(0.267, 0.749, 0.441)],
13 | [0.8, new Color(0.478, 0.821, 0.318)],
14 | [0.9, new Color(0.741, 0.873, 0.15)],
15 | [1.0, new Color(0.993, 0.906, 0.144)]
16 | ];
17 |
--------------------------------------------------------------------------------
/source/materials/gradients/yellow-green.ts:
--------------------------------------------------------------------------------
1 | import {Color} from 'three';
2 | import {IGradient} from '../types';
3 |
4 | export const YELLOW_GREEN: IGradient = [
5 | [0, new Color(0.1647, 0.2824, 0.3451)],
6 | [0.1, new Color(0.1338, 0.3555, 0.4227)],
7 | [0.2, new Color(0.061, 0.4319, 0.4864)],
8 | [0.3, new Color(0.0, 0.5099, 0.5319)],
9 | [0.4, new Color(0.0, 0.5881, 0.5569)],
10 | [0.5, new Color(0.137, 0.665, 0.5614)],
11 | [0.6, new Color(0.2906, 0.7395, 0.5477)],
12 | [0.7, new Color(0.4453, 0.8099, 0.5201)],
13 | [0.8, new Color(0.6102, 0.8748, 0.485)],
14 | [0.9, new Color(0.7883, 0.9323, 0.4514)],
15 | [1, new Color(0.9804, 0.9804, 0.4314)]
16 | ];
17 |
--------------------------------------------------------------------------------
/source/materials/index.ts:
--------------------------------------------------------------------------------
1 | export * from './blur-material';
2 | export * from './clipping';
3 | export * from './enums';
4 | export * from './point-cloud-material';
5 | export * from './texture-generation';
6 | export * from './types';
7 | export * from './gradients';
8 |
--------------------------------------------------------------------------------
/source/materials/shaders/blur.fs:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | precision highp int;
3 |
4 | uniform mat4 projectionMatrix;
5 |
6 | uniform float screenWidth;
7 | uniform float screenHeight;
8 |
9 | uniform sampler2D map;
10 |
11 | varying vec2 vUv;
12 |
13 | void main() {
14 |
15 | float dx = 1.0 / screenWidth;
16 | float dy = 1.0 / screenHeight;
17 |
18 | vec3 color = vec3(0.0, 0.0, 0.0);
19 | color += texture2D(map, vUv + vec2(-dx, -dy)).rgb;
20 | color += texture2D(map, vUv + vec2( 0, -dy)).rgb;
21 | color += texture2D(map, vUv + vec2(+dx, -dy)).rgb;
22 | color += texture2D(map, vUv + vec2(-dx, 0)).rgb;
23 | color += texture2D(map, vUv + vec2( 0, 0)).rgb;
24 | color += texture2D(map, vUv + vec2(+dx, 0)).rgb;
25 | color += texture2D(map, vUv + vec2(-dx, dy)).rgb;
26 | color += texture2D(map, vUv + vec2( 0, dy)).rgb;
27 | color += texture2D(map, vUv + vec2(+dx, dy)).rgb;
28 |
29 | color = color / 9.0;
30 |
31 | gl_FragColor = vec4(color, 1.0);
32 |
33 |
34 | }
--------------------------------------------------------------------------------
/source/materials/shaders/blur.vs:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | precision highp int;
3 |
4 | attribute vec3 position;
5 | attribute vec2 uv;
6 |
7 | uniform mat4 modelViewMatrix;
8 | uniform mat4 projectionMatrix;
9 |
10 | varying vec2 vUv;
11 |
12 | void main() {
13 | vUv = uv;
14 |
15 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
16 | }
--------------------------------------------------------------------------------
/source/materials/shaders/edl.fs:
--------------------------------------------------------------------------------
1 | // adapted from the EDL shader code from Christian Boucheny in cloud compare:
2 | // https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL
3 |
4 | uniform float screenWidth;
5 | uniform float screenHeight;
6 | uniform vec2 neighbours[NEIGHBOUR_COUNT];
7 | uniform float edlStrength;
8 | uniform float radius;
9 | uniform float opacity;
10 |
11 | uniform sampler2D colorMap;
12 |
13 | varying vec2 vUv;
14 |
15 | float response(float depth){
16 | vec2 uvRadius = radius / vec2(screenWidth, screenHeight);
17 |
18 | float sum = 0.0;
19 |
20 | for(int i = 0; i < NEIGHBOUR_COUNT; i++){
21 | vec2 uvNeighbor = vUv + uvRadius * neighbours[i];
22 |
23 | float neighbourDepth = texture2D(colorMap, uvNeighbor).a;
24 |
25 | if(neighbourDepth != 0.0){
26 | if(depth == 0.0){
27 | sum += 100.0;
28 | }else{
29 | sum += max(0.0, depth - neighbourDepth);
30 | }
31 | }
32 | }
33 |
34 | return sum / float(NEIGHBOUR_COUNT);
35 | }
36 |
37 | void main(){
38 | vec4 color = texture2D(colorMap, vUv);
39 |
40 | float depth = color.a;
41 | float res = response(depth);
42 | float shade = exp(-res * 300.0 * edlStrength);
43 |
44 | if(color.a == 0.0 && res == 0.0){
45 | discard;
46 | }else{
47 | gl_FragColor = vec4(color.rgb * shade, opacity);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/source/materials/shaders/edl.vs:
--------------------------------------------------------------------------------
1 |
2 |
3 | varying vec2 vUv;
4 |
5 | void main() {
6 | vUv = uv;
7 |
8 | vec4 mvPosition = modelViewMatrix * vec4(position,1.0);
9 |
10 | gl_Position = projectionMatrix * mvPosition;
11 | }
--------------------------------------------------------------------------------
/source/materials/shaders/normalize.fs:
--------------------------------------------------------------------------------
1 |
2 | #extension GL_EXT_frag_depth : enable
3 |
4 | uniform sampler2D depthMap;
5 | uniform sampler2D texture;
6 |
7 | varying vec2 vUv;
8 |
9 | void main() {
10 | float depth = texture2D(depthMap, vUv).g;
11 |
12 | if(depth <= 0.0){
13 | discard;
14 | }
15 |
16 | vec4 color = texture2D(texture, vUv);
17 | color = color / color.w;
18 |
19 | gl_FragColor = vec4(color.xyz, 1.0);
20 |
21 | gl_FragDepthEXT = depth;
22 | }
--------------------------------------------------------------------------------
/source/materials/shaders/normalize.vs:
--------------------------------------------------------------------------------
1 |
2 | varying vec2 vUv;
3 |
4 | void main() {
5 | vUv = uv;
6 |
7 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
8 | }
--------------------------------------------------------------------------------
/source/materials/shaders/pointcloud.fs:
--------------------------------------------------------------------------------
1 | precision highp float;
2 | precision highp int;
3 |
4 | uniform mat4 viewMatrix;
5 | uniform vec3 cameraPosition;
6 |
7 | uniform mat4 projectionMatrix;
8 | uniform float opacity;
9 |
10 | uniform bool useOrthographicCamera;
11 | uniform float blendHardness;
12 | uniform float blendDepthSupplement;
13 | uniform float fov;
14 | uniform float spacing;
15 | uniform float pcIndex;
16 | uniform float screenWidth;
17 | uniform float screenHeight;
18 | uniform float far;
19 |
20 | uniform sampler2D depthMap;
21 |
22 | out vec4 fragColor;
23 |
24 | #ifdef highlight_point
25 | uniform vec4 highlightedPointColor;
26 | #endif
27 |
28 | #ifdef new_format
29 | in vec4 vColor;
30 | #else
31 | in vec3 vColor;
32 | #endif
33 |
34 | #if !defined(color_type_point_index)
35 | in float vOpacity;
36 | #endif
37 |
38 | #if defined(weighted_splats)
39 | in float vLinearDepth;
40 | #endif
41 |
42 | #ifdef use_edl
43 | in float vLogDepth;
44 | #endif
45 |
46 | in vec3 vViewPosition;
47 |
48 | #if defined(weighted_splats) || defined(paraboloid_point_shape)
49 | in float vRadius;
50 | #endif
51 |
52 | #if defined(color_type_phong) && (MAX_POINT_LIGHTS > 0 || MAX_DIR_LIGHTS > 0)
53 | in vec3 vNormal;
54 | #endif
55 |
56 | #ifdef highlight_point
57 | in float vHighlight;
58 | #endif
59 |
60 | float specularStrength = 1.0;
61 |
62 | void main() {
63 |
64 | #ifdef new_format
65 | // set actualColor vec3 from vec4 vColor
66 | vec3 actualColor = vColor.xyz;
67 | #else
68 | // set actualColor RGB from the XYZ of vColor
69 | vec3 actualColor = vColor;
70 | #endif
71 |
72 | vec3 color = actualColor;
73 | float depth = gl_FragCoord.z;
74 |
75 | #if defined(circle_point_shape) || defined(paraboloid_point_shape) || defined (weighted_splats)
76 | float u = 2.0 * gl_PointCoord.x - 1.0;
77 | float v = 2.0 * gl_PointCoord.y - 1.0;
78 | #endif
79 |
80 | #if defined(circle_point_shape) || defined (weighted_splats)
81 | float cc = u*u + v*v;
82 | if(cc > 1.0){
83 | discard;
84 | }
85 | #endif
86 |
87 | #if defined weighted_splats
88 | vec2 uv = gl_FragCoord.xy / vec2(screenWidth, screenHeight);
89 | float sDepth = texture2D(depthMap, uv).r;
90 | if(vLinearDepth > sDepth + vRadius + blendDepthSupplement){
91 | discard;
92 | }
93 | #endif
94 |
95 | #if defined(color_type_phong)
96 | #if MAX_POINT_LIGHTS > 0 || MAX_DIR_LIGHTS > 0
97 | vec3 normal = normalize( vNormal );
98 | normal.z = abs(normal.z);
99 |
100 | vec3 viewPosition = normalize( vViewPosition );
101 | #endif
102 |
103 | // code taken from three.js phong light fragment shader
104 |
105 | #if MAX_POINT_LIGHTS > 0
106 |
107 | vec3 pointDiffuse = vec3( 0.0 );
108 | vec3 pointSpecular = vec3( 0.0 );
109 |
110 | for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {
111 |
112 | vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );
113 | vec3 lVector = lPosition.xyz + vViewPosition.xyz;
114 |
115 | float lDistance = 1.0;
116 | if ( pointLightDistance[ i ] > 0.0 )
117 | lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );
118 |
119 | lVector = normalize( lVector );
120 |
121 | // diffuse
122 |
123 | float dotProduct = dot( normal, lVector );
124 |
125 | #ifdef WRAP_AROUND
126 |
127 | float pointDiffuseWeightFull = max( dotProduct, 0.0 );
128 | float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );
129 |
130 | vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );
131 |
132 | #else
133 |
134 | float pointDiffuseWeight = max( dotProduct, 0.0 );
135 |
136 | #endif
137 |
138 | pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;
139 |
140 | // specular
141 |
142 | vec3 pointHalfVector = normalize( lVector + viewPosition );
143 | float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );
144 | float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );
145 |
146 | float specularNormalization = ( shininess + 2.0 ) / 8.0;
147 |
148 | vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );
149 | pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;
150 | pointSpecular = vec3(0.0, 0.0, 0.0);
151 | }
152 |
153 | #endif
154 |
155 | #if MAX_DIR_LIGHTS > 0
156 |
157 | vec3 dirDiffuse = vec3( 0.0 );
158 | vec3 dirSpecular = vec3( 0.0 );
159 |
160 | for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {
161 |
162 | vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );
163 | vec3 dirVector = normalize( lDirection.xyz );
164 |
165 | // diffuse
166 |
167 | float dotProduct = dot( normal, dirVector );
168 |
169 | #ifdef WRAP_AROUND
170 |
171 | float dirDiffuseWeightFull = max( dotProduct, 0.0 );
172 | float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );
173 |
174 | vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );
175 |
176 | #else
177 |
178 | float dirDiffuseWeight = max( dotProduct, 0.0 );
179 |
180 | #endif
181 |
182 | dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;
183 |
184 | // specular
185 |
186 | vec3 dirHalfVector = normalize( dirVector + viewPosition );
187 | float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );
188 | float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );
189 |
190 | float specularNormalization = ( shininess + 2.0 ) / 8.0;
191 |
192 | vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );
193 | dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;
194 | }
195 |
196 | #endif
197 |
198 | vec3 totalDiffuse = vec3( 0.0 );
199 | vec3 totalSpecular = vec3( 0.0 );
200 |
201 | #if MAX_POINT_LIGHTS > 0
202 |
203 | totalDiffuse += pointDiffuse;
204 | totalSpecular += pointSpecular;
205 |
206 | #endif
207 |
208 | #if MAX_DIR_LIGHTS > 0
209 |
210 | totalDiffuse += dirDiffuse;
211 | totalSpecular += dirSpecular;
212 |
213 | #endif
214 |
215 | fragColor.xyz = fragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;
216 |
217 | #endif
218 |
219 | #if defined weighted_splats
220 | float wx = 2.0 * length(2.0 * gl_PointCoord - 1.0);
221 | float w = exp(-wx * wx * 0.5);
222 | fragColor.rgb = fragColor.rgb * w;
223 | fragColor.a = w;
224 | #else
225 | #if defined(color_type_point_index)
226 | fragColor = vec4(color, pcIndex / 255.0);
227 | #else
228 | fragColor = vec4(color, vOpacity);
229 | #endif
230 | #endif
231 |
232 | // Adjust position and compute depth
233 | vec4 pos = vec4(vViewPosition, 1.0);
234 |
235 | #if defined(paraboloid_point_shape)
236 | if(!useOrthographicCamera){
237 | float wi = 0.0 - ( u*u + v*v);
238 | pos.z += wi * vRadius;
239 | }
240 | #endif
241 |
242 | float linearDepth = -pos.z;
243 | vec4 clipPos = projectionMatrix * pos;
244 | clipPos = clipPos / clipPos.w;
245 | float expDepth = clipPos.z;
246 |
247 | #if defined(use_log_depth)
248 | gl_FragDepth = log2( linearDepth + 1.0 ) * log(2.0) / log(far + 1.0 );
249 | #else
250 | gl_FragDepth = (clipPos.z + 1.0) / 2.0;
251 | #endif
252 |
253 | #if defined(color_type_depth)
254 | fragColor.r = linearDepth;
255 | fragColor.g = expDepth;
256 | #endif
257 |
258 | #if defined(use_edl)
259 | fragColor.a = vLogDepth;
260 | #endif
261 |
262 |
263 |
264 | #if defined(highlight_point)
265 | if (vHighlight > 0.0) {
266 | fragColor = highlightedPointColor;
267 | }
268 | #endif
269 | }
270 |
--------------------------------------------------------------------------------
/source/materials/texture-generation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CanvasTexture,
3 | Color,
4 | DataTexture,
5 | LinearFilter,
6 | NearestFilter,
7 | RGBAFormat,
8 | Texture
9 | } from 'three';
10 | import {IClassification, IGradient} from '../materials/types';
11 | import {isBrowser} from '../utils/utils';
12 |
13 | /**
14 | * Generates a texture from a color.
15 | *
16 | * @param width - The width of the texture.
17 | * @param height - The height of the texture.
18 | * @param color - The color of the texture.
19 | * @returns The generated texture.
20 | */
21 | export function generateDataTexture(width: number, height: number, color: Color): Texture
22 | {
23 | const size = width * height;
24 | const data = new Uint8Array(4 * size);
25 |
26 | const r = Math.floor(color.r * 255);
27 | const g = Math.floor(color.g * 255);
28 | const b = Math.floor(color.b * 255);
29 |
30 | for (let i = 0; i < size; i++)
31 | {
32 | data[i * 3] = r;
33 | data[i * 3 + 1] = g;
34 | data[i * 3 + 2] = b;
35 | }
36 |
37 | const texture = new DataTexture(data, width, height, RGBAFormat);
38 | texture.needsUpdate = true;
39 | texture.magFilter = NearestFilter;
40 |
41 | return texture;
42 | }
43 |
44 | /**
45 | * Generates a texture from a gradient.
46 | *
47 | * @param gradient - The gradient to generate the texture from.
48 | * @returns The generated texture.
49 | */
50 | export function generateGradientTexture(gradient: IGradient): Texture
51 | {
52 | if (!isBrowser()) {
53 | return new Texture();
54 | }
55 |
56 | const size = 64;
57 |
58 | const canvas = document.createElement('canvas');
59 | canvas.width = size;
60 | canvas.height = size;
61 |
62 | const context = canvas.getContext('2d')!;
63 |
64 | context.rect(0, 0, size, size);
65 | const ctxGradient = context.createLinearGradient(0, 0, size, size);
66 |
67 | for (let i = 0; i < gradient.length; i++)
68 | {
69 | const step = gradient[i];
70 | ctxGradient.addColorStop(step[0], `#${step[1].getHexString()}`);
71 | }
72 |
73 | context.fillStyle = ctxGradient;
74 | context.fill();
75 |
76 | const texture = new CanvasTexture(canvas);
77 | texture.needsUpdate = true;
78 |
79 | texture.minFilter = LinearFilter;
80 | // textureImage = texture.image;
81 |
82 | return texture;
83 | }
84 |
85 | export function generateClassificationTexture(classification: IClassification): Texture
86 | {
87 | const width = 256;
88 | const height = 256;
89 | const size = width * height;
90 |
91 | const data = new Uint8Array(4 * size);
92 |
93 | for (let x = 0; x < width; x++)
94 | {
95 | for (let y = 0; y < height; y++)
96 | {
97 | const i = x + width * y;
98 |
99 | let color;
100 | if (classification[x])
101 | {
102 | color = classification[x];
103 | }
104 | else if (classification[x % 32])
105 | {
106 | color = classification[x % 32];
107 | }
108 | else
109 | {
110 | color = classification.DEFAULT;
111 | }
112 |
113 | data[4 * i + 0] = 255 * color.x;
114 | data[4 * i + 1] = 255 * color.y;
115 | data[4 * i + 2] = 255 * color.z;
116 | data[4 * i + 3] = 255 * color.w;
117 | }
118 | }
119 |
120 | const texture = new DataTexture(data, width, height, RGBAFormat);
121 | texture.magFilter = NearestFilter;
122 | texture.needsUpdate = true;
123 |
124 | return texture;
125 | }
126 |
--------------------------------------------------------------------------------
/source/materials/types.ts:
--------------------------------------------------------------------------------
1 | import {Color, IUniform as IThreeUniform, Vector4} from 'three';
2 |
3 | export type IGradient = [number, Color][];
4 |
5 | export interface IClassification {
6 | [value: string]: Vector4;
7 | DEFAULT: Vector4;
8 | }
9 |
10 | export interface IUniform extends IThreeUniform {
11 | type: string;
12 | value: T;
13 | }
14 |
--------------------------------------------------------------------------------
/source/point-attributes.ts:
--------------------------------------------------------------------------------
1 |
2 | export enum PointAttributeName {
3 | POSITION_CARTESIAN = 0, // float x, y, z;
4 | COLOR_PACKED = 1, // byte r, g, b, a; I = [0,1]
5 | COLOR_FLOATS_1 = 2, // float r, g, b; I = [0,1]
6 | COLOR_FLOATS_255 = 3, // float r, g, b; I = [0,255]
7 | NORMAL_FLOATS = 4, // float x, y, z;
8 | FILLER = 5,
9 | INTENSITY = 6,
10 | CLASSIFICATION = 7,
11 | NORMAL_SPHEREMAPPED = 8,
12 | NORMAL_OCT16 = 9,
13 | NORMAL = 10,
14 | }
15 |
16 | export interface PointAttributeType {
17 | ordinal: number;
18 | size: number;
19 | }
20 |
21 | export const POINT_ATTRIBUTE_TYPES: Record = {
22 | DATA_TYPE_DOUBLE: {ordinal: 0, size: 8},
23 | DATA_TYPE_FLOAT: {ordinal: 1, size: 4},
24 | DATA_TYPE_INT8: {ordinal: 2, size: 1},
25 | DATA_TYPE_UINT8: {ordinal: 3, size: 1},
26 | DATA_TYPE_INT16: {ordinal: 4, size: 2},
27 | DATA_TYPE_UINT16: {ordinal: 5, size: 2},
28 | DATA_TYPE_INT32: {ordinal: 6, size: 4},
29 | DATA_TYPE_UINT32: {ordinal: 7, size: 4},
30 | DATA_TYPE_INT64: {ordinal: 8, size: 8},
31 | DATA_TYPE_UINT64: {ordinal: 9, size: 8}
32 | };
33 |
34 | export interface IPointAttribute {
35 | name: PointAttributeName;
36 | type: PointAttributeType;
37 | numElements: number;
38 | byteSize: number;
39 | }
40 |
41 | export interface IPointAttributes {
42 | attributes: IPointAttribute[];
43 | byteSize: number;
44 | size: number;
45 | }
46 |
47 | function makePointAttribute(
48 | name: PointAttributeName,
49 | type: PointAttributeType,
50 | numElements: number,
51 | ): IPointAttribute
52 | {
53 | return {
54 | name: name,
55 | type: type,
56 | numElements: numElements,
57 | byteSize: numElements * type.size
58 | };
59 | }
60 |
61 | const RGBA_PACKED = makePointAttribute(
62 | PointAttributeName.COLOR_PACKED,
63 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_INT8,
64 | 4,
65 | );
66 |
67 | export const POINT_ATTRIBUTES = {
68 | POSITION_CARTESIAN: makePointAttribute(
69 | PointAttributeName.POSITION_CARTESIAN,
70 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_FLOAT,
71 | 3,
72 | ),
73 | RGBA_PACKED: RGBA_PACKED,
74 | COLOR_PACKED: RGBA_PACKED,
75 | RGB_PACKED: makePointAttribute(
76 | PointAttributeName.COLOR_PACKED,
77 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_INT8,
78 | 3,
79 | ),
80 | NORMAL_FLOATS: makePointAttribute(
81 | PointAttributeName.NORMAL_FLOATS,
82 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_FLOAT,
83 | 3,
84 | ),
85 | FILLER_1B: makePointAttribute(
86 | PointAttributeName.FILLER,
87 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_UINT8,
88 | 1,
89 | ),
90 | INTENSITY: makePointAttribute(
91 | PointAttributeName.INTENSITY,
92 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_UINT16,
93 | 1,
94 | ),
95 | CLASSIFICATION: makePointAttribute(
96 | PointAttributeName.CLASSIFICATION,
97 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_UINT8,
98 | 1,
99 | ),
100 | NORMAL_SPHEREMAPPED: makePointAttribute(
101 | PointAttributeName.NORMAL_SPHEREMAPPED,
102 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_UINT8,
103 | 2,
104 | ),
105 | NORMAL_OCT16: makePointAttribute(
106 | PointAttributeName.NORMAL_OCT16,
107 | POINT_ATTRIBUTE_TYPES.DATA_TYPE_UINT8,
108 | 2,
109 | ),
110 | NORMAL: makePointAttribute(PointAttributeName.NORMAL, POINT_ATTRIBUTE_TYPES.DATA_TYPE_FLOAT, 3)
111 | };
112 |
113 | export type PointAttributeStringName = keyof typeof POINT_ATTRIBUTES;
114 |
115 | export class PointAttributes implements IPointAttributes
116 | {
117 | attributes: IPointAttribute[] = [];
118 |
119 | byteSize: number = 0;
120 |
121 | size: number = 0;
122 |
123 | constructor(pointAttributeNames: PointAttributeStringName[] = [])
124 | {
125 | for (let i = 0; i < pointAttributeNames.length; i++)
126 | {
127 | const pointAttributeName = pointAttributeNames[i];
128 | const pointAttribute = POINT_ATTRIBUTES[pointAttributeName];
129 | try {
130 | this.attributes.push(pointAttribute);
131 | this.byteSize += pointAttribute.byteSize;
132 | this.size++;
133 | } catch(e) {}
134 | }
135 | }
136 |
137 | add(pointAttribute: IPointAttribute): void
138 | {
139 | this.attributes.push(pointAttribute);
140 | this.byteSize += pointAttribute.byteSize;
141 | this.size++;
142 | }
143 |
144 | hasColors(): boolean
145 | {
146 | return this.attributes.find(isColorAttribute) !== undefined;
147 | }
148 |
149 | hasNormals(): boolean
150 | {
151 | return this.attributes.find(isNormalAttribute) !== undefined;
152 | }
153 | }
154 |
155 | function isColorAttribute({name}: IPointAttribute): boolean
156 | {
157 | return name === PointAttributeName.COLOR_PACKED;
158 | }
159 |
160 | function isNormalAttribute({name}: IPointAttribute): boolean
161 | {
162 | return (
163 | name === PointAttributeName.NORMAL_SPHEREMAPPED ||
164 | name === PointAttributeName.NORMAL_FLOATS ||
165 | name === PointAttributeName.NORMAL ||
166 | name === PointAttributeName.NORMAL_OCT16
167 | );
168 | }
169 |
--------------------------------------------------------------------------------
/source/point-cloud-octree-geometry-node.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Adapted from Potree.js http://potree.org
3 | * Potree License: https://github.com/potree/potree/blob/1.5/LICENSE
4 | */
5 |
6 | import {Box3, BufferGeometry, EventDispatcher, Sphere, Vector3} from 'three';
7 | import {PointCloudOctreeGeometry} from './point-cloud-octree-geometry';
8 | import {IPointCloudTreeNode} from './types';
9 | import {createChildAABB} from './utils/bounds';
10 | import {getIndexFromName} from './utils/utils';
11 |
12 | export interface NodeData {
13 | children: number;
14 | numPoints: number;
15 | name: string;
16 | }
17 |
18 | const NODE_STRIDE = 5;
19 |
20 | export class PointCloudOctreeGeometryNode extends EventDispatcher implements IPointCloudTreeNode
21 | {
22 | public id: number = PointCloudOctreeGeometryNode.idCount++;
23 |
24 | public name: string;
25 |
26 | public pcoGeometry: PointCloudOctreeGeometry;
27 |
28 | public index: number;
29 |
30 | public level: number = 0;
31 |
32 | public spacing: number = 0;
33 |
34 | public hasChildren: boolean = false;
35 |
36 | readonly children: ReadonlyArray = [
37 | null,
38 | null,
39 | null,
40 | null,
41 | null,
42 | null,
43 | null,
44 | null
45 | ];
46 |
47 | public boundingBox: Box3;
48 |
49 | public tightBoundingBox: Box3;
50 |
51 | public boundingSphere: Sphere;
52 |
53 | public mean: Vector3 = new Vector3();
54 |
55 | public numPoints: number = 0;
56 |
57 | public geometry: BufferGeometry | undefined;
58 |
59 | public loaded: boolean = false;
60 |
61 | public loading: boolean = false;
62 |
63 | public failed: boolean = false;
64 |
65 | public parent: PointCloudOctreeGeometryNode | null = null;
66 |
67 | public oneTimeDisposeHandlers: (()=> void)[] = [];
68 |
69 | public isLeafNode: boolean = true;
70 |
71 | readonly isTreeNode: boolean = false;
72 |
73 | readonly isGeometryNode: boolean = true;
74 |
75 | private static idCount = 0;
76 |
77 | constructor(name: string, pcoGeometry: PointCloudOctreeGeometry, boundingBox: Box3)
78 | {
79 | super();
80 |
81 | this.name = name;
82 | this.index = getIndexFromName(name);
83 | this.pcoGeometry = pcoGeometry;
84 | this.boundingBox = boundingBox;
85 | this.tightBoundingBox = boundingBox.clone();
86 | this.boundingSphere = boundingBox.getBoundingSphere(new Sphere());
87 | }
88 |
89 | dispose(): void
90 | {
91 | if (!this.geometry || !this.parent)
92 | {
93 | return;
94 | }
95 |
96 | this.geometry.dispose();
97 | this.geometry = undefined;
98 | this.loaded = false;
99 |
100 | this.oneTimeDisposeHandlers.forEach((handler) => {return handler();});
101 | this.oneTimeDisposeHandlers = [];
102 | }
103 |
104 | /**
105 | * Gets the url of the binary file for this node.
106 | */
107 | getUrl(): string
108 | {
109 | const geometry = this.pcoGeometry;
110 | const version = geometry.loader.version;
111 | const pathParts = [geometry.octreeDir];
112 |
113 | if (geometry.loader && version.equalOrHigher('1.5'))
114 | {
115 | pathParts.push(this.getHierarchyBaseUrl());
116 | pathParts.push(this.name);
117 | }
118 | else if (version.equalOrHigher('1.4'))
119 | {
120 | pathParts.push(this.name);
121 | }
122 | else if (version.upTo('1.3'))
123 | {
124 | pathParts.push(this.name);
125 | }
126 |
127 | return pathParts.join('/');
128 | }
129 |
130 | /**
131 | * Gets the url of the hierarchy file for this node.
132 | */
133 | public getHierarchyUrl(): string
134 | {
135 | return `${this.pcoGeometry.octreeDir}/${this.getHierarchyBaseUrl()}/${this.name}.hrc`;
136 | }
137 |
138 | /**
139 | * Adds the specified node as a child of the current node.
140 | *
141 | * @param child - The node which is to be added as a child.
142 | */
143 | public addChild(child: PointCloudOctreeGeometryNode): void
144 | {
145 | (this.children as any)[child.index] = child;
146 | this.isLeafNode = false;
147 | child.parent = this;
148 | }
149 |
150 | /**
151 | * Calls the specified callback for the current node (if includeSelf is set to true) and all its children.
152 | *
153 | * @param cb - The function which is to be called for each node.
154 | */
155 | public traverse(cb: (node: PointCloudOctreeGeometryNode)=> void, includeSelf = true): void
156 | {
157 | const stack: PointCloudOctreeGeometryNode[] = includeSelf ? [this] : [];
158 |
159 | let current: PointCloudOctreeGeometryNode | undefined;
160 |
161 | while ((current = stack.pop()) !== undefined)
162 | {
163 | cb(current);
164 |
165 | for (const child of current.children)
166 | {
167 | if (child !== null)
168 | {
169 | stack.push(child);
170 | }
171 | }
172 | }
173 | }
174 |
175 | public load(): Promise
176 | {
177 | if (!this.canLoad())
178 | {
179 | return Promise.resolve();
180 | }
181 |
182 | this.loading = true;
183 | this.pcoGeometry.numNodesLoading++;
184 | this.pcoGeometry.needsUpdate = true;
185 |
186 | let promise: Promise;
187 |
188 | if (
189 | this.pcoGeometry.loader.version.equalOrHigher('1.5') &&
190 | this.level % this.pcoGeometry.hierarchyStepSize === 0 &&
191 | this.hasChildren
192 | )
193 | {
194 | promise = this.loadHierachyThenPoints();
195 | }
196 | else
197 | {
198 | promise = this.loadPoints();
199 | }
200 |
201 | return promise.catch((reason) =>
202 | {
203 | this.loading = false;
204 | this.failed = true;
205 | this.pcoGeometry.numNodesLoading--;
206 | throw reason;
207 | });
208 | }
209 |
210 | private canLoad(): boolean
211 | {
212 | return (
213 | !this.loading &&
214 | !this.loaded &&
215 | !this.pcoGeometry.disposed &&
216 | !this.pcoGeometry.loader.disposed &&
217 | this.pcoGeometry.numNodesLoading < this.pcoGeometry.maxNumNodesLoading
218 | );
219 | }
220 |
221 | private loadPoints(): Promise
222 | {
223 | this.pcoGeometry.needsUpdate = true;
224 | return this.pcoGeometry.loader.load(this);
225 | }
226 |
227 | private loadHierachyThenPoints(): Promise
228 | {
229 | if (this.level % this.pcoGeometry.hierarchyStepSize !== 0)
230 | {
231 | return Promise.resolve();
232 | }
233 |
234 | return Promise.resolve(this.pcoGeometry.loader.getUrl(this.getHierarchyUrl()))
235 | .then((url) => {return this.pcoGeometry.xhrRequest(url, {mode: 'cors'});})
236 | .then((res) => {return res.arrayBuffer();})
237 | .then((data) => {return this.loadHierarchy(this, data);});
238 | }
239 |
240 | /**
241 | * Gets the url of the folder where the hierarchy is, relative to the octreeDir.
242 | */
243 | private getHierarchyBaseUrl(): string
244 | {
245 | const hierarchyStepSize = this.pcoGeometry.hierarchyStepSize;
246 | const indices = this.name.substr(1);
247 | const numParts = Math.floor(indices.length / hierarchyStepSize);
248 |
249 | let path = 'r/';
250 | for (let i = 0; i < numParts; i++)
251 | {
252 | path += `${indices.substr(i * hierarchyStepSize, hierarchyStepSize)}/`;
253 | }
254 |
255 | return path.slice(0, -1);
256 | }
257 |
258 | // tslint:disable:no-bitwise
259 | private loadHierarchy(node: PointCloudOctreeGeometryNode, buffer: ArrayBuffer)
260 | {
261 | const view = new DataView(buffer);
262 |
263 | const firstNodeData = this.getNodeData(node.name, 0, view);
264 | node.numPoints = firstNodeData.numPoints;
265 |
266 | // Nodes which need be visited.
267 | const stack: NodeData[] = [firstNodeData];
268 | // Nodes which have already been decoded. We will take nodes from the stack and place them here.
269 | const decoded: NodeData[] = [];
270 |
271 | let offset = NODE_STRIDE;
272 | while (stack.length > 0)
273 | {
274 | const stackNodeData = stack.shift()!;
275 |
276 | // From the last bit, all the way to the 8th one from the right.
277 | let mask = 1;
278 | for (let i = 0; i < 8 && offset + 1 < buffer.byteLength; i++)
279 | {
280 | if ((stackNodeData.children & mask) !== 0)
281 | {
282 | const nodeData = this.getNodeData(stackNodeData.name + i, offset, view);
283 |
284 | decoded.push(nodeData); // Node is decoded.
285 | stack.push(nodeData); // Need to check its children.
286 |
287 | offset += NODE_STRIDE; // Move over to the next node in the buffer.
288 | }
289 |
290 | mask = mask * 2;
291 | }
292 | }
293 |
294 | node.pcoGeometry.needsUpdate = true;
295 |
296 | // Map containing all the nodes.
297 | const nodes = new Map();
298 | nodes.set(node.name, node);
299 | decoded.forEach((nodeData) => {return this.addNode(nodeData, node.pcoGeometry, nodes);});
300 |
301 | node.loadPoints();
302 | }
303 |
304 | // tslint:enable:no-bitwise
305 |
306 | private getNodeData(name: string, offset: number, view: DataView): NodeData
307 | {
308 | const children = view.getUint8(offset);
309 | const numPoints = view.getUint32(offset + 1, true);
310 | return {children: children, numPoints: numPoints, name: name};
311 | }
312 |
313 | addNode(
314 | {name, numPoints, children}: NodeData,
315 | pco: PointCloudOctreeGeometry,
316 | nodes: Map,
317 | ): void
318 | {
319 | const index = getIndexFromName(name);
320 | const parentName = name.substring(0, name.length - 1);
321 | const parentNode = nodes.get(parentName)!;
322 | const level = name.length - 1;
323 | const boundingBox = createChildAABB(parentNode.boundingBox, index);
324 |
325 | const node = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
326 | node.level = level;
327 | node.numPoints = numPoints;
328 | node.hasChildren = children > 0;
329 | node.spacing = pco.spacing / Math.pow(2, level);
330 |
331 | parentNode.addChild(node);
332 | nodes.set(name, node);
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/source/point-cloud-octree-geometry.ts:
--------------------------------------------------------------------------------
1 | import {Box3, Vector3} from 'three';
2 | import {BinaryLoader, XhrRequest} from './loading';
3 | import {PointAttributes} from './point-attributes';
4 | import {PointCloudOctreeGeometryNode} from './point-cloud-octree-geometry-node';
5 |
6 | export class PointCloudOctreeGeometry
7 | {
8 | public disposed: boolean = false;
9 |
10 | public needsUpdate: boolean = true;
11 |
12 | public root!: PointCloudOctreeGeometryNode;
13 |
14 | public octreeDir: string = '';
15 |
16 | public hierarchyStepSize: number = -1;
17 |
18 | public nodes: Record = {};
19 |
20 | public numNodesLoading: number = 0;
21 |
22 | public maxNumNodesLoading: number = 3;
23 |
24 | public spacing: number = 0;
25 |
26 | public pointAttributes: PointAttributes = new PointAttributes([]);
27 |
28 | public projection: any = null;
29 |
30 | public url: string | null = null;
31 |
32 | constructor(
33 | public loader: BinaryLoader,
34 | public boundingBox: Box3,
35 | public tightBoundingBox: Box3,
36 | public offset: Vector3,
37 | public xhrRequest: XhrRequest,
38 | ) {}
39 |
40 | dispose(): void
41 | {
42 | this.loader.dispose();
43 | this.root.traverse((node) => {return node.dispose();});
44 |
45 | this.disposed = true;
46 | }
47 |
48 | addNodeLoadedCallback(callback: (node: PointCloudOctreeGeometryNode)=> void): void
49 | {
50 | this.loader.callbacks.push(callback);
51 | }
52 |
53 | clearNodeLoadedCallbacks(): void
54 | {
55 | this.loader.callbacks = [];
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/source/point-cloud-octree-node.ts:
--------------------------------------------------------------------------------
1 | import {Box3, BufferGeometry, EventDispatcher, Object3D, Points, Sphere} from 'three';
2 | import {PointCloudOctreeGeometryNode} from './point-cloud-octree-geometry-node';
3 | import {IPointCloudTreeNode} from './types';
4 |
5 | export class PointCloudOctreeNode extends EventDispatcher implements IPointCloudTreeNode
6 | {
7 | geometryNode: PointCloudOctreeGeometryNode;
8 |
9 | sceneNode: Points;
10 |
11 | pcIndex: number | undefined = undefined;
12 |
13 | boundingBoxNode: Object3D | null = null;
14 |
15 | readonly children: (IPointCloudTreeNode | null)[];
16 |
17 | readonly loaded = true;
18 |
19 | readonly isTreeNode: boolean = true;
20 |
21 | readonly isGeometryNode: boolean = false;
22 |
23 | constructor(geometryNode: PointCloudOctreeGeometryNode, sceneNode: Points)
24 | {
25 | super();
26 |
27 | this.geometryNode = geometryNode;
28 | this.sceneNode = sceneNode;
29 | this.children = geometryNode.children.slice();
30 | }
31 |
32 | dispose(): void
33 | {
34 | this.geometryNode.dispose();
35 | }
36 |
37 | disposeSceneNode(): void
38 | {
39 | const node = this.sceneNode;
40 |
41 | if (node.geometry instanceof BufferGeometry)
42 | {
43 | const attributes = node.geometry.attributes;
44 |
45 | // tslint:disable-next-line:forin
46 | for (const key in attributes)
47 | {
48 | if (key === 'position')
49 | {
50 | delete (attributes[key] as any).array;
51 | }
52 |
53 | delete attributes[key];
54 | }
55 |
56 | node.geometry.dispose();
57 | node.geometry = undefined as any;
58 | }
59 | }
60 |
61 | traverse(cb: (node: IPointCloudTreeNode)=> void, includeSelf?: boolean): void
62 | {
63 | this.geometryNode.traverse(cb, includeSelf);
64 | }
65 |
66 | get id()
67 | {
68 | return this.geometryNode.id;
69 | }
70 |
71 | get name()
72 | {
73 | return this.geometryNode.name;
74 | }
75 |
76 | get level(): number
77 | {
78 | return this.geometryNode.level;
79 | }
80 |
81 | get isLeafNode(): boolean
82 | {
83 | return this.geometryNode.isLeafNode;
84 | }
85 |
86 | get numPoints(): number
87 | {
88 | return this.geometryNode.numPoints;
89 | }
90 |
91 | get index()
92 | {
93 | return this.geometryNode.index;
94 | }
95 |
96 | get boundingSphere(): Sphere
97 | {
98 | return this.geometryNode.boundingSphere;
99 | }
100 |
101 | get boundingBox(): Box3
102 | {
103 | return this.geometryNode.boundingBox;
104 | }
105 |
106 | get spacing()
107 | {
108 | return this.geometryNode.spacing;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/source/point-cloud-octree.ts:
--------------------------------------------------------------------------------
1 | import {OctreeGeometry} from './loading2/OctreeGeometry';
2 | import {Box3, Camera, Object3D, Points, Ray, Sphere, Vector3, WebGLRenderer} from 'three';
3 | import {DEFAULT_MIN_NODE_PIXEL_SIZE} from './constants';
4 | import {PointCloudMaterial, PointSizeType} from './materials';
5 | import {PointCloudOctreeGeometryNode} from './point-cloud-octree-geometry-node';
6 | import {PointCloudOctreeNode} from './point-cloud-octree-node';
7 | import {PickParams, PointCloudOctreePicker} from './point-cloud-octree-picker';
8 | import {PointCloudTree} from './point-cloud-tree';
9 | import {IPointCloudTreeNode, IPotree, PickPoint, PCOGeometry} from './types';
10 | import {computeTransformedBoundingBox} from './utils/bounds';
11 |
12 | export class PointCloudOctree extends PointCloudTree
13 | {
14 | potree: IPotree;
15 |
16 | disposed: boolean = false;
17 |
18 | pcoGeometry: PCOGeometry;
19 |
20 | boundingBox: Box3;
21 |
22 | boundingSphere: Sphere;
23 |
24 | material: PointCloudMaterial;
25 |
26 | level: number = 0;
27 |
28 | maxLevel: number = Infinity;
29 |
30 | /**
31 | * The minimum radius of a node's bounding sphere on the screen in order to be displayed.
32 | */
33 | minNodePixelSize: number = DEFAULT_MIN_NODE_PIXEL_SIZE;
34 |
35 | root: IPointCloudTreeNode | null = null;
36 |
37 | boundingBoxNodes: Object3D[] = [];
38 |
39 | visibleNodes: PointCloudOctreeNode[] = [];
40 |
41 | visibleGeometry: PointCloudOctreeGeometryNode[] = [];
42 |
43 | numVisiblePoints: number = 0;
44 |
45 | showBoundingBox: boolean = false;
46 |
47 | private visibleBounds: Box3 = new Box3();
48 |
49 | private picker: PointCloudOctreePicker | undefined;
50 |
51 | constructor(
52 | potree: IPotree,
53 | pcoGeometry: PCOGeometry,
54 | material?: PointCloudMaterial,
55 | )
56 | {
57 | super();
58 |
59 | this.name = '';
60 | this.potree = potree;
61 | this.root = pcoGeometry.root;
62 | this.pcoGeometry = pcoGeometry;
63 | this.boundingBox = pcoGeometry.boundingBox;
64 | this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
65 |
66 | this.position.copy(pcoGeometry.offset);
67 | this.updateMatrix();
68 |
69 | this.material = material || pcoGeometry instanceof OctreeGeometry ? new PointCloudMaterial({newFormat: true}) : new PointCloudMaterial();
70 | this.initMaterial(this.material);
71 | }
72 |
73 | private initMaterial(material: PointCloudMaterial): void
74 | {
75 | this.updateMatrixWorld(true);
76 |
77 | const {min, max} = computeTransformedBoundingBox(
78 | this.pcoGeometry.tightBoundingBox || this.getBoundingBoxWorld(),
79 | this.matrixWorld,
80 | );
81 |
82 | const bWidth = max.z - min.z;
83 | material.heightMin = min.z - 0.2 * bWidth;
84 | material.heightMax = max.z + 0.2 * bWidth;
85 | }
86 |
87 | dispose(): void
88 | {
89 | if (this.root)
90 | {
91 | this.root.dispose();
92 | }
93 |
94 | this.pcoGeometry.root.traverse((n: IPointCloudTreeNode) => {return this.potree.lru.remove(n);});
95 | this.pcoGeometry.dispose();
96 | this.material.dispose();
97 |
98 | this.visibleNodes = [];
99 | this.visibleGeometry = [];
100 |
101 | if (this.picker)
102 | {
103 | this.picker.dispose();
104 | this.picker = undefined;
105 | }
106 |
107 | this.disposed = true;
108 | }
109 |
110 | get pointSizeType(): PointSizeType
111 | {
112 | return this.material.pointSizeType;
113 | }
114 |
115 | set pointSizeType(value: PointSizeType)
116 | {
117 | this.material.pointSizeType = value;
118 | }
119 |
120 | toTreeNode(
121 | geometryNode: PointCloudOctreeGeometryNode,
122 | parent?: PointCloudOctreeNode | null,
123 | ): PointCloudOctreeNode
124 | {
125 | const points = new Points(geometryNode.geometry, this.material);
126 | const node = new PointCloudOctreeNode(geometryNode, points);
127 | points.name = geometryNode.name;
128 | points.position.copy(geometryNode.boundingBox.min);
129 | points.frustumCulled = false;
130 | points.onBeforeRender = PointCloudMaterial.makeOnBeforeRender(this, node);
131 |
132 | if (parent)
133 | {
134 | parent.sceneNode.add(points);
135 | parent.children[geometryNode.index] = node;
136 |
137 | geometryNode.oneTimeDisposeHandlers.push(() =>
138 | {
139 | node.disposeSceneNode();
140 | parent.sceneNode.remove(node.sceneNode);
141 | // Replace the tree node (rendered and in the GPU) with the geometry node.
142 | parent.children[geometryNode.index] = geometryNode;
143 | });
144 | }
145 | else
146 | {
147 | this.root = node;
148 | this.add(points);
149 | }
150 |
151 | return node;
152 | }
153 |
154 | updateVisibleBounds()
155 | {
156 | const bounds = this.visibleBounds;
157 | bounds.min.set(Infinity, Infinity, Infinity);
158 | bounds.max.set(-Infinity, -Infinity, -Infinity);
159 |
160 | for (const node of this.visibleNodes)
161 | {
162 | if (node.isLeafNode)
163 | {
164 | bounds.expandByPoint(node.boundingBox.min);
165 | bounds.expandByPoint(node.boundingBox.max);
166 | }
167 | }
168 | }
169 |
170 | updateBoundingBoxes(): void
171 | {
172 | if (!this.showBoundingBox || !this.parent)
173 | {
174 | return;
175 | }
176 | // Above: If we're not showing the bounding box or we don't have a parent, we can't update it.
177 |
178 | let bbRoot: any = this.parent.getObjectByName('bbroot');
179 | if (!bbRoot)
180 | {
181 | bbRoot = new Object3D();
182 | bbRoot.name = 'bbroot';
183 | this.parent.add(bbRoot);
184 | }
185 | // Above: If we don't have a root object, we need to create one.
186 |
187 | const visibleBoxes: (Object3D | null)[] = [];
188 | for (const node of this.visibleNodes)
189 | {
190 | if (node.boundingBoxNode !== undefined && node.isLeafNode)
191 | {
192 | visibleBoxes.push(node.boundingBoxNode);
193 | }
194 | }
195 |
196 | bbRoot.children = visibleBoxes;
197 | }
198 |
199 | updateMatrixWorld(force: boolean): void
200 | {
201 | if (this.matrixAutoUpdate === true)
202 | {
203 | this.updateMatrix();
204 | }
205 |
206 | if (this.matrixWorldNeedsUpdate === true || force === true)
207 | {
208 | if (!this.parent)
209 | {
210 | this.matrixWorld.copy(this.matrix);
211 | }
212 | else
213 | {
214 | this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
215 | }
216 |
217 | this.matrixWorldNeedsUpdate = false;
218 |
219 | force = true;
220 | }
221 | }
222 |
223 | hideDescendants(object: Object3D): void
224 | {
225 | const toHide: Object3D[] = [];
226 | addVisibleChildren(object);
227 |
228 | while (toHide.length > 0)
229 | {
230 | const objToHide = toHide.shift()!;
231 | objToHide.visible = false;
232 | addVisibleChildren(objToHide);
233 | }
234 |
235 | function addVisibleChildren(obj: Object3D)
236 | {
237 | for (const child of obj.children)
238 | {
239 | if (child.visible)
240 | {
241 | toHide.push(child);
242 | }
243 | }
244 | }
245 | }
246 |
247 | moveToOrigin(): void
248 | {
249 | this.position.set(0, 0, 0); // Reset, then the matrix will be updated in getBoundingBoxWorld()
250 | this.position.set(0, 0, 0).sub(this.getBoundingBoxWorld().getCenter(new Vector3()));
251 | }
252 |
253 | moveToGroundPlane(): void
254 | {
255 | this.position.y += -this.getBoundingBoxWorld().min.y;
256 | }
257 |
258 | getBoundingBoxWorld(): Box3
259 | {
260 | this.updateMatrixWorld(true);
261 | return computeTransformedBoundingBox(this.boundingBox, this.matrixWorld);
262 | }
263 |
264 | getVisibleExtent()
265 | {
266 | return this.visibleBounds.applyMatrix4(this.matrixWorld);
267 | }
268 |
269 | pick(
270 | renderer: WebGLRenderer,
271 | camera: Camera,
272 | ray: Ray,
273 | params: Partial = {},
274 | ): PickPoint | null
275 | {
276 | this.picker = this.picker || new PointCloudOctreePicker();
277 | return this.picker.pick(renderer, camera, ray, [this], params);
278 | }
279 |
280 | get progress()
281 | {
282 | return this.visibleGeometry.length === 0
283 | ? 0
284 | : this.visibleNodes.length / this.visibleGeometry.length;
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/source/point-cloud-tree.ts:
--------------------------------------------------------------------------------
1 | import {Object3D} from 'three';
2 | import {IPointCloudTreeNode} from './types';
3 |
4 | export class PointCloudTree extends Object3D
5 | {
6 | root: IPointCloudTreeNode | null = null;
7 |
8 | initialized()
9 | {
10 | return this.root !== null;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/source/type-predicates.ts:
--------------------------------------------------------------------------------
1 | import {PointCloudOctreeGeometryNode} from './point-cloud-octree-geometry-node';
2 | // import { PointCloudOctreeNode } from './point-cloud-octree-node';
3 |
4 | export function isGeometryNode(node?: any): node is PointCloudOctreeGeometryNode
5 | { // 'node is' in this case
6 | return node !== undefined && node !== null && node.isGeometryNode;
7 | }
8 |
9 | // export function isTreeNode(node?: any): node is PointCloudOctreeNode { // Problem is here! Pnext modified isTreeNode to be one function when it's normally attached as a method to the root node.
10 | // return node !== undefined && node !== null && node.isTreeNode;
11 | // }
12 |
13 | export function isTreeNode(node?: any)
14 | {
15 | return node !== undefined && node !== null && node.isTreeNode;
16 | }
17 |
--------------------------------------------------------------------------------
/source/types.ts:
--------------------------------------------------------------------------------
1 | import {OctreeGeometry} from './loading2/OctreeGeometry';
2 | import {PointCloudOctreeGeometry} from './point-cloud-octree-geometry';
3 | import {Box3, Camera, Sphere, Vector3, WebGLRenderer} from 'three';
4 | import {GetUrlFn, XhrRequest} from './loading/types';
5 | import {PointCloudOctree} from './point-cloud-octree';
6 | import {LRU} from './utils/lru';
7 |
8 | export interface IPointCloudTreeNode {
9 | id: number;
10 | name: string;
11 | level: number;
12 | index: number;
13 | spacing: number;
14 | boundingBox: Box3;
15 | boundingSphere: Sphere;
16 | loaded: boolean;
17 | numPoints: number;
18 | readonly children: ReadonlyArray;
19 | readonly isLeafNode: boolean;
20 | // This probably needs isTreeNode and isGeometryNode as readonly properties too?
21 |
22 | dispose(): void;
23 |
24 | traverse(cb: (node: IPointCloudTreeNode)=> void, includeSelf?: boolean): void;
25 | }
26 |
27 | export interface IVisibilityUpdateResult {
28 | visibleNodes: IPointCloudTreeNode[];
29 | numVisiblePoints: number;
30 | /**
31 | * True when a node has been loaded but was not added to the scene yet.
32 | * Make sure to call updatePointClouds() again on the next frame.
33 | */
34 | exceededMaxLoadsToGPU: boolean;
35 | /**
36 | * True when at least one node in view has failed to load.
37 | */
38 | nodeLoadFailed: boolean;
39 | /**
40 | * Promises for loading nodes, will reject when loading fails.
41 | */
42 | nodeLoadPromises: Promise[];
43 | }
44 |
45 | export interface IPotree {
46 | pointBudget: number;
47 | maxNumNodesLoading: number;
48 | lru: LRU;
49 |
50 | loadPointCloud(url: string, getUrl: GetUrlFn, xhrRequest?: XhrRequest): Promise;
51 |
52 | updatePointClouds(
53 | pointClouds: PointCloudOctree[],
54 | camera: Camera,
55 | renderer: WebGLRenderer,
56 | ): IVisibilityUpdateResult;
57 | }
58 |
59 | export interface PickPoint {
60 | position?: Vector3;
61 | normal?: Vector3;
62 | pointCloud?: PointCloudOctree;
63 | [property: string]: any;
64 | }
65 |
66 | export interface PointCloudHit {
67 | pIndex: number;
68 | pcIndex: number;
69 | }
70 |
71 | export type PCOGeometry = PointCloudOctreeGeometry | OctreeGeometry;
72 |
--------------------------------------------------------------------------------
/source/utils/binary-heap.d.ts:
--------------------------------------------------------------------------------
1 | export class BinaryHeap
2 | {
3 | constructor(scoreFunction: (node: T)=> number);
4 |
5 | push(node: T): void;
6 |
7 | pop(): T | undefined;
8 |
9 | remove(node: T): void;
10 |
11 | size(): number;
12 |
13 | bubbleUp(n: number): void;
14 |
15 | sinkDown(n: number): void;
16 | }
17 |
--------------------------------------------------------------------------------
/source/utils/binary-heap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * from: http://eloquentjavascript.net/1st_edition/appendix2.html
3 | *
4 | */
5 |
6 | export function BinaryHeap(scoreFunction)
7 | {
8 | this.content = [];
9 | this.scoreFunction = scoreFunction;
10 | }
11 |
12 | BinaryHeap.prototype = {
13 | push: function(element)
14 | {
15 | // Add the new element to the end of the array.
16 | this.content.push(element);
17 | // Allow it to bubble up.
18 | this.bubbleUp(this.content.length - 1);
19 | },
20 |
21 | pop: function()
22 | {
23 | // Store the first element so we can return it later.
24 | var result = this.content[0];
25 | // Get the element at the end of the array.
26 | var end = this.content.pop();
27 | // If there are any elements left, put the end element at the
28 | // start, and let it sink down.
29 | if (this.content.length > 0)
30 | {
31 | this.content[0] = end;
32 | this.sinkDown(0);
33 | }
34 | return result;
35 | },
36 |
37 | remove: function(node)
38 | {
39 | var length = this.content.length;
40 | // To remove a value, we must search through the array to find
41 | // it.
42 | for (var i = 0; i < length; i++)
43 | {
44 | if (this.content[i] !== node) {continue;}
45 | // When it is found, the process seen in 'pop' is repeated
46 | // to fill up the hole.
47 | var end = this.content.pop();
48 | // If the element we popped was the one we needed to remove,
49 | // we're done.
50 | if (i === length - 1) {break;}
51 | // Otherwise, we replace the removed element with the popped
52 | // one, and allow it to float up or sink down as appropriate.
53 | this.content[i] = end;
54 | this.bubbleUp(i);
55 | this.sinkDown(i);
56 | break;
57 | }
58 | },
59 |
60 | size: function()
61 | {
62 | return this.content.length;
63 | },
64 |
65 | bubbleUp: function(n)
66 | {
67 | // Fetch the element that has to be moved.
68 | var element = this.content[n],
69 | score = this.scoreFunction(element);
70 | // When at 0, an element can not go up any further.
71 | while (n > 0)
72 | {
73 | // Compute the parent element's index, and fetch it.
74 | var parentN = Math.floor((n + 1) / 2) - 1,
75 | parent = this.content[parentN];
76 | // If the parent has a lesser score, things are in order and we
77 | // are done.
78 | if (score >= this.scoreFunction(parent)) {break;}
79 |
80 | // Otherwise, swap the parent with the current element and
81 | // continue.
82 | this.content[parentN] = element;
83 | this.content[n] = parent;
84 | n = parentN;
85 | }
86 | },
87 |
88 | sinkDown: function(n)
89 | {
90 | // Look up the target element and its score.
91 | var length = this.content.length,
92 | element = this.content[n],
93 | elemScore = this.scoreFunction(element);
94 |
95 | while (true)
96 | {
97 | // Compute the indices of the child elements.
98 | var child2N = (n + 1) * 2,
99 | child1N = child2N - 1;
100 | // This is used to store the new position of the element,
101 | // if any.
102 | var swap = null;
103 | // If the first child exists (is inside the array)...
104 | if (child1N < length)
105 | {
106 | // Look it up and compute its score.
107 | var child1 = this.content[child1N],
108 | child1Score = this.scoreFunction(child1);
109 | // If the score is less than our element's, we need to swap.
110 | if (child1Score < elemScore) {swap = child1N;}
111 | }
112 | // Do the same checks for the other child.
113 | if (child2N < length)
114 | {
115 | var child2 = this.content[child2N],
116 | child2Score = this.scoreFunction(child2);
117 | if (child2Score < (swap == null ? elemScore : child1Score)) {swap = child2N;}
118 | }
119 |
120 | // No need to swap further, we are done.
121 | if (swap == null) {break;}
122 |
123 | // Otherwise, swap and continue.
124 | this.content[n] = this.content[swap];
125 | this.content[swap] = element;
126 | n = swap;
127 | }
128 | }
129 | };
130 |
--------------------------------------------------------------------------------
/source/utils/bounds.ts:
--------------------------------------------------------------------------------
1 | import {Box3, Matrix4, Vector3} from 'three';
2 |
3 | /**
4 | * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561
5 | */
6 | export function computeTransformedBoundingBox(box: Box3, transform: Matrix4): Box3
7 | {
8 | return new Box3().setFromPoints([
9 | new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
10 | new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
11 | new Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform),
12 | new Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform),
13 | new Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform),
14 | new Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform),
15 | new Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform),
16 | new Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform),
17 | new Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform)
18 | ]);
19 | }
20 |
21 | export function createChildAABB(aabb: Box3, index: number): Box3
22 | {
23 | const min = aabb.min.clone();
24 | const max = aabb.max.clone();
25 | const size = new Vector3().subVectors(max, min);
26 |
27 | // tslint:disable-next-line:no-bitwise
28 | if ((index & 0b0001) > 0)
29 | {
30 | min.z += size.z / 2;
31 | }
32 | else
33 | {
34 | max.z -= size.z / 2;
35 | }
36 |
37 | // tslint:disable-next-line:no-bitwise
38 | if ((index & 0b0010) > 0)
39 | {
40 | min.y += size.y / 2;
41 | }
42 | else
43 | {
44 | max.y -= size.y / 2;
45 | }
46 |
47 | // tslint:disable-next-line:no-bitwise
48 | if ((index & 0b0100) > 0)
49 | {
50 | min.x += size.x / 2;
51 | }
52 | else
53 | {
54 | max.x -= size.x / 2;
55 | }
56 |
57 | return new Box3(min, max);
58 | }
59 |
--------------------------------------------------------------------------------
/source/utils/box3-helper.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Box3,
3 | BufferAttribute,
4 | BufferGeometry,
5 | Color,
6 | LineBasicMaterial,
7 | LineSegments
8 | } from 'three';
9 |
10 | /**
11 | *
12 | * code adapted from three.js BoxHelper.js
13 | * https://github.com/mrdoob/three.js/blob/dev/src/helpers/BoxHelper.js
14 | *
15 | * @author mrdoob / http://mrdoob.com/
16 | * @author Mugen87 / http://github.com/Mugen87
17 | * @author mschuetz / http://potree.org
18 | */
19 |
20 | export class Box3Helper extends LineSegments
21 | {
22 | constructor(box: Box3, color: Color = new Color(0xffff00))
23 | {
24 | // prettier-ignore
25 | const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]);
26 | // prettier-ignore
27 | const positions = new Float32Array([
28 | box.min.x, box.min.y, box.min.z,
29 | box.max.x, box.min.y, box.min.z,
30 | box.max.x, box.min.y, box.max.z,
31 | box.min.x, box.min.y, box.max.z,
32 | box.min.x, box.max.y, box.min.z,
33 | box.max.x, box.max.y, box.min.z,
34 | box.max.x, box.max.y, box.max.z,
35 | box.min.x, box.max.y, box.max.z
36 | ]);
37 |
38 | const geometry = new BufferGeometry();
39 | geometry.setIndex(new BufferAttribute(indices, 1));
40 | geometry.setAttribute('position', new BufferAttribute(positions, 3));
41 |
42 | const material = new LineBasicMaterial({color: color});
43 |
44 | super(geometry, material);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/source/utils/lru.ts:
--------------------------------------------------------------------------------
1 | import {IPointCloudTreeNode} from '../types';
2 |
3 | export type Node = IPointCloudTreeNode;
4 |
5 | export class LRUItem
6 | {
7 | next: LRUItem | null = null;
8 |
9 | previous: LRUItem | null = null;
10 |
11 | constructor(public node: Node) {}
12 | }
13 |
14 | /**
15 | * A doubly-linked-list of the least recently used elements.
16 | */
17 | export class LRU
18 | {
19 | // the least recently used item
20 | first: LRUItem | null = null;
21 |
22 | // the most recently used item
23 | last: LRUItem | null = null;
24 |
25 | numPoints: number = 0;
26 |
27 | private items = new Map();
28 |
29 | constructor(public pointBudget: number = 1_000_000) {}
30 |
31 | get size(): number
32 | {
33 | return this.items.size;
34 | }
35 |
36 | has(node: Node): boolean
37 | {
38 | return this.items.has(node.id);
39 | }
40 |
41 | /**
42 | * Makes the specified the most recently used item. if the list does not contain node, it will
43 | * be added.
44 | */
45 | touch(node: Node)
46 | {
47 | if (!node.loaded)
48 | {
49 | return;
50 | }
51 |
52 | const item = this.items.get(node.id);
53 | if (item)
54 | {
55 | this.touchExisting(item);
56 | }
57 | else
58 | {
59 | this.addNew(node);
60 | }
61 | }
62 |
63 | private addNew(node: Node): void
64 | {
65 | const item = new LRUItem(node);
66 | item.previous = this.last;
67 | this.last = item;
68 | if (item.previous)
69 | {
70 | item.previous.next = item;
71 | }
72 |
73 | if (!this.first)
74 | {
75 | this.first = item;
76 | }
77 |
78 | this.items.set(node.id, item);
79 | this.numPoints += node.numPoints;
80 | }
81 |
82 | private touchExisting(item: LRUItem): void
83 | {
84 | if (!item.previous)
85 | {
86 | // handle touch on first element
87 | if (item.next)
88 | {
89 | this.first = item.next;
90 | this.first.previous = null;
91 | item.previous = this.last;
92 | item.next = null;
93 | this.last = item;
94 |
95 | if (item.previous)
96 | {
97 | item.previous.next = item;
98 | }
99 | }
100 | }
101 | else if (!item.next)
102 | {
103 | // handle touch on last element
104 | }
105 | else
106 | {
107 | // handle touch on any other element
108 | item.previous.next = item.next;
109 | item.next.previous = item.previous;
110 | item.previous = this.last;
111 | item.next = null;
112 | this.last = item;
113 |
114 | if (item.previous)
115 | {
116 | item.previous.next = item;
117 | }
118 | }
119 | }
120 |
121 | remove(node: Node)
122 | {
123 | const item = this.items.get(node.id);
124 | if (!item)
125 | {
126 | return;
127 | }
128 |
129 | if (this.items.size === 1)
130 | {
131 | this.first = null;
132 | this.last = null;
133 | }
134 | else
135 | {
136 | if (!item.previous)
137 | {
138 | this.first = item.next;
139 | this.first!.previous = null;
140 | }
141 |
142 | if (!item.next)
143 | {
144 | this.last = item.previous;
145 | this.last!.next = null;
146 | }
147 |
148 | if (item.previous && item.next)
149 | {
150 | item.previous.next = item.next;
151 | item.next.previous = item.previous;
152 | }
153 | }
154 |
155 | this.items.delete(node.id);
156 | this.numPoints -= node.numPoints;
157 | }
158 |
159 | getLRUItem(): Node | undefined
160 | {
161 | return this.first ? this.first.node : undefined;
162 | }
163 |
164 | freeMemory(): void
165 | {
166 | if (this.items.size <= 1)
167 | {
168 | return;
169 | }
170 |
171 | while (this.numPoints > this.pointBudget * 2)
172 | {
173 | const node = this.getLRUItem();
174 | if (node)
175 | {
176 | this.disposeSubtree(node);
177 | }
178 | }
179 | }
180 |
181 | disposeSubtree(node: Node): void
182 | {
183 | // Collect all the nodes which are to be disposed and removed.
184 | const nodesToDispose: Node[] = [node];
185 | node.traverse((n) =>
186 | {
187 | if (n.loaded)
188 | {
189 | nodesToDispose.push(n);
190 | }
191 | });
192 |
193 | // Dispose of all the nodes in one go.
194 | for (const n of nodesToDispose)
195 | {
196 | n.dispose();
197 | this.remove(n);
198 | }
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/source/utils/math.ts:
--------------------------------------------------------------------------------
1 | export function clamp(value: number, min: number, max: number): number
2 | {
3 | return Math.min(Math.max(min, value), max);
4 | }
5 |
--------------------------------------------------------------------------------
/source/utils/utils.ts:
--------------------------------------------------------------------------------
1 | import {IPointCloudTreeNode} from '../types';
2 |
3 | /**
4 | * Check if running on browser or node.js.
5 | *
6 | * @returns True if running on browser.
7 | */
8 | export function isBrowser() {
9 | return typeof window !== 'undefined' && typeof document !== "undefined";
10 | }
11 |
12 | /**
13 | * Returns the index of the node in the hierarchy from its name.
14 | *
15 | * @param name The name of the node.
16 | */
17 | export function getIndexFromName(name: string)
18 | {
19 | return parseInt(name.charAt(name.length - 1), 10);
20 | }
21 |
22 | /**
23 | * When passed to `[].sort`, sorts the array by level and index: r, r0, r3, r4, r01, r07, r30, ...
24 | *
25 | * @param a The first node.
26 | * @param b The second node.
27 | */
28 | export function byLevelAndIndex(a: IPointCloudTreeNode, b: IPointCloudTreeNode)
29 | {
30 | const na = a.name;
31 | const nb = b.name;
32 | if (na.length !== nb.length)
33 | {
34 | return na.length - nb.length;
35 | }
36 | else if (na < nb)
37 | {
38 | return -1;
39 | }
40 | else if (na > nb)
41 | {
42 | return 1;
43 | }
44 | else
45 | {
46 | return 0;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/source/version.ts:
--------------------------------------------------------------------------------
1 | export class Version
2 | {
3 | version: string;
4 |
5 | versionMajor: number;
6 |
7 | versionMinor: number = 0;
8 |
9 | constructor(version: string)
10 | {
11 | this.version = version;
12 |
13 | const vmLength = version.indexOf('.') === -1 ? version.length : version.indexOf('.');
14 | this.versionMajor = parseInt(version.substr(0, vmLength), 10);
15 | this.versionMinor = parseInt(version.substr(vmLength + 1), 10);
16 | if (isNaN(this.versionMinor))
17 | {
18 | this.versionMinor = 0;
19 | }
20 | }
21 |
22 | newerThan(version: string): boolean
23 | {
24 | const v = new Version(version);
25 |
26 | if (this.versionMajor > v.versionMajor)
27 | {
28 | return true;
29 | }
30 | else if (this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor)
31 | {
32 | return true;
33 | }
34 | else
35 | {
36 | return false;
37 | }
38 | }
39 |
40 | equalOrHigher(version: string): boolean
41 | {
42 | const v = new Version(version);
43 |
44 | if (this.versionMajor > v.versionMajor)
45 | {
46 | return true;
47 | }
48 | else if (this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor)
49 | {
50 | return true;
51 | }
52 | else
53 | {
54 | return false;
55 | }
56 | }
57 |
58 | upTo(version: string): boolean
59 | {
60 | return !this.newerThan(version);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/source/workers/GreyhoundBinaryDecoderWorker.js:
--------------------------------------------------------------------------------
1 | function CustomView(buffer)
2 | {
3 | this.buffer = buffer;
4 | this.u8 = new Uint8Array(buffer);
5 |
6 | const tmp = new ArrayBuffer(4);
7 | const tmpf = new Float32Array(tmp);
8 | const tmpu8 = new Uint8Array(tmp);
9 |
10 | this.getUint32 = function(i)
11 | {
12 | return this.u8[i + 3] << 24 | this.u8[i + 2] << 16 | this.u8[i + 1] << 8 | this.u8[i];
13 | };
14 |
15 | this.getUint16 = function(i)
16 | {
17 | return this.u8[i + 1] << 8 | this.u8[i];
18 | };
19 |
20 | this.getFloat = function(i)
21 | {
22 | tmpu8[0] = this.u8[i + 0];
23 | tmpu8[1] = this.u8[i + 1];
24 | tmpu8[2] = this.u8[i + 2];
25 | tmpu8[3] = this.u8[i + 3];
26 |
27 | return tmpf[0];
28 | };
29 |
30 | this.getUint8 = function(i)
31 | {
32 | return this.u8[i];
33 | };
34 | }
35 |
36 | const decompress = function(schema, input, numPoints)
37 | {
38 | const x = new Module.DynamicLASZip();
39 |
40 | const abInt = new Uint8Array(input);
41 | const buf = Module._malloc(input.byteLength);
42 |
43 | Module.HEAPU8.set(abInt, buf);
44 | x.open(buf, input.byteLength);
45 |
46 | let pointSize = 0;
47 |
48 | schema.forEach(function(f)
49 | {
50 | pointSize += f.size;
51 | if (f.type === 'floating') {x.addFieldFloating(f.size);}
52 | else if (f.type === 'unsigned') {x.addFieldUnsigned(f.size);}
53 | else if (f.type === 'signed') {x.addFieldSigned(f.size);}
54 | else
55 | {
56 | if (PRODUCTION)
57 | {
58 | throw new Error();
59 | }
60 | else
61 | {
62 | throw new Error('Unrecognized field desc:', f);
63 | }
64 | }
65 | });
66 |
67 | const out = Module._malloc(numPoints * pointSize);
68 |
69 | for (let i = 0; i < numPoints; i++)
70 | {
71 | x.getPoint(out + i * pointSize);
72 | }
73 |
74 | const ret = new Uint8Array(numPoints * pointSize);
75 | ret.set(Module.HEAPU8.subarray(out, out + numPoints * pointSize));
76 |
77 | Module._free(out);
78 | Module._free(buf);
79 |
80 | return ret.buffer;
81 | };
82 |
83 |
84 | onmessage = function(event)
85 | {
86 | const NUM_POINTS_BYTES = 4;
87 |
88 | let buffer = event.data.buffer;
89 | const numPoints = new DataView(
90 | buffer,
91 | buffer.byteLength - NUM_POINTS_BYTES,
92 | NUM_POINTS_BYTES
93 | ).getUint32(0, true);
94 | buffer = buffer.slice(0, buffer.byteLength - NUM_POINTS_BYTES);
95 | buffer = decompress(event.data.schema, buffer, numPoints);
96 |
97 | const pointAttributes = event.data.pointAttributes;
98 | const cv = new CustomView(buffer);
99 | const version = new Potree.Version(event.data.version);
100 | const nodeOffset = event.data.offset;
101 | const scale = event.data.scale;
102 |
103 | const tightBoxMin = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
104 | const tightBoxMax = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
105 | const mean = [0, 0, 0];
106 |
107 | const attributeBuffers = {};
108 | let inOffset = 0;
109 | for (const pointAttribute of pointAttributes.attributes)
110 | {
111 | if (pointAttribute.name === Potree.PointAttribute.POSITION_CARTESIAN.name)
112 | {
113 | const buff = new ArrayBuffer(numPoints * 4 * 3);
114 | const positions = new Float32Array(buff);
115 |
116 | for (let j = 0; j < numPoints; j++)
117 | {
118 | const ux = cv.getUint32(inOffset + j * pointAttributes.byteSize + 0);
119 | const uy = cv.getUint32(inOffset + j * pointAttributes.byteSize + 4);
120 | const uz = cv.getUint32(inOffset + j * pointAttributes.byteSize + 8);
121 |
122 | const x = scale * ux + nodeOffset[0];
123 | const y = scale * uy + nodeOffset[1];
124 | const z = scale * uz + nodeOffset[2];
125 |
126 | positions[3 * j + 0] = x;
127 | positions[3 * j + 1] = y;
128 | positions[3 * j + 2] = z;
129 |
130 | mean[0] += x / numPoints;
131 | mean[1] += y / numPoints;
132 | mean[2] += z / numPoints;
133 |
134 | tightBoxMin[0] = Math.min(tightBoxMin[0], x);
135 | tightBoxMin[1] = Math.min(tightBoxMin[1], y);
136 | tightBoxMin[2] = Math.min(tightBoxMin[2], z);
137 |
138 | tightBoxMax[0] = Math.max(tightBoxMax[0], x);
139 | tightBoxMax[1] = Math.max(tightBoxMax[1], y);
140 | tightBoxMax[2] = Math.max(tightBoxMax[2], z);
141 | }
142 |
143 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
144 | }
145 | else if (pointAttribute.name === Potree.PointAttribute.COLOR_PACKED.name)
146 | {
147 | const buff = new ArrayBuffer(numPoints * 4);
148 | const colors = new Uint8Array(buff);
149 | const div = event.data.normalize.color ? 256 : 1;
150 |
151 | for (let j = 0; j < numPoints; j++)
152 | {
153 | const r = cv.getUint16(inOffset + j * pointAttributes.byteSize + 0) / div;
154 | const g = cv.getUint16(inOffset + j * pointAttributes.byteSize + 2) / div;
155 | const b = cv.getUint16(inOffset + j * pointAttributes.byteSize + 4) / div;
156 |
157 | colors[4 * j + 0] = r;
158 | colors[4 * j + 1] = g;
159 | colors[4 * j + 2] = b;
160 | }
161 |
162 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
163 | }
164 | else if (pointAttribute.name === Potree.PointAttribute.INTENSITY.name)
165 | {
166 | const buff = new ArrayBuffer(numPoints * 4);
167 | const intensities = new Float32Array(buff);
168 |
169 | for (let j = 0; j < numPoints; j++)
170 | {
171 | const intensity = cv.getUint16(inOffset + j * pointAttributes.byteSize, true);
172 | intensities[j] = intensity;
173 | }
174 |
175 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
176 | }
177 | else if (pointAttribute.name === Potree.PointAttribute.CLASSIFICATION.name)
178 | {
179 | const buff = new ArrayBuffer(numPoints);
180 | const classifications = new Uint8Array(buff);
181 |
182 | for (let j = 0; j < numPoints; j++)
183 | {
184 | const classification = cv.getUint8(inOffset + j * pointAttributes.byteSize);
185 | classifications[j] = classification;
186 | }
187 |
188 | attributeBuffers[pointAttribute.name] = {buffer: buff, attribute: pointAttribute};
189 | }
190 |
191 | inOffset += pointAttribute.byteSize;
192 | }
193 |
194 | {
195 | // add indices
196 | const buff = new ArrayBuffer(numPoints * 4);
197 | const indices = new Uint32Array(buff);
198 |
199 | for (let i = 0; i < numPoints; i++)
200 | {
201 | indices[i] = i;
202 | }
203 |
204 | attributeBuffers[Potree.PointAttribute.INDICES.name] = {
205 | buffer: buff,
206 | attribute: Potree.PointAttribute.INDICES
207 | };
208 | }
209 |
210 | const message = {
211 | numPoints: numPoints,
212 | mean: mean,
213 | attributeBuffers: attributeBuffers,
214 | tightBoundingBox: {min: tightBoxMin, max: tightBoxMax}
215 | };
216 |
217 | const transferables = [];
218 | for (const property in message.attributeBuffers)
219 | {
220 | transferables.push(message.attributeBuffers[property].buffer);
221 | }
222 | transferables.push(buffer);
223 |
224 | postMessage(message, transferables);
225 | };
226 |
--------------------------------------------------------------------------------
/source/workers/LazLoaderWorker.js:
--------------------------------------------------------------------------------
1 |
2 | let instance = null; // laz-perf instance
3 |
4 | function readAs(buf, Type, offset, count)
5 | {
6 | count = count === undefined || count === 0 ? 1 : count;
7 | const sub = buf.slice(offset, offset + Type.BYTES_PER_ELEMENT * count);
8 |
9 | const r = new Type(sub);
10 | if (count === undefined || count === 1)
11 | {
12 | return r[0];
13 | }
14 |
15 | const ret = [];
16 | for (let i = 0; i < count; i++)
17 | {
18 | ret.push(r[i]);
19 | }
20 |
21 | return ret;
22 | }
23 |
24 | function parseLASHeader(arraybuffer)
25 | {
26 | const o = {};
27 |
28 | o.pointsOffset = readAs(arraybuffer, Uint32Array, 32 * 3);
29 | o.pointsFormatId = readAs(arraybuffer, Uint8Array, 32 * 3 + 8);
30 | o.pointsStructSize = readAs(arraybuffer, Uint16Array, 32 * 3 + 8 + 1);
31 | o.pointsCount = readAs(arraybuffer, Uint32Array, 32 * 3 + 11);
32 |
33 | let start = 32 * 3 + 35;
34 | o.scale = readAs(arraybuffer, Float64Array, start, 3);
35 | start += 24; // 8*3
36 | o.offset = readAs(arraybuffer, Float64Array, start, 3);
37 | start += 24;
38 |
39 | const bounds = readAs(arraybuffer, Float64Array, start, 6);
40 | start += 48; // 8*6;
41 | o.maxs = [bounds[0], bounds[2], bounds[4]];
42 | o.mins = [bounds[1], bounds[3], bounds[5]];
43 |
44 | return o;
45 | }
46 |
47 | function handleEvent(msg)
48 | {
49 | switch (msg.type)
50 | {
51 | case 'open':
52 | try
53 | {
54 | instance = new Module.LASZip();
55 | const abInt = new Uint8Array(msg.arraybuffer);
56 | const buf = Module._malloc(msg.arraybuffer.byteLength);
57 |
58 | instance.arraybuffer = msg.arraybuffer;
59 | instance.buf = buf;
60 | Module.HEAPU8.set(abInt, buf);
61 | instance.open(buf, msg.arraybuffer.byteLength);
62 |
63 | instance.readOffset = 0;
64 |
65 | postMessage({type: 'open', status: 1});
66 | }
67 | catch (e)
68 | {
69 | postMessage({type: 'open', status: 0, details: e});
70 | }
71 | break;
72 |
73 | case 'header':
74 | if (!instance)
75 | {
76 | if (PRODUCTION)
77 | {
78 | throw new Error();
79 | }
80 | else
81 | {
82 | throw new Error('You need to open the file before trying to read header');
83 | }
84 | }
85 |
86 | const header = parseLASHeader(instance.arraybuffer);
87 | header.pointsFormatId &= 0x3f;
88 | instance.header = header;
89 | postMessage({type: 'header', status: 1, header: header});
90 | break;
91 |
92 | case 'read':
93 | if (!instance)
94 | {
95 | if (PRODUCTION)
96 | {
97 | throw new Error();
98 | }
99 | else
100 | {
101 | throw new Error('You need to open the file before trying to read stuff');
102 | }
103 | }
104 |
105 | // msg.start
106 | const count = msg.count;
107 | const skip = msg.skip;
108 | const o = instance;
109 |
110 | if (!o.header)
111 | {
112 | if (PRODUCTION)
113 | {
114 | throw new Error();
115 | }
116 | else
117 | {
118 | throw new Error(
119 | 'You need to query header before reading, I maintain state that way, sorry :('
120 | );
121 | }
122 | }
123 |
124 | const pointsToRead = Math.min(count * skip, o.header.pointsCount - o.readOffset);
125 | const bufferSize = Math.ceil(pointsToRead / skip);
126 | let pointsRead = 0;
127 |
128 | const thisBuf = new Uint8Array(bufferSize * o.header.pointsStructSize);
129 | const bufRead = Module._malloc(o.header.pointsStructSize);
130 | for (let i = 0; i < pointsToRead; i++)
131 | {
132 | o.getPoint(bufRead);
133 |
134 | if (i % skip === 0)
135 | {
136 | const a = new Uint8Array(Module.HEAPU8.buffer, bufRead, o.header.pointsStructSize);
137 | thisBuf.set(a, pointsRead * o.header.pointsStructSize, o.header.pointsStructSize);
138 | pointsRead++;
139 | }
140 |
141 | o.readOffset++;
142 | }
143 |
144 | postMessage({
145 | type: 'header',
146 | status: 1,
147 | buffer: thisBuf.buffer,
148 | count: pointsRead,
149 | hasMoreData: o.readOffset < o.header.pointsCount
150 | });
151 |
152 | break;
153 |
154 | case 'close':
155 | if (instance !== null)
156 | {
157 | instance.delete();
158 | instance = null;
159 | }
160 | postMessage({type: 'close', status: 1});
161 | break;
162 | }
163 | }
164 |
165 | onmessage = function(event)
166 | {
167 | try
168 | {
169 | handleEvent(event.data);
170 | }
171 | catch (e)
172 | {
173 | postMessage({type: event.data.type, status: 0, details: e});
174 | }
175 | };
176 |
--------------------------------------------------------------------------------
/source/workers/binary-decoder-worker-internal.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Adapted from Potree.js http://potree.org
3 | * Potree License: https://github.com/potree/potree/blob/1.5/LICENSE
4 | */
5 |
6 | import {
7 | IPointAttribute,
8 | IPointAttributes,
9 | PointAttributeName,
10 | POINT_ATTRIBUTES
11 | } from '../point-attributes';
12 | import {Version} from '../version';
13 | import {CustomArrayView} from './custom-array-view';
14 |
15 | // IE11 does not have Math.sign(), this has been adapted from CoreJS es6.math.sign.js for TypeScript
16 | const mathSign =
17 | Math.sign ||
18 | function(x: number): number
19 | {
20 | // tslint:disable-next-line:triple-equals
21 | return (x = Number(x)) == 0 || x != x ? x : x < 0 ? -1 : 1;
22 | };
23 |
24 | interface DecodedAttribute {
25 | buffer: ArrayBuffer;
26 | attribute: IPointAttribute;
27 | }
28 |
29 | interface Ctx {
30 | attributeBuffers: Record;
31 | currentOffset: number;
32 | data: CustomArrayView;
33 | mean: [number, number, number];
34 | nodeOffset: [number, number, number];
35 | numPoints: number;
36 | pointAttributes: IPointAttributes;
37 | scale: number;
38 | tightBoxMax: [number, number, number];
39 | tightBoxMin: [number, number, number];
40 | transferables: ArrayBuffer[];
41 | version: Version;
42 | }
43 |
44 | export function handleMessage(event: MessageEvent)
45 | {
46 | const buffer = event.data.buffer;
47 | const pointAttributes: IPointAttributes = event.data.pointAttributes;
48 |
49 | const ctx: Ctx = {
50 | attributeBuffers: {},
51 | currentOffset: 0,
52 | data: new CustomArrayView(buffer),
53 | mean: [0, 0, 0],
54 | nodeOffset: event.data.offset,
55 | numPoints: event.data.buffer.byteLength / pointAttributes.byteSize,
56 | pointAttributes: pointAttributes,
57 | scale: event.data.scale,
58 | tightBoxMax: [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
59 | tightBoxMin: [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
60 | transferables: [],
61 | version: new Version(event.data.version)
62 | };
63 |
64 | for (const pointAttribute of ctx.pointAttributes.attributes)
65 | {
66 | decodeAndAddAttribute(pointAttribute, ctx);
67 | ctx.currentOffset += pointAttribute.byteSize;
68 | }
69 |
70 | const indices = new ArrayBuffer(ctx.numPoints * 4);
71 | const iIndices = new Uint32Array(indices);
72 | for (let i = 0; i < ctx.numPoints; i++)
73 | {
74 | iIndices[i] = i;
75 | }
76 |
77 | if (!ctx.attributeBuffers[PointAttributeName.CLASSIFICATION])
78 | {
79 | addEmptyClassificationBuffer(ctx);
80 | }
81 |
82 | const message = {
83 | buffer: buffer,
84 | mean: ctx.mean,
85 | attributeBuffers: ctx.attributeBuffers,
86 | tightBoundingBox: {min: ctx.tightBoxMin, max: ctx.tightBoxMax},
87 | indices: indices
88 | };
89 |
90 | // console.log('old', message)
91 | postMessage(message, ctx.transferables as any);
92 | }
93 |
94 | function addEmptyClassificationBuffer(ctx: Ctx): void
95 | {
96 | const buffer = new ArrayBuffer(ctx.numPoints * 4);
97 | const classifications = new Float32Array(buffer);
98 |
99 | for (let i = 0; i < ctx.numPoints; i++)
100 | {
101 | classifications[i] = 0;
102 | }
103 |
104 | ctx.attributeBuffers[PointAttributeName.CLASSIFICATION] = {
105 | buffer: buffer,
106 | attribute: POINT_ATTRIBUTES.CLASSIFICATION
107 | };
108 | }
109 |
110 | function decodeAndAddAttribute(attribute: IPointAttribute, ctx: Ctx): void
111 | {
112 | const decodedAttribute = decodePointAttribute(attribute, ctx);
113 | if (decodedAttribute === undefined)
114 | {
115 | return;
116 | }
117 |
118 | ctx.attributeBuffers[decodedAttribute.attribute.name] = decodedAttribute;
119 | ctx.transferables.push(decodedAttribute.buffer);
120 | }
121 |
122 | function decodePointAttribute(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute | undefined
123 | {
124 | switch (attribute.name)
125 | {
126 | case PointAttributeName.POSITION_CARTESIAN:
127 | return decodePositionCartesian(attribute, ctx);
128 | case PointAttributeName.COLOR_PACKED:
129 | return decodeColor(attribute, ctx);
130 | case PointAttributeName.INTENSITY:
131 | return decodeIntensity(attribute, ctx);
132 | case PointAttributeName.CLASSIFICATION:
133 | return decodeClassification(attribute, ctx);
134 | case PointAttributeName.NORMAL_SPHEREMAPPED:
135 | return decodeNormalSphereMapped(attribute, ctx);
136 | case PointAttributeName.NORMAL_OCT16:
137 | return decodeNormalOct16(attribute, ctx);
138 | case PointAttributeName.NORMAL:
139 | return decodeNormal(attribute, ctx);
140 | default:
141 | return undefined;
142 | }
143 | }
144 |
145 | function decodePositionCartesian(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
146 | {
147 | const buffer = new ArrayBuffer(ctx.numPoints * 4 * 3);
148 | const positions = new Float32Array(buffer);
149 |
150 | for (let i = 0; i < ctx.numPoints; i++)
151 | {
152 | let x;
153 | let y;
154 | let z;
155 |
156 | if (ctx.version.newerThan('1.3'))
157 | {
158 | x = ctx.data.getUint32(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 0) * ctx.scale;
159 | y = ctx.data.getUint32(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 4) * ctx.scale;
160 | z = ctx.data.getUint32(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 8) * ctx.scale;
161 | }
162 | else
163 | {
164 | x = ctx.data.getFloat32(i * ctx.pointAttributes.byteSize + 0) + ctx.nodeOffset[0];
165 | y = ctx.data.getFloat32(i * ctx.pointAttributes.byteSize + 4) + ctx.nodeOffset[1];
166 | z = ctx.data.getFloat32(i * ctx.pointAttributes.byteSize + 8) + ctx.nodeOffset[2];
167 | }
168 |
169 | positions[3 * i + 0] = x;
170 | positions[3 * i + 1] = y;
171 | positions[3 * i + 2] = z;
172 |
173 | ctx.mean[0] += x / ctx.numPoints;
174 | ctx.mean[1] += y / ctx.numPoints;
175 | ctx.mean[2] += z / ctx.numPoints;
176 |
177 | ctx.tightBoxMin[0] = Math.min(ctx.tightBoxMin[0], x);
178 | ctx.tightBoxMin[1] = Math.min(ctx.tightBoxMin[1], y);
179 | ctx.tightBoxMin[2] = Math.min(ctx.tightBoxMin[2], z);
180 |
181 | ctx.tightBoxMax[0] = Math.max(ctx.tightBoxMax[0], x);
182 | ctx.tightBoxMax[1] = Math.max(ctx.tightBoxMax[1], y);
183 | ctx.tightBoxMax[2] = Math.max(ctx.tightBoxMax[2], z);
184 | }
185 |
186 | return {buffer: buffer, attribute: attribute};
187 | }
188 |
189 | function decodeColor(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
190 | {
191 | const buffer = new ArrayBuffer(ctx.numPoints * 3);
192 | const colors = new Uint8Array(buffer);
193 |
194 | for (let i = 0; i < ctx.numPoints; i++)
195 | {
196 | colors[3 * i + 0] = ctx.data.getUint8(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 0);
197 | colors[3 * i + 1] = ctx.data.getUint8(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 1);
198 | colors[3 * i + 2] = ctx.data.getUint8(ctx.currentOffset + i * ctx.pointAttributes.byteSize + 2);
199 | }
200 |
201 | return {buffer: buffer, attribute: attribute};
202 | }
203 |
204 | function decodeIntensity(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
205 | {
206 | const buffer = new ArrayBuffer(ctx.numPoints * 4);
207 | const intensities = new Float32Array(buffer);
208 |
209 | for (let i = 0; i < ctx.numPoints; i++)
210 | {
211 | intensities[i] = ctx.data.getUint16(ctx.currentOffset + i * ctx.pointAttributes.byteSize);
212 | }
213 |
214 | return {buffer: buffer, attribute: attribute};
215 | }
216 |
217 | function decodeClassification(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
218 | {
219 | const buffer = new ArrayBuffer(ctx.numPoints);
220 | const classifications = new Uint8Array(buffer);
221 |
222 | for (let j = 0; j < ctx.numPoints; j++)
223 | {
224 | classifications[j] = ctx.data.getUint8(ctx.currentOffset + j * ctx.pointAttributes.byteSize);
225 | }
226 |
227 | return {buffer: buffer, attribute: attribute};
228 | }
229 |
230 | function decodeNormalSphereMapped(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
231 | {
232 | const buffer = new ArrayBuffer(ctx.numPoints * 4 * 3);
233 | const normals = new Float32Array(buffer);
234 |
235 | for (let j = 0; j < ctx.numPoints; j++)
236 | {
237 | const bx = ctx.data.getUint8(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 0);
238 | const by = ctx.data.getUint8(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 1);
239 |
240 | const ex = bx / 255;
241 | const ey = by / 255;
242 |
243 | let nx = ex * 2 - 1;
244 | let ny = ey * 2 - 1;
245 | let nz = 1;
246 | const nw = -1;
247 |
248 | const l = nx * -nx + ny * -ny + nz * -nw;
249 | nz = l;
250 | nx = nx * Math.sqrt(l);
251 | ny = ny * Math.sqrt(l);
252 |
253 | nx = nx * 2;
254 | ny = ny * 2;
255 | nz = nz * 2 - 1;
256 |
257 | normals[3 * j + 0] = nx;
258 | normals[3 * j + 1] = ny;
259 | normals[3 * j + 2] = nz;
260 | }
261 |
262 | return {buffer: buffer, attribute: attribute};
263 | }
264 |
265 | function decodeNormalOct16(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
266 | {
267 | const buff = new ArrayBuffer(ctx.numPoints * 4 * 3);
268 | const normals = new Float32Array(buff);
269 |
270 | for (let j = 0; j < ctx.numPoints; j++)
271 | {
272 | const bx = ctx.data.getUint8(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 0);
273 | const by = ctx.data.getUint8(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 1);
274 |
275 | const u = bx / 255 * 2 - 1;
276 | const v = by / 255 * 2 - 1;
277 |
278 | let z = 1 - Math.abs(u) - Math.abs(v);
279 |
280 | let x = 0;
281 | let y = 0;
282 | if (z >= 0)
283 | {
284 | x = u;
285 | y = v;
286 | }
287 | else
288 | {
289 | x = -(v / mathSign(v) - 1) / mathSign(u);
290 | y = -(u / mathSign(u) - 1) / mathSign(v);
291 | }
292 |
293 | const length = Math.sqrt(x * x + y * y + z * z);
294 | x = x / length;
295 | y = y / length;
296 | z = z / length;
297 |
298 | normals[3 * j + 0] = x;
299 | normals[3 * j + 1] = y;
300 | normals[3 * j + 2] = z;
301 | }
302 |
303 | return {buffer: buff, attribute: attribute};
304 | }
305 |
306 | function decodeNormal(attribute: IPointAttribute, ctx: Ctx): DecodedAttribute
307 | {
308 | const buffer = new ArrayBuffer(ctx.numPoints * 4 * 3);
309 | const normals = new Float32Array(buffer);
310 |
311 | for (let j = 0; j < ctx.numPoints; j++)
312 | {
313 | const x = ctx.data.getFloat32(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 0);
314 | const y = ctx.data.getFloat32(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 4);
315 | const z = ctx.data.getFloat32(ctx.currentOffset + j * ctx.pointAttributes.byteSize + 8);
316 |
317 | normals[3 * j + 0] = x;
318 | normals[3 * j + 1] = y;
319 | normals[3 * j + 2] = z;
320 | }
321 |
322 | return {buffer: buffer, attribute: attribute};
323 | }
324 |
--------------------------------------------------------------------------------
/source/workers/binary-decoder.worker.js:
--------------------------------------------------------------------------------
1 | import {handleMessage} from './binary-decoder-worker-internal.ts';
2 |
3 | onmessage = handleMessage;
4 |
--------------------------------------------------------------------------------
/source/workers/custom-array-view.ts:
--------------------------------------------------------------------------------
1 | export class CustomArrayView
2 | {
3 | private u8: Uint8Array;
4 |
5 | private tmp = new ArrayBuffer(4);
6 |
7 | private tmpf = new Float32Array(this.tmp);
8 |
9 | private tmpu8 = new Uint8Array(this.tmp);
10 |
11 | constructor(buffer: ArrayBuffer)
12 | {
13 | this.u8 = new Uint8Array(buffer);
14 | }
15 |
16 | getUint32(i: number)
17 | {
18 | return this.u8[i + 3] << 24 | this.u8[i + 2] << 16 | this.u8[i + 1] << 8 | this.u8[i];
19 | }
20 |
21 | getUint16(i: number): number
22 | {
23 | return this.u8[i + 1] << 8 | this.u8[i];
24 | }
25 |
26 | getFloat32(i: number): number
27 | {
28 | const tmpu8 = this.tmpu8;
29 | const u8 = this.u8;
30 | const tmpf = this.tmpf;
31 |
32 | tmpu8[0] = u8[i + 0];
33 | tmpu8[1] = u8[i + 1];
34 | tmpu8[2] = u8[i + 2];
35 | tmpu8[3] = u8[i + 3];
36 |
37 | return tmpf[0];
38 | }
39 |
40 | getUint8(i: number): number
41 | {
42 | return this.u8[i];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./source/",
5 | "sourceMap": true,
6 | "declaration": true,
7 | "declarationDir": "dist",
8 | "module": "es2020",
9 | "moduleResolution": "node",
10 | "strict": false,
11 | "noImplicitAny": false,
12 | "noImplicitReturns": false,
13 | "noImplicitThis": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": false,
16 | "alwaysStrict": false,
17 | "target": "es2020",
18 | "experimentalDecorators": true,
19 | "typeRoots": ["node_modules/@types"],
20 | "lib": ["es2017", "dom"],
21 | "plugins": []
22 | },
23 | "include": ["source/**/*.ts"],
24 | "exclude": ["node_modules"]
25 | }
26 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | export default {
4 | entry: './source/index.ts',
5 | stats: {
6 | children: true,
7 | },
8 | experiments: {
9 | outputModule: true,
10 | },
11 | output: {
12 | path: path.resolve('dist'),
13 | filename: 'index.js',
14 | library: {
15 | type: 'module',
16 | },
17 | },
18 | resolve: {
19 | extensions: ['.tsx', '.ts', '.js'],
20 | },
21 | plugins: [],
22 | externals: ['three'],
23 | module: {
24 | rules: [
25 | {
26 | test: /\.worker\.js$/,
27 | loader: 'worker-loader',
28 | options: {inline: 'no-fallback'},
29 | },
30 | {
31 | test: /\.tsx?$/,
32 | loader: 'ts-loader',
33 | exclude: /node_modules/,
34 | },
35 | {
36 | test: /\.(vs|fs)$/,
37 | loader: 'raw-loader',
38 | options: {
39 | esModule: true,
40 | },
41 | }
42 | ],
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/webpack.example.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import HtmlPlugin from 'html-webpack-plugin';
3 | import CopyPlugin from 'copy-webpack-plugin';
4 | import config from './webpack.config.js';
5 |
6 | export default {
7 | context: path.resolve('./example'),
8 | entry: './main.ts',
9 | output: {
10 | filename: 'example.bundle.js',
11 | path: path.resolve('build'),
12 | },
13 | devtool: 'source-map',
14 | devServer: {
15 | compress: true,
16 | port: 5000,
17 | },
18 | resolve: {
19 | extensions: ['.ts', '.tsx', '.js'],
20 | },
21 | module: {
22 | rules: config.module.rules.concat([
23 | {
24 | test: /\.html$/,
25 | use: [
26 | {
27 | loader: 'html-loader',
28 | options: { minimize: true },
29 | },
30 | ],
31 | },
32 | {
33 | test: /\.css$/,
34 | use: ['style-loader', 'css-loader'],
35 | },
36 | ]),
37 | },
38 | plugins: [
39 | new CopyPlugin({
40 | patterns: [{
41 | from: "data",
42 | to: "data"
43 | }]
44 | }),
45 | new HtmlPlugin()
46 | ],
47 | };
48 |
--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
1 | import config from './webpack.config.js';
2 |
3 | export default Object.assign(config, {
4 | devtool: false,
5 | mode: 'production'
6 | });
7 |
--------------------------------------------------------------------------------