├── .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 | [![npm version](https://badge.fury.io/js/potree-core.svg)](https://badge.fury.io/js/potree-core) 4 | [![GitHub version](https://badge.fury.io/gh/tentone%2Fpotree-core.svg)](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 | --------------------------------------------------------------------------------