├── .gitignore ├── LICENSE ├── README.md ├── _geoerror.txt ├── _notes.txt ├── bs-config.js ├── images ├── goo3dtiles.png ├── state_building.png ├── terrain_tile.png ├── topo.png └── voxels.png ├── import-map.js ├── index.html ├── lib ├── Starter.css ├── Starter.js ├── meshes │ ├── DynLineMesh.js │ ├── GridAlternative.js │ ├── PlaneGrid.js │ └── ShapePointsMesh.js ├── misc │ ├── TextureCanvas.js │ └── TexturePass.js ├── thirdparty │ ├── OrbitControls.js │ ├── TransformControls.js │ ├── draco │ │ ├── draco_decoder_gltf.js │ │ └── draco_decoder_gltf.wasm │ ├── earcut.js │ ├── gl-matrix │ │ ├── common.js │ │ ├── index.js │ │ ├── mat2.js │ │ ├── mat2d.js │ │ ├── mat3.js │ │ ├── mat4.js │ │ ├── quat.js │ │ ├── quat2.js │ │ ├── vec2.js │ │ ├── vec3.js │ │ └── vec4.js │ ├── gltf2parser.es.js │ ├── map_style.json │ ├── perlin.js │ ├── three.module.min.js │ ├── threePostProcess │ │ ├── CopyShader.js │ │ ├── EffectComposer.js │ │ ├── LuminosityHighPassShader.js │ │ ├── MaskPass.js │ │ ├── Pass.js │ │ ├── RenderPass.js │ │ ├── ShaderPass.js │ │ └── UnrealBloomPass.js │ └── vector_tile_v2_1.proto ├── useThreeComposer.js ├── useThreeWebGL2.js └── useTransformControl.js ├── package.json ├── prototypes ├── 001_slippy_mechanics.html ├── 002_texture_tile.html ├── 003_building_tile.html ├── 004_3d_terrain_tile.html ├── 005_floating_origin.html ├── 006_vector_tile.html ├── 007_topographic.html ├── 007_topographic_bloom.html ├── 008_procedural_heightmap.html ├── 009_2dcanvas.html ├── 009_smooth_contours.html ├── 010_height_coloring.html ├── 011_cubes.html ├── 012_heightmap_stamp.html ├── 013_google_3dtiles_barebones.html ├── 013_google_3dtiles_search.html ├── 099_google_3dtiles_01.html ├── 099_mapgl.html ├── _template.html ├── info.txt ├── lib │ ├── CanvasOffscreen.js │ ├── FetchBatch.js │ ├── FetchQueue.js │ ├── MeshUtil.js │ ├── fetchArrayBuffer.js │ ├── fetchAsyncTexture.js │ ├── fetchImage.js │ ├── fetchJson.js │ └── fetchTexture.js ├── res │ └── heightmap.jpg └── sweepWave.txt └── tiles ├── 0_2.glb ├── root.json └── tileset.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | 84 | # Gatsby files 85 | .cache/ 86 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 87 | # https://nextjs.org/blog/next-9-1#public-directory-support 88 | # public 89 | 90 | # vuepress build output 91 | .vuepress/dist 92 | 93 | # Serverless directories 94 | .serverless/ 95 | 96 | # FuseBox cache 97 | .fusebox/ 98 | 99 | # DynamoDB Local files 100 | .dynamodb/ 101 | 102 | # TernJS port file 103 | .tern-port 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sketchpunk Labs 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiles & Mapping 2 | 3 | [![npm](https://img.shields.io/badge/Sponsor-donate-blue?style=flat-square&logo=github)](https://github.com/sponsors/sketchpunklabs) 4 | [![twitter](https://img.shields.io/badge/Twitter-profile-blue?style=flat-square&logo=twitter)](https://twitter.com/SketchpunkLabs) 5 | [![youtube](https://img.shields.io/badge/Youtube-subscribe-red?style=flat-square&logo=youtube)](https://youtube.com/c/sketchpunklabs) 6 | [![Patreon](https://img.shields.io/badge/Patreon-donate-red?style=flat-square&logo=youtube)](https://www.patreon.com/sketchpunk) 7 | 8 | Demos : https://sketchpunklabs.github.io/mapping/ 9 | 10 | ### Development Setup ### 11 | ``` 12 | git clone --depth=1 https://github.com/sketchpunklabs/mapping 13 | cd mapping 14 | npm install 15 | npm run start 16 | ``` -------------------------------------------------------------------------------- /_geoerror.txt: -------------------------------------------------------------------------------- 1 | //https://github.com/Geodan/mapbox-3dtiles/blob/master/Mapbox3DTiles.mjs 2 | 3 | let worldBox = this.box.clone().applyMatrix4(this.worldTransform); 4 | let dist = worldBox.distanceToPoint(cameraPosition); 5 | 6 | 7 | //console.log(`dist: ${dist}, geometricError: ${this.geometricError}`); 8 | // are we too far to render this tile? 9 | if (this.geometricError > 0.0 && dist > this.geometricError * 50.0) { 10 | this.unload(true); 11 | return; 12 | } 13 | //console.log(`camPos: ${cameraPosition.z}, dist: ${dist}, geometricError: ${this.geometricError}`); 14 | 15 | // should we load this tile? 16 | if (this.refine == 'REPLACE' && dist < this.geometricError * 20.0) { 17 | this.unload(false); 18 | } else { 19 | this.load(); 20 | } 21 | 22 | 23 | // should we load its children? 24 | for (let i=0; i 0.25) { 46 | this.load(); 47 | this.children.forEach(child => { 48 | child.checkLoad(camera); 49 | }); 50 | }*/ 51 | -------------------------------------------------------------------------------- /_notes.txt: -------------------------------------------------------------------------------- 1 | https://www.rfc-editor.org/rfc/rfc7946#page-6 2 | 3 | https://osmbuildings.org/documentation/data/ 4 | https://www.mapzen.com/blog/getting-crafty/ 5 | 6 | 7 | https://github.com/tangrams/tangram vector and 3d buildings 8 | https://github.com/kekscom/osmbuildings 2.5 Maps & 3d buildings 9 | https://github.com/OSMBuildings/OSMBuildings WebGL Viewer 10 | 11 | 12 | Which gives us a formula to calculate resolution at any given zoom: 13 | resolution = 156543.03 meters/pixel * cos(latitude) / (2 ^ zoomlevel) 14 | 15 | Some applications need to know a map scale, that is, how 1 cm on a screen translates to 1 cm of a map. 16 | scale = 1 : (screen_dpi * 1/0.0254 in/m * resolution) 17 | 18 | 19 | // size = tileSize * Math.pow( 2, zoom ); 20 | 21 | 22 | https://developer.tomtom.com/map-display-api/documentation/zoom-levels-and-tile-grid 23 | 24 | 25 | // https://learn.microsoft.com/en-us/azure/azure-maps/zoom-levels-and-tile-grid?tabs=csharp 26 | // Given latitude and longitude in degrees, and the level of detail, the pixel XY coordinates 27 | var sinLatitude = Math.sin(latitude * Math.PI/180); 28 | var pixelX = ((longitude + 180) / 360) * tileSize * Math.pow(2, zoom); 29 | var pixelY = (0.5 – Math.log((1 + sinLatitude) / (1 – sinLatitude)) / (4 * Math.PI)) * tileSize * Math.pow(2, zoom); 30 | 31 | var numberOfTilesWide = Math.pow(2, zoom); 32 | var numberOfTilesHigh = numberOfTilesWide; 33 | 34 | var tileX = Math.floor(pixelX / tileSize); 35 | var tileY = Math.floor(pixelY / tileSize); 36 | 37 | 38 | https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_2 39 | Given Longitude/latitude/zoom to tile numbers : 40 | n = 2 ^ zoom 41 | xtile = n * ((lon_deg + 180) / 360) 42 | ytile = n * (1 - (log(tan(lat_rad) + sec(lat_rad)) / π)) / 2 43 | 44 | Given Tile numbers to longitude/latitude : 45 | n = 2 ^ zoom 46 | lon_deg = xtile / n * 360.0 - 180.0 47 | lat_rad = arctan(sinh(π * (1 - 2 * ytile / n))) 48 | lat_deg = lat_rad * 180.0 / 49 | 50 | //////////////////////////////////////////////////////////////////////// 51 | Example for calculating number of tiles within given extent and zoom-level: 52 | function lon2tile(lon,zoom) { return (Math.floor((lon+180)/360*Math.pow(2,zoom))); } 53 | function lat2tile(lat,zoom) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); } 54 | 55 | Inverse process: 56 | 57 | function tile2long(x,z) { 58 | return (x/Math.pow(2,z)*360-180); 59 | } 60 | function tile2lat(y,z) { 61 | var n=Math.PI-2*Math.PI*y/Math.pow(2,z); 62 | return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); 63 | } 64 | 65 | var zoom = 9; 66 | var top_tile = lat2tile(north_edge, zoom); // eg.lat2tile(34.422, 9); 67 | var left_tile = lon2tile(west_edge, zoom); 68 | var bottom_tile = lat2tile(south_edge, zoom); 69 | var right_tile = lon2tile(east_edge, zoom); 70 | var width = Math.abs(left_tile - right_tile) + 1; 71 | var height = Math.abs(top_tile - bottom_tile) + 1; 72 | 73 | // total tiles 74 | var total_tiles = width * height; // -> eg. 377 75 | 76 | 77 | //////////////////////////////////////////////////////////////////////// 78 | class Tile { 79 | 80 | constructor (x, y, zoom) { 81 | this.x = x; 82 | this.y = y; 83 | this.zoom = zoom; 84 | this.key = [x, y, zoom].join(','); 85 | 86 | this.distance = Infinity; 87 | } 88 | 89 | // parent () { 90 | // return { 91 | // x: this.x / 2, 92 | // y: this.y / 2, 93 | // z: this.zoom - 1 94 | // }; 95 | // } 96 | 97 | // children () { 98 | // return [ 99 | // { x: this.x * 2, y: this.y * 2, z: this.zoom + 1 }, 100 | // { x: this.x * 2 + 1, y: this.y * 2, z: this.zoom + 1 }, 101 | // { x: this.x * 2, y: this.y * 2 + 1, z: this.zoom + 1 }, 102 | // { x: this.x * 2 + 1, y: this.y * 2 + 1, z: this.zoom + 1 } 103 | // ]; 104 | // } 105 | 106 | 107 | https://observablehq.com/@sw1227/calculating-pixel-size-m-of-map-tile 108 | // Calculate lat/lon of pixel (pX, pY) on tile tileZ/tileX/tileY 109 | function pixelOnTileToLatLon(pX, pY, tileZ, tileX, tileY) { 110 | const L = 85.05112878; 111 | const x = 256 * tileX + pX; 112 | const y = 256 * tileY + pY; 113 | 114 | const lon = 180 * (x / (1 << (tileZ + 7)) - 1); 115 | const lat = (180/Math.PI) * Math.asin(Math.tanh( 116 | - Math.PI / (1 << (tileZ + 7)) * y + Math.atanh(Math.sin(L * Math.PI/180)) 117 | )); 118 | return {lat: lat, lon: lon}; 119 | } 120 | 121 | function pixelSize(tileZ, tileX, tileY) { 122 | // North west / South east / Center 123 | const pNW =pixelOnTileToLatLon(0, 0, tileZ, tileX, tileY); 124 | const pSE = pixelOnTileToLatLon(255, 255, tileZ, tileX, tileY); 125 | const pCenter = pixelOnTileToLatLon(128, 128, tileZ, tileX, tileY); 126 | 127 | const deg2rad = deg => deg / 180 * Math.PI; 128 | 129 | return { 130 | min: resolution(deg2rad(pNW.lat), tileZ), 131 | max: resolution(deg2rad(pSE.lat), tileZ), 132 | center: resolution(deg2rad(pCenter.lat), tileZ) 133 | }; 134 | } 135 | 136 | https://openlayers.org/en/latest/examples/osm-vector-tiles.html 137 | 138 | https://cloud.maptiler.com/account/keys/ 139 | https://temp-mail.org/en/ 140 | cevewo8680@eilnews.com vbxVBX333# 141 | h3WICQANUooj1n2U0lPk 142 | 143 | https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=h3WICQANUooj1n2U0lPk 144 | https://api.maptiler.com/tiles/v3-openmaptiles/6/18/23.pbf?key=h3WICQANUooj1n2U0lPk 145 | https://api.maptiler.com/tiles/v3-openmaptiles/{z}/{x}/{y}.pbf?key=h3WICQANUooj1n2U0lPk 146 | 147 | https://api.maptiler.com/tiles/v3-openmaptiles/{z}/{x}/{y}.pbf?key=h3WICQANUooj1n2U0lPk 148 | 149 | https://tile.nextzen.org/tilezen/vector/v1/all/{z}/{x}/{y}.topojson?api_key= 150 | 151 | https://github.com/mapbox/vector-tile-spec/tree/master/2.1 152 | https://docs.mapbox.com/data/tilesets/guides/vector-tiles-standards/ 153 | https://www.mapzen.com/projects/vector-tiles/ 154 | https://stevebennett.me/2017/08/23/openstreetmap-vector-tiles-mixing-and-matching-engines-schemas-and-styles/ 7nmb -------------------------------------------------------------------------------- /bs-config.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | |-------------------------------------------------------------------------- 4 | | Browser-sync config file 5 | |-------------------------------------------------------------------------- 6 | | 7 | | For up-to-date information about the options: 8 | | http://www.browsersync.io/docs/options/ 9 | | 10 | | There are more options than you see here, these are just the ones that are 11 | | set internally. See the website for more info. 12 | | 13 | | 14 | */ 15 | module.exports = { 16 | //"ui": { "port": 3333 }, 17 | "ui": false, 18 | "files": [ './**/*.{html,htm,css,js}' ], 19 | "watchEvents": [ "change" ], 20 | "watch": false, 21 | "ignore": [], 22 | "single": false, 23 | "watchOptions": { 24 | "ignoreInitial": true, 25 | "ignored": 'node_modules' 26 | }, 27 | "server": { 28 | baseDir : './', 29 | directory: true 30 | }, 31 | "proxy": false, 32 | "port": 1352, 33 | "middleware": false, 34 | "serveStatic": [], 35 | "ghostMode": { 36 | "clicks": true, 37 | "scroll": true, 38 | "location": true, 39 | "forms": { 40 | "submit": true, 41 | "inputs": true, 42 | "toggles": true 43 | } 44 | }, 45 | "logLevel": "info", 46 | "logPrefix": "Browsersync", 47 | "logConnections": false, 48 | "logFileChanges": true, 49 | "logSnippet": true, 50 | "rewriteRules": [], 51 | "open": "local", 52 | "browser": "default", 53 | "cors": false, 54 | "xip": false, 55 | "hostnameSuffix": false, 56 | "reloadOnRestart": false, 57 | "notify": true, 58 | "scrollProportionally": true, 59 | "scrollThrottle": 0, 60 | "scrollRestoreTechnique": "window.name", 61 | "scrollElements": [], 62 | "scrollElementMapping": [], 63 | "reloadDelay": 0, 64 | "reloadDebounce": 500, 65 | "reloadThrottle": 0, 66 | "plugins": [], 67 | "injectChanges": false, 68 | "startPath": null, 69 | "minify": true, 70 | "host": null, 71 | "localOnly": false, 72 | "codeSync": true, 73 | "timestamps": true, 74 | "clientEvents": [ 75 | "scroll", 76 | "scroll:element", 77 | "input:text", 78 | "input:toggles", 79 | "form:submit", 80 | "form:reset", 81 | "click" 82 | ], 83 | "socket": { 84 | "socketIoOptions": { 85 | "log": false 86 | }, 87 | "socketIoClientConfig": { 88 | "reconnectionAttempts": 50 89 | }, 90 | "path": "/browser-sync/socket.io", 91 | "clientPath": "/browser-sync", 92 | "namespace": "/browser-sync", 93 | "clients": { 94 | "heartbeatTimeout": 5000 95 | } 96 | }, 97 | "tagNames": { 98 | "less": "link", 99 | "scss": "link", 100 | "css": "link", 101 | "jpg": "img", 102 | "jpeg": "img", 103 | "png": "img", 104 | "svg": "img", 105 | "gif": "img", 106 | "js": "script" 107 | }, 108 | "injectNotification": false 109 | }; -------------------------------------------------------------------------------- /images/goo3dtiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/images/goo3dtiles.png -------------------------------------------------------------------------------- /images/state_building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/images/state_building.png -------------------------------------------------------------------------------- /images/terrain_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/images/terrain_tile.png -------------------------------------------------------------------------------- /images/topo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/images/topo.png -------------------------------------------------------------------------------- /images/voxels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/images/voxels.png -------------------------------------------------------------------------------- /import-map.js: -------------------------------------------------------------------------------- 1 | // in the future can prob do : 2 | const prepend = ( document.location.hostname.indexOf( 'localhost' ) === -1 )? '/mapping' : ''; 3 | 4 | document.body.appendChild(Object.assign(document.createElement('script'), { 5 | type : 'importmap', 6 | innerHTML : ` 7 | {"imports":{ 8 | "three" : "${prepend}/lib/thirdparty/three.module.min.js", 9 | "OrbitControls" : "${prepend}/lib/thirdparty/OrbitControls.js", 10 | "TransformControls" : "${prepend}/lib/thirdparty/TransformControls.js", 11 | "gl-matrix" : "${prepend}/lib/thirdparty/gl-matrix/index.js", 12 | "earcut" : "${prepend}/lib/thirdparty/earcut.js", 13 | "postprocess/" : "${prepend}/lib/thirdparty/threePostProcess/" 14 | }} 15 | `})); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mapping 6 | 20 | 21 | 22 | 23 |
24 |

Maps & Terrains

25 |
26 |

27 | I have interest in building virtual worlds that can be used in games or apps. There was an interesting 28 | demo by @wooorldXR that really inspired me into trying my hand in learning how to build a 3D map viewer 29 | from scratch. So this repo will contain all the little experiments & prototypes using freely available mapping 30 | data to recreate that demo & then some :) 31 |

32 | 33 |

34 | Another purpose of this repo is to develop a collection of examples / prototypes that is feature focused. Ideally 35 | this should make it easier for people to pick apart & learn how each bit works as a good starting point. Mind you, 36 | nothing here is production ready as things aren't optimized or very well abstracted. Since the goal is for learning, 37 | things are kept as bare bones and simple as possible. 38 |

39 | 40 |

41 | Inspiration :
42 | - https://twitter.com/haltor/status/1594166328356704258
43 | - https://twitter.com/tiltfive/status/1597644083828518913
44 | - https://twitter.com/SketchpunkLabs/status/1600487910758907905
45 |

46 | 47 |

48 | Source Code : https://github.com/sketchpunklabs/mapping 49 |

50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 |
59 |
60 | 61 |
62 | Mapping Tile Prototypes 63 |
    64 | 65 |
  1. Google Photorealistic 3DTiles - Render tile at LonLat ( API Key Needed ) 66 |

    Modification from the basic example, this one converts LonLat coordinates of the empire state building 67 | to cartesian cords then performs a tree search for the last possible tile to render. It streams down sub tilesets 68 | during the tile search in the process. Renders the 3D tile when the location has been found. 69 |

    70 |
  2. 71 | 72 |
  3. Google Photorealistic 3DTiles - Basic ( API Key Needed ) 73 |

    From scratch with a basic 3D Tiles parser & loader, this example shows how to parse 3D tiles 74 | from google, download JSON and GLBs to render two initial tiles in the tileset. 75 |

    76 |
  4. 77 | 78 |
  5. Vector Tiles 79 |

    Download PBF data, use protobuf to deserialize binary into JSON. From there turn the points, lines and 80 | polygons into meshes that can be rendered with webgl. 81 |

    82 |
  6. 83 | 84 |
  7. Terrarium Heightmap Terrain Tiles 85 |

    Download Mapzen's terrarium & normal map tiles along with ArcGIS satellite map tile. 86 | Use height map to apply vertex displacement to a grid plane mesh, then render it well 87 | with the normals for to handle lighting and satellite image as its texture. 88 |

    89 |
  8. 90 | 91 |
  9. Render 3D Build Tile 92 |

    Parse and extrude multiple polygons to build a single building, then scale and place it correctly 93 | over a raster tile. Filtering all buildings from the tile except for Empire State Building. 94 |

    95 |
  10. 96 | 97 |
  11. Render Raster Tile 98 |

    Download tile image, convert to a texture then render to a quad face mesh.

    99 |
  12. 100 | 101 |
  13. Slippy Viewer Mechanics
    102 |

    Scaling and swopping tiles during zoom operation, plus doing tile translations using a floating origin.

    103 |
  14. 104 | 105 |
  15. Fix Mesh with Floating Origin 106 |

    3D tiles that have their vertices far away from origin causes z-fighting like problems caused by floating point 107 | errors. This example fixes the mesh into localspace, then uses a ModelViewMatrix in a shader to properly render 108 | the tile at extremely large distances from origin with the camera without any strange artifacts. 109 |

    110 |
  16. 111 | 112 |
113 |
114 | Procedural Terrain Prototypes 115 |
    116 |
  1. Topographic Contour Lines ( With Bloom ) 117 |

    Render a procedural terrain tile using a custom shader that draws out the contour lines along 118 | with using the composer to apply a bloom post effect. View without bloom

    119 |
  2. 120 | 121 |
  3. Procedural Heightmap Texture 122 |

    Using a material with noise, render to a square text to create a heightmap. Then use that texture for displacement of a grid along with generating normals from the heightmap.

    123 |
  4. 124 | 125 |
  5. Smooth Contour Lines 126 |

    Generating smooth contour lines requires a bit of extra work by doing the noise displacement in the vertex shader (Heightmaps won't work) for smooth height values plus procedurally create the lines as a texture to utilize mipmaps to smooth things out

    127 |
  6. 128 | 129 |
  7. Height Coloring 130 |

    Procedurally generate a heightmap textture then apply colors in relation to the height.

    131 |
  8. 132 | 133 |
  9. Instanced Cubes with FBM & ColorRamp 134 |

    Generating smooth contour lines requires a bit of extra work by doing the noise displacement in the vertex shader (Heightmaps won't work) for smooth height values plus procedurally create the lines as a texture to utilize mipmaps to smooth things out

    135 |
  10. 136 | 137 |
  11. Displacement Stamp 138 |

    Using a heightmap image as a displacement stamping.

    139 |
  12. 140 |
141 |
142 |
143 | 144 | 145 | -------------------------------------------------------------------------------- /lib/Starter.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | background-color: #000; 4 | color: #fff; 5 | font-family: monospace; 6 | font-size: 12px; 7 | overscroll-behavior: none; 8 | } 9 | canvas{ display: block; } -------------------------------------------------------------------------------- /lib/Starter.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | import { OrbitControls } from 'OrbitControls'; 3 | 4 | // #region STARTUP 5 | const mod_path = import.meta.url.substring( 0, import.meta.url.lastIndexOf( '/' ) + 1 ); 6 | const css_path = mod_path + "Starter.css"; 7 | 8 | (function(){ 9 | let link = document.createElement( 'link' ); 10 | link.rel = 'stylesheet'; 11 | link.type = 'text/css'; 12 | link.media = 'all'; 13 | link.href = css_path; 14 | document.getElementsByTagName( 'head' )[0].appendChild( link ); 15 | })(); 16 | // #endregion 17 | 18 | // Boiler Plate Starter for ThreeJS 19 | class Starter{ 20 | // #region MAIN 21 | scene = null; 22 | camera = null; 23 | renderer = null; 24 | clock = null; 25 | orbit = null; 26 | renderBind = this.render.bind( this ); 27 | onRender = null; 28 | onRenderPost = null; 29 | useRequestFrame = true; 30 | 31 | /** 32 | { 33 | webgl2 : false, // Use WebGL 2 instead of 1 34 | grid : false, // Put a Grid Floor in the scene 35 | lights : true, 36 | container : null, // Put Generated Canvas in a Container Element 37 | canvas : null, // Use an existing canvas 38 | ortho : null, // Starting Orthographic worldspace box height, disables Perspective Projection if value != falsey 39 | orbit : true, 40 | xr : false, 41 | } 42 | */ 43 | constructor( config={} ){ 44 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | // MAIN SETUPS 46 | this.initCore( config ); 47 | this.initEnv( config ); 48 | this.initUtil( config ); 49 | 50 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 51 | // Have the canvas set as full screen or fill its container's space 52 | if( config.fullscreen != false ){ 53 | window.addEventListener( 'resize', this.onWindowResize.bind( this ) ); 54 | this.renderer.setSize( window.innerWidth, window.innerHeight ); 55 | }else{ 56 | // Take the size of the parent element. 57 | let box = this.renderer.domElement.parentNode.getBoundingClientRect(); 58 | this.renderer.setSize( box.width , box.height ); 59 | 60 | // When changing the canvas size, need to update the Projection Aspect Ratio to render correctly. 61 | this.camera.aspect = box.width / box.height; 62 | this.camera.updateProjectionMatrix(); 63 | } 64 | } 65 | 66 | initCore( config ){ 67 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68 | // RENDERER 69 | let options = { 70 | antialias : true, 71 | alpha : true, 72 | }; 73 | 74 | // THREE.JS can't handle loading into WebGL2 on its own 75 | // Need to create canvas & get the proper context, pass those 2 into 3js 76 | if( config.webgl2 ){ 77 | let canvas = ( config.canvas )? config.canvas : document.createElement( 'canvas' ); 78 | options.canvas = canvas; 79 | options.context = canvas.getContext( 'webgl2' ); 80 | }else if( config.canvas ){ 81 | options.canvas = config.canvas; 82 | } 83 | 84 | this.renderer = new THREE.WebGLRenderer( options ); 85 | this.renderer.setPixelRatio( window.devicePixelRatio ); 86 | this.renderer.setClearColor( 0x3a3a3a, 1 ); 87 | 88 | if( config.xr == true ) this.renderer.xr.enabled = true; 89 | 90 | // where to add the cnavas object, in a container or in the body. 91 | if( !config.canvas ){ 92 | if( config.container ) config.container.appendChild( this.renderer.domElement ); 93 | else document.body.appendChild( this.renderer.domElement ); 94 | } 95 | 96 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 97 | // Camera 98 | const ratio = window.innerWidth / window.innerHeight; 99 | 100 | if( !config.ortho ){ 101 | this.camera = new THREE.PerspectiveCamera( 45, ratio, 0.01, 1000 ); 102 | }else{ 103 | let height = config.ortho / 2; 104 | let width = config.ortho * ratio / 2; 105 | this.camera = new THREE.OrthographicCamera( -width, width, height, -height, -2500, 2500 ); 106 | } 107 | 108 | this.camera.position.set( 0, 10, 20 ); 109 | 110 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 111 | // Misc 112 | this.scene = new THREE.Scene(); 113 | } 114 | 115 | initEnv( config ){ 116 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 117 | // LIGHTING 118 | if( config.lights === undefined || config.lights === true ){ 119 | let light = new THREE.DirectionalLight( 0xffffff, 0.8 ); 120 | light.position.set( 4, 10, 1 ); 121 | 122 | this.scene.add( light ); 123 | this.scene.add( new THREE.AmbientLight( 0x404040 ) ); 124 | } 125 | 126 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 127 | // MISC 128 | if( config.grid ) this.scene.add( new THREE.GridHelper( 20, 20, 0x0c610c, 0x444444 ) ); 129 | } 130 | 131 | initUtil( config ){ 132 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 133 | // Camera Controller 134 | if( config.orbit || config.orbit == undefined ){ 135 | this.orbit = new OrbitControls( this.camera, this.renderer.domElement ); 136 | } 137 | 138 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 139 | // Clock 140 | this.clock = new THREE.Clock(); 141 | this.clock.start(); 142 | 143 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 144 | // Need to watch the canvas's parent element for any change in size, since 145 | // the canvas needs it's size to be explicitly set then trigger updating 146 | // various threejs rendering data. 147 | // this.resizeObserver = new ResizeObserver( _ => { 148 | // this.onResize(); 149 | // }); 150 | 151 | // this.resizeObserver.observe(this.renderer.domElement.parentNode); 152 | } 153 | // #endregion 154 | 155 | // #region EVENTS 156 | onWindowResize(){ 157 | const W = window.innerWidth; 158 | const H = window.innerHeight; 159 | const DPR = window.devicePixelRatio; 160 | const RATIO = W / H; 161 | 162 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 163 | // Update Camera 164 | this.camera.aspect = RATIO; 165 | this.camera.updateProjectionMatrix(); 166 | 167 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 168 | // Update Renderer 169 | this.renderer.setSize( W, H ); 170 | this.render(); 171 | } 172 | // #endregion 173 | 174 | // #region METHODS 175 | add( o ){ 176 | for( let a of arguments ) this.scene.add( a ); 177 | return this; 178 | } 179 | 180 | remove( o ){ this.scene.remove( o ); return this; } 181 | 182 | setCamera( lon, lat, radius, target=null ){ 183 | let phi = ( 90 - lat ) * Math.PI / 180, 184 | theta = ( lon + 180 ) * Math.PI / 180; 185 | 186 | this.camera.position.set( 187 | -(radius * Math.sin( phi ) * Math.sin(theta)), 188 | radius * Math.cos( phi ), 189 | -(radius * Math.sin( phi ) * Math.cos(theta)) 190 | ); 191 | 192 | if( this.camera.isOrthographicCamera ){ 193 | this.camera.zoom = radius; 194 | this.camera.updateProjectionMatrix(); 195 | } 196 | 197 | if( target ) this.orbit.target.fromArray( target ); 198 | 199 | if( this.orbit ) this.orbit.update(); 200 | return this; 201 | } 202 | 203 | render( time, frame ){ // Time & Frame is only for XR Animation Loop 204 | if( this.useRequestFrame ) window.requestAnimationFrame( this.renderBind ); 205 | 206 | const deltaTime = this.clock.getDelta(); 207 | const ellapseTime = this.clock.getElapsedTime(); 208 | 209 | if( this.onRender ) this.onRender( deltaTime, ellapseTime, frame, time ); 210 | 211 | this.renderer.render( this.scene, this.camera ); 212 | 213 | if( this.onRenderPost ) this.onRenderPost( deltaTime, ellapseTime ); 214 | } 215 | 216 | getRendererSize(){ return this.renderer.getSize( new THREE.Vector2() ).toArray(); } 217 | 218 | startXRRender() { 219 | this.useRequestFrame = false; 220 | this.renderer.setAnimationLoop( this.renderBind ); 221 | } 222 | // #endregion 223 | } 224 | 225 | export default Starter; 226 | export { THREE }; -------------------------------------------------------------------------------- /lib/meshes/DynLineMesh.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | class DynLineMesh extends THREE.LineSegments{ 4 | _defaultColor = 0x00ff00; 5 | _cnt = 0; 6 | _verts = []; 7 | _color = []; 8 | _config = []; 9 | _dirty = false; 10 | 11 | constructor( initSize = 20 ){ 12 | super( 13 | _newDynLineMeshGeometry( 14 | new Float32Array( initSize * 2 * 3 ), // Two Points for Each Line 15 | new Float32Array( initSize * 2 * 3 ), 16 | new Float32Array( initSize * 2 * 1 ), 17 | false 18 | ), 19 | newDynLineMeshMaterial() //new THREE.PointsMaterial( { color: 0xffffff, size:8, sizeAttenuation:false } ) 20 | ); 21 | 22 | this.geometry.setDrawRange( 0, 0 ); 23 | this.onBeforeRender = ()=>{ if( this._dirty ) this._updateGeometry(); } 24 | } 25 | 26 | reset(){ 27 | this._cnt = 0; 28 | this._verts.length = 0; 29 | this._color.length = 0; 30 | this._config.length = 0; 31 | this.geometry.setDrawRange( 0, 0 ); 32 | return this; 33 | } 34 | 35 | add( p0, p1, color0=this._defaultColor, color1=null, isDash=false ){ 36 | this._verts.push( p0[0], p0[1], p0[2], p1[0], p1[1], p1[2] ); 37 | this._color.push( ...glColor( color0 ), ...glColor( (color1 != null) ? color1:color0 ) ); 38 | 39 | if( isDash ){ 40 | const len = Math.sqrt( 41 | (p1[0] - p0[0]) ** 2 + 42 | (p1[1] - p0[1]) ** 2 + 43 | (p1[2] - p0[2]) ** 2 44 | ); 45 | this._config.push( 0, len ); 46 | }else{ 47 | this._config.push( 0, 0 ); 48 | } 49 | 50 | this._cnt++; 51 | this._dirty = true; 52 | return this; 53 | } 54 | 55 | box( v0, v1, col=this._defaultColor, is_dash=false ){ 56 | let x1 = v0[0], y1 = v0[1], z1 = v0[2], 57 | x2 = v1[0], y2 = v1[1], z2 = v1[2]; 58 | 59 | this.add( [x1,y1,z1], [x1,y1,z2], col, null, is_dash ); // Bottom 60 | this.add( [x1,y1,z2], [x2,y1,z2], col, null, is_dash ); 61 | this.add( [x2,y1,z2], [x2,y1,z1], col, null, is_dash ); 62 | this.add( [x2,y1,z1], [x1,y1,z1], col, null, is_dash ); 63 | this.add( [x1,y2,z1], [x1,y2,z2], col, null, is_dash ); // Top 64 | this.add( [x1,y2,z2], [x2,y2,z2], col, null, is_dash ); 65 | this.add( [x2,y2,z2], [x2,y2,z1], col, null, is_dash ); 66 | this.add( [x2,y2,z1], [x1,y2,z1], col, null, is_dash ); 67 | this.add( [x1,y1,z1], [x1,y2,z1], col, null, is_dash ); // Sides 68 | this.add( [x1,y1,z2], [x1,y2,z2], col, null, is_dash ); 69 | this.add( [x2,y1,z2], [x2,y2,z2], col, null, is_dash ); 70 | this.add( [x2,y1,z1], [x2,y2,z1], col, null, is_dash ); 71 | return this; 72 | } 73 | 74 | obb( c, x, y, z, col=this._defaultColor, is_dash=false ){ 75 | const ba = [ c[0] - x[0] + y[0] - z[0], c[1] - x[1] + y[1] - z[1], c[2] - x[2] + y[2] - z[2] ]; 76 | const bb = [ c[0] - x[0] - y[0] - z[0], c[1] - x[1] - y[1] - z[1], c[2] - x[2] - y[2] - z[2] ]; 77 | const bc = [ c[0] + x[0] - y[0] - z[0], c[1] + x[1] - y[1] - z[1], c[2] + x[2] - y[2] - z[2] ]; 78 | const bd = [ c[0] + x[0] + y[0] - z[0], c[1] + x[1] + y[1] - z[1], c[2] + x[2] + y[2] - z[2] ]; 79 | const fa = [ c[0] - x[0] + y[0] + z[0], c[1] - x[1] + y[1] + z[1], c[2] - x[2] + y[2] + z[2] ]; 80 | const fb = [ c[0] - x[0] - y[0] + z[0], c[1] - x[1] - y[1] + z[1], c[2] - x[2] - y[2] + z[2] ]; 81 | const fc = [ c[0] + x[0] - y[0] + z[0], c[1] + x[1] - y[1] + z[1], c[2] + x[2] - y[2] + z[2] ]; 82 | const fd = [ c[0] + x[0] + y[0] + z[0], c[1] + x[1] + y[1] + z[1], c[2] + x[2] + y[2] + z[2] ]; 83 | this.add( ba, bb, col, null, is_dash ); // Back 84 | this.add( bb, bc, col, null, is_dash ); 85 | this.add( bc, bd, col, null, is_dash ); 86 | this.add( bd, ba, col, null, is_dash ); 87 | this.add( fa, fb, col, null, is_dash ); // Front 88 | this.add( fb, fc, col, null, is_dash ); 89 | this.add( fc, fd, col, null, is_dash ); 90 | this.add( fd, fa, col, null, is_dash ); 91 | this.add( fa, ba, col, null, is_dash ); // Connect 92 | this.add( fb, bb, col, null, is_dash ); 93 | this.add( fc, bc, col, null, is_dash ); 94 | this.add( fd, bd, col, null, is_dash ); 95 | return this 96 | } 97 | 98 | circle( origin, xAxis, yAxis, radius, seg, col=ln_color, is_dash=false ){ 99 | const prevPos = [0,0,0]; 100 | const pos = [0,0,0]; 101 | const PI2 = Math.PI * 2; 102 | let rad = 0; 103 | 104 | planeCircle( origin, xAxis, yAxis, 0, radius, prevPos ); 105 | for( let i=1; i <= seg; i++ ){ 106 | rad = PI2 * ( i / seg ); 107 | planeCircle( origin, xAxis, yAxis, rad, radius, pos ); 108 | this.add( prevPos, pos, col, null, is_dash ); 109 | 110 | prevPos[0] = pos[0]; 111 | prevPos[1] = pos[1]; 112 | prevPos[2] = pos[2]; 113 | } 114 | } 115 | 116 | _updateGeometry(){ 117 | const geo = this.geometry; 118 | const bVerts = geo.attributes.position; 119 | const bColor = geo.attributes.color; //this.geometry.index; 120 | const bConfig = geo.attributes.config; 121 | 122 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 123 | if( this._verts.length > bVerts.array.length || 124 | this._color.length > bColor.array.length || 125 | this._config.length > bConfig.array.length 126 | ){ 127 | if( this.geometry ) this.geometry.dispose(); 128 | this.geometry = _newDynLineMeshGeometry( this._verts, this._color, this._config ); 129 | this._dirty = false; 130 | return; 131 | } 132 | 133 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 134 | bVerts.array.set( this._verts ); 135 | bVerts.count = this._verts.length / 3; 136 | bVerts.needsUpdate = true; 137 | 138 | bColor.array.set( this._color ); 139 | bColor.count = this._color.length / 3; 140 | bColor.needsUpdate = true; 141 | 142 | bConfig.array.set( this._config ); 143 | bConfig.count = this._config.length / 1; 144 | bConfig.needsUpdate = true; 145 | 146 | geo.setDrawRange( 0, bVerts.count ); 147 | geo.computeBoundingBox(); 148 | geo.computeBoundingSphere(); 149 | 150 | this._dirty = false; 151 | } 152 | } 153 | 154 | //#region SUPPORT 155 | function _newDynLineMeshGeometry( aVerts, aColor, aConfig, doCompute=true ){ 156 | //if( !( aVerts instanceof Float32Array) ) aVerts = new Float32Array( aVerts ); 157 | //if( !( aColor instanceof Float32Array) ) aColor = new Float32Array( aColor ); 158 | 159 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 160 | const bVerts = new THREE.Float32BufferAttribute( aVerts, 3 ); 161 | const bColor = new THREE.Float32BufferAttribute( aColor, 3 ); 162 | const bConfig = new THREE.Float32BufferAttribute( aConfig, 1 ); 163 | bVerts.setUsage( THREE.DynamicDrawUsage ); 164 | bColor.setUsage( THREE.DynamicDrawUsage ); 165 | bConfig.setUsage( THREE.DynamicDrawUsage ); 166 | 167 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 168 | const geo = new THREE.BufferGeometry(); 169 | geo.setAttribute( 'position', bVerts ); 170 | geo.setAttribute( 'color', bColor ); 171 | geo.setAttribute( 'config', bConfig ); 172 | 173 | if( doCompute ){ 174 | geo.computeBoundingSphere(); 175 | geo.computeBoundingBox(); 176 | } 177 | return geo; 178 | } 179 | 180 | function glColor( hex, out = null ){ 181 | const NORMALIZE_RGB = 1 / 255; 182 | out = out || [0,0,0]; 183 | 184 | out[0] = ( hex >> 16 & 255 ) * NORMALIZE_RGB; 185 | out[1] = ( hex >> 8 & 255 ) * NORMALIZE_RGB; 186 | out[2] = ( hex & 255 ) * NORMALIZE_RGB; 187 | 188 | return out; 189 | } 190 | //#endregion 191 | 192 | //#region SHADER 193 | 194 | function newDynLineMeshMaterial(){ 195 | return new THREE.RawShaderMaterial({ 196 | depthTest : true, 197 | transparent : true, 198 | uniforms : { 199 | dashSeg : { value : 1 / 0.07 }, 200 | dashDiv : { value : 0.4 }, 201 | }, 202 | vertexShader : `#version 300 es 203 | in vec3 position; 204 | in vec3 color; 205 | in float config; 206 | 207 | uniform mat4 modelViewMatrix; 208 | uniform mat4 projectionMatrix; 209 | uniform float u_scale; 210 | 211 | out vec3 fragColor; 212 | out float fragLen; 213 | 214 | void main(){ 215 | vec4 wPos = modelViewMatrix * vec4( position, 1.0 ); 216 | 217 | fragColor = color; 218 | fragLen = config; 219 | 220 | gl_Position = projectionMatrix * wPos; 221 | }`, 222 | fragmentShader : `#version 300 es 223 | precision mediump float; 224 | 225 | uniform float dashSeg; 226 | uniform float dashDiv; 227 | 228 | in vec3 fragColor; 229 | in float fragLen; 230 | out vec4 outColor; 231 | 232 | void main(){ 233 | float alpha = 1.0; 234 | if( fragLen > 0.0 ) alpha = step( dashDiv, fract( fragLen * dashSeg ) ); 235 | outColor = vec4( fragColor, alpha ); 236 | }`}); 237 | } 238 | 239 | //#endregion 240 | 241 | export default DynLineMesh; -------------------------------------------------------------------------------- /lib/meshes/GridAlternative.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | export default class GridAlternative{ 4 | 5 | static geometry( width=1, height=1, xCells=2, yCells=2, fromCenter=true ){ 6 | const rtn = { 7 | vertices : [], 8 | indices : [], 9 | texcoord : [], 10 | normals : [], 11 | }; 12 | 13 | this._genVertices( rtn.vertices, width, height, xCells, yCells, fromCenter ); 14 | this._genAltIndices( rtn.indices, xCells+1, yCells+1, 0, true ); 15 | this._genTexcoord( rtn.texcoord, xCells, yCells ); 16 | this._repeatVec( rtn.normals, rtn.vertices.length / 3, 0, 1, 0 ); 17 | 18 | return rtn; 19 | } 20 | 21 | static geometryBuffer( width=1, height=1, xCells=2, yCells=2, fromCenter=true ){ 22 | const geo = this.geometry( width, height, xCells, yCells, fromCenter ); 23 | 24 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 25 | const bGeo = new THREE.BufferGeometry(); 26 | bGeo.setIndex( geo.indices ); 27 | bGeo.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geo.vertices ), 3 ) ); 28 | bGeo.setAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geo.normals ), 3 ) ); 29 | bGeo.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geo.texcoord ), 2 ) ); 30 | 31 | return bGeo; 32 | } 33 | 34 | static mesh( mat, width=1, height=1, xCells=2, yCells=2, fromCenter=true,wireFrame=false ){ 35 | const bGeo = this.geometryBuffer( width, height, xCells, yCells, fromCenter ); 36 | 37 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38 | mat = mat || new THREE.MeshPhongMaterial( { color:0x009999 } ); // ,side:THREE.DoubleSide 39 | const mesh = new THREE.Mesh( bGeo, mat ); 40 | 41 | if( wireFrame ){ 42 | const mat = new THREE.LineBasicMaterial({ color:0xffffff, opacity:0.6, transparent:true }); 43 | const wGeo = new THREE.WireframeGeometry( bGeo ); 44 | const grp = new THREE.Group(); 45 | grp.add( mesh ); 46 | grp.add( new THREE.LineSegments( wGeo, mat ) ) 47 | return grp; 48 | }else{ 49 | return mesh; 50 | } 51 | } 52 | 53 | // #region GENERATION 54 | static _genVertices( out, width=1, height=1, xCells=2, yCells=2, useCenter=false ){ 55 | const x_inc = width / xCells, 56 | y_inc = height / yCells; 57 | let ox = 0, 58 | oz = 0, 59 | x, z, xi, yi; 60 | 61 | if( useCenter ){ 62 | ox = -width * 0.5; 63 | oz = -height * 0.5; 64 | } 65 | 66 | for( yi=0; yi <= yCells; yi++ ){ 67 | z = yi * y_inc; 68 | for( xi=0; xi <= xCells; xi++ ){ 69 | x = xi * x_inc; 70 | out.push( x+ox, 0.0, z+oz ); 71 | } 72 | } 73 | } 74 | 75 | /** Alternating Triangle Pattern, Front/Back Slash */ 76 | static _genAltIndices( out, row_size, row_cnt, start_idx=0, rev_quad=true ){ 77 | const row_stop = row_cnt - 1; 78 | const col_stop = row_size - 1; 79 | let x, y, a, b, c, d, bit; 80 | 81 | for( y=0; y < row_stop; y++ ){ 82 | bit = y & 1; // Alternate the starting Quad Layout for every row 83 | 84 | for( x=0; x < col_stop; x++ ){ 85 | a = start_idx + y * row_size + x; 86 | b = a + row_size; 87 | c = b + 1 88 | d = a + 1; 89 | 90 | // Alternate the Quad Layout for each cell 91 | if( rev_quad ){ 92 | if( ( x & 1 ) == bit ) out.push( d, a, b, b, c, d ); // Front Slash 93 | else out.push( a, b, c, c, d, a ); // Back Slash 94 | }else{ 95 | if( ( x & 1 ) == bit ) out.push( d, c, b, b, a, d ); // Front Slash 96 | else out.push( a, d, c, c, b, a ); // Back Slash 97 | } 98 | } 99 | } 100 | } 101 | 102 | static _genTexcoord( out, xLen, yLen ){ 103 | let x, y, yt; 104 | for( y=0; y <= yLen; y++ ){ 105 | yt = 1 - ( y / yLen ); 106 | for( x=0; x <= xLen; x++ ) out.push( x / xLen, yt ); 107 | } 108 | } 109 | 110 | static _repeatVec( out, cnt, x, y, z ){ 111 | for( let i=0; i < cnt; i++ ) out.push( x, y, z ); 112 | } 113 | // #endregion 114 | } -------------------------------------------------------------------------------- /lib/meshes/PlaneGrid.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | export default function PlaneGrid( props ){ 4 | props = Object.assign( { mat:null, size:1, segments:1 }, props ); 5 | 6 | const geo = new THREE.PlaneGeometry( props.size, props.size, props.segments, props.segments ); 7 | geo.rotateX( -Math.PI * 0.5 ); // Uses matrices, but also rotates normals 8 | 9 | // let y, z, ary = geo.attributes.position.array; 10 | // for( i=0; i < ary.length; i+=3 ){ 11 | // y = ary[ i+1 ]; 12 | // z = ary[ i+2 ]; 13 | // ary[ i+1 ] = z; 14 | // ary[ i+2 ] = -y; 15 | // } 16 | 17 | const mesh = new THREE.Mesh( geo, props.mat || new THREE.MeshBasicMaterial() ); 18 | if( props.y ) mesh.position.y = props.y; 19 | 20 | 21 | return mesh; 22 | } -------------------------------------------------------------------------------- /lib/meshes/ShapePointsMesh.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | class ShapePointsMesh extends THREE.Points{ 4 | _defaultShape = 1; 5 | _defaultSize = 6; 6 | _defaultColor = 0x00ff00; 7 | _cnt = 0; 8 | _verts = []; 9 | _color = []; 10 | _config = []; 11 | _dirty = false; 12 | 13 | constructor( initSize = 20 ){ 14 | super( 15 | _newShapePointsMeshGeometry( 16 | new Float32Array( initSize * 3 ), 17 | new Float32Array( initSize * 3 ), 18 | new Float32Array( initSize * 2 ), 19 | false 20 | ), 21 | newShapePointsMeshMaterial() //new THREE.PointsMaterial( { color: 0xffffff, size:8, sizeAttenuation:false } ) 22 | ); 23 | 24 | this.geometry.setDrawRange( 0, 0 ); 25 | this.onBeforeRender = ()=>{ if( this._dirty ) this._updateGeometry(); } 26 | } 27 | 28 | reset(){ 29 | this._cnt = 0; 30 | this._verts.length = 0; 31 | this._color.length = 0; 32 | this._config.length = 0; 33 | this.geometry.setDrawRange( 0, 0 ); 34 | return this; 35 | } 36 | 37 | add( pos, color = this._defaultColor, size = this._defaultSize, shape = this._defaultShape ){ 38 | this._verts.push( pos[0], pos[1], pos[2] ); 39 | 40 | if( Array.isArray( color ) ) this._color.push( ...color ); // Already GLSL encoded 41 | else this._color.push( ...glColor( color ) ); // Numeric Hex, need glsl encoding 42 | 43 | this._config.push( size, shape ); 44 | this._cnt++; 45 | this._dirty = true; 46 | return this; 47 | } 48 | 49 | getByteSize(){ 50 | const geo = this.geometry; 51 | let size = 0; 52 | for( const attr of Object.values( geo.attributes ) ){ 53 | size += attr.array.byteLength; 54 | } 55 | return size; 56 | } 57 | 58 | _updateGeometry(){ 59 | const geo = this.geometry; 60 | const bVerts = geo.attributes.position; 61 | const bColor = geo.attributes.color; //this.geometry.index; 62 | const bConfig = geo.attributes.config; 63 | 64 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 65 | if( this._verts.length > bVerts.array.length || 66 | this._color.length > bColor.array.length || 67 | this._config.length > bConfig.array.length 68 | ){ 69 | if( this.geometry ){ 70 | this.geometry.dispose(); 71 | this.geometry = null; 72 | } 73 | this.geometry = _newShapePointsMeshGeometry( this._verts, this._color, this._config ); 74 | this._dirty = false; 75 | 76 | return; 77 | } 78 | 79 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 | bVerts.array.set( this._verts ); 81 | bVerts.count = this._verts.length / 3; 82 | bVerts.needsUpdate = true; 83 | 84 | bColor.array.set( this._color ); 85 | bColor.count = this._color.length / 3; 86 | bColor.needsUpdate = true; 87 | 88 | bConfig.array.set( this._config ); 89 | bConfig.count = this._config.length / 2; 90 | bConfig.needsUpdate = true; 91 | 92 | geo.setDrawRange( 0, bVerts.count ); 93 | geo.computeBoundingBox(); 94 | geo.computeBoundingSphere(); 95 | 96 | this._dirty = false; 97 | } 98 | } 99 | 100 | // #region SUPPORT 101 | function _newShapePointsMeshGeometry( aVerts, aColor, aConfig, doCompute=true ){ 102 | //if( !( aVerts instanceof Float32Array) ) aVerts = new Float32Array( aVerts ); 103 | //if( !( aColor instanceof Float32Array) ) aColor = new Float32Array( aColor ); 104 | 105 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 106 | const bVerts = new THREE.Float32BufferAttribute( aVerts, 3 ); 107 | const bColor = new THREE.Float32BufferAttribute( aColor, 3 ); 108 | const bConfig = new THREE.Float32BufferAttribute( aConfig, 2 ); 109 | bVerts.setUsage( THREE.DynamicDrawUsage ); 110 | bColor.setUsage( THREE.DynamicDrawUsage ); 111 | bConfig.setUsage( THREE.DynamicDrawUsage ); 112 | 113 | // bVerts.count = aVerts.length / 3; 114 | // bVerts.needsUpdate = true; 115 | 116 | // bColor.count = aColor.length / 3; 117 | // bColor.needsUpdate = true; 118 | 119 | // bConfig.count = aConfig.length / 2; 120 | // bConfig.needsUpdate = true; 121 | 122 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 123 | const geo = new THREE.BufferGeometry(); 124 | geo.setAttribute( 'position', bVerts ); 125 | geo.setAttribute( 'color', bColor ); 126 | geo.setAttribute( 'config', bConfig ); 127 | 128 | geo.setDrawRange( 0, bVerts.count ); 129 | geo.needsUpdate = true; 130 | 131 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 132 | if( doCompute ){ 133 | geo.computeBoundingSphere(); 134 | geo.computeBoundingBox(); 135 | } 136 | 137 | return geo; 138 | } 139 | 140 | function glColor( hex, out = null ){ 141 | const NORMALIZE_RGB = 1 / 255; 142 | out = out || [0,0,0]; 143 | 144 | out[0] = ( hex >> 16 & 255 ) * NORMALIZE_RGB; 145 | out[1] = ( hex >> 8 & 255 ) * NORMALIZE_RGB; 146 | out[2] = ( hex & 255 ) * NORMALIZE_RGB; 147 | 148 | return out; 149 | } 150 | // #endregion 151 | 152 | // #region SHADER 153 | 154 | function newShapePointsMeshMaterial(){ 155 | 156 | return new THREE.RawShaderMaterial({ 157 | depthTest : true, 158 | transparent : true, 159 | alphaToCoverage : true, 160 | uniforms : { u_scale:{ value : 20.0 } }, 161 | vertexShader : `#version 300 es 162 | in vec3 position; 163 | in vec3 color; 164 | in vec2 config; 165 | 166 | uniform mat4 modelViewMatrix; 167 | uniform mat4 projectionMatrix; 168 | uniform float u_scale; 169 | 170 | out vec3 fragColor; 171 | flat out int fragShape; 172 | 173 | void main(){ 174 | vec4 wPos = modelViewMatrix * vec4( position.xyz, 1.0 ); 175 | 176 | fragColor = color; 177 | fragShape = int( config.y ); 178 | 179 | gl_Position = projectionMatrix * wPos; 180 | gl_PointSize = config.x * ( u_scale / -wPos.z ); 181 | 182 | // Get pnt to be World Space Size 183 | //gl_PointSize = view_port_size.y * projectionMatrix[1][5] * 1.0 / gl_Position.w; 184 | //gl_PointSize = view_port_size.y * projectionMatrix[1][1] * 1.0 / gl_Position.w; 185 | }`, 186 | fragmentShader : `#version 300 es 187 | precision mediump float; 188 | 189 | #define PI 3.14159265359 190 | #define PI2 6.28318530718 191 | 192 | in vec3 fragColor; 193 | flat in int fragShape; 194 | out vec4 outColor; 195 | 196 | float circle(){ 197 | vec2 coord = gl_PointCoord * 2.0 - 1.0; // v_uv * 2.0 - 1.0; 198 | float radius = dot( coord, coord ); 199 | float dxdy = fwidth( radius ); 200 | return smoothstep( 0.90 + dxdy, 0.90 - dxdy, radius ); 201 | } 202 | 203 | float ring( float inner ){ 204 | vec2 coord = gl_PointCoord * 2.0 - 1.0; 205 | float radius = dot( coord, coord ); 206 | float dxdy = fwidth( radius ); 207 | return smoothstep( inner - dxdy, inner + dxdy, radius ) - 208 | smoothstep( 1.0 - dxdy, 1.0 + dxdy, radius ); 209 | } 210 | 211 | float diamond(){ 212 | // http://www.numb3r23.net/2015/08/17/using-fwidth-for-distance-based-anti-aliasing/ 213 | const float radius = 0.5; 214 | 215 | float dst = dot( abs(gl_PointCoord-vec2(0.5)), vec2(1.0) ); 216 | float aaf = fwidth( dst ); 217 | return 1.0 - smoothstep( radius - aaf, radius, dst ); 218 | } 219 | 220 | float poly( int sides, float offset, float scale ){ 221 | // https://thebookofshaders.com/07/ 222 | vec2 coord = gl_PointCoord * 2.0 - 1.0; 223 | 224 | coord.y += offset; 225 | coord *= scale; 226 | 227 | float a = atan( coord.x, coord.y ) + PI; // Angle of Pixel 228 | float r = PI2 / float( sides ); // Radius of Pixel 229 | float d = cos( floor( 0.5 + a / r ) * r-a ) * length( coord ); 230 | float f = fwidth( d ); 231 | return smoothstep( 0.5, 0.5 - f, d ); 232 | } 233 | 234 | // signed distance to a n-star polygon with external angle en 235 | float sdStar( float r, int n, float m ){ // m=[2,n] 236 | vec2 p = vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * 2.0 - 1.0; 237 | 238 | // these 4 lines can be precomputed for a given shape 239 | float an = 3.141593/float(n); 240 | float en = 3.141593/m; 241 | vec2 acs = vec2(cos(an),sin(an)); 242 | vec2 ecs = vec2(cos(en),sin(en)); // ecs=vec2(0,1) and simplify, for regular polygon, 243 | 244 | // reduce to first sector 245 | float bn = mod(atan(p.x,p.y),2.0*an) - an; 246 | p = length(p)*vec2(cos(bn),abs(sin(bn))); 247 | 248 | // line sdf 249 | p -= r*acs; 250 | p += ecs*clamp( -dot(p,ecs), 0.0, r*acs.y/ecs.y); 251 | 252 | float dist = length(p)*sign(p.x); 253 | float f = fwidth( dist ); 254 | 255 | return smoothstep( 0.0, 0.0 - f, dist ); 256 | } 257 | 258 | 259 | void main(){ 260 | float alpha = 1.0; 261 | 262 | if( fragShape == 1 ) alpha = circle(); 263 | if( fragShape == 2 ) alpha = diamond(); 264 | if( fragShape == 3 ) alpha = poly( 3, 0.2, 1.0 ); // Triangle 265 | if( fragShape == 4 ) alpha = poly( 5, 0.0, 0.65 ); // Pentagram 266 | if( fragShape == 5 ) alpha = poly( 6, 0.0, 0.65 ); // Hexagon 267 | if( fragShape == 6 ) alpha = ring( 0.2 ); 268 | if( fragShape == 7 ) alpha = ring( 0.7 ); 269 | if( fragShape == 8 ) alpha = sdStar( 1.0, 3, 2.3 ); 270 | if( fragShape == 9 ) alpha = sdStar( 1.0, 6, 2.5 ); 271 | if( fragShape == 10 ) alpha = sdStar( 1.0, 4, 2.4 ); 272 | if( fragShape == 11 ) alpha = sdStar( 1.0, 5, 2.8 ); 273 | 274 | outColor = vec4( fragColor, alpha ); 275 | }`}); 276 | } 277 | 278 | // #endregion 279 | 280 | export default ShapePointsMesh; -------------------------------------------------------------------------------- /lib/misc/TextureCanvas.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | export default class TextureCanvas{ 4 | 5 | constructor( w=256, h=256 ){ 6 | this.canvas = document.createElement( 'canvas' ); 7 | this.canvas.width = w; 8 | this.canvas.height = h; 9 | this.width = w; 10 | this.height = h; 11 | 12 | this.ctx = this.canvas.getContext( '2d' );; 13 | this.texture = new THREE.CanvasTexture( this.canvas ); 14 | } 15 | 16 | appendToBody(){ 17 | document.body.append( this.canvas ); 18 | return this; 19 | } 20 | 21 | useUVWrapping(){ 22 | this.texture.wrapS = THREE.RepeatWrapping; 23 | this.texture.wrapT = THREE.RepeatWrapping; 24 | return this; 25 | } 26 | 27 | gen(){ 28 | const ctx = this.ctx; 29 | const w = this.width; 30 | const h = this.height; 31 | 32 | const bigLineWidth = 0.04; 33 | const smallLineWidth = 0.01; 34 | const linesCount = 5; 35 | 36 | 37 | ctx.fillStyle = '#000000'; 38 | ctx.clearRect( 0, 0, w, h ); 39 | ctx.fillRect( 0, 0, w, h ); 40 | 41 | // Big Lines 42 | ctx.globalAlpha = 1.0; //terrain.texture.smallLineAlpha; 43 | ctx.fillStyle = '#ffffff'; 44 | ctx.fillRect( 0, 0, w, Math.round( h * bigLineWidth ) ); 45 | 46 | // Small Lines 47 | const smallLinesCount = linesCount - 1; 48 | 49 | for (let i = 0; i < smallLinesCount; i++) { 50 | ctx.fillRect( 51 | 0, 52 | Math.round( h / linesCount) * ( i + 1 ), 53 | w, 54 | Math.round( h * smallLineWidth ) 55 | ); 56 | } 57 | 58 | this.texture.needsUpdate = true; 59 | 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/misc/TexturePass.js: -------------------------------------------------------------------------------- 1 | import { Scene, Mesh, PlaneGeometry, WebGLRenderTarget, OrthographicCamera } from 'three'; 2 | import * as THREE from 'three'; 3 | 4 | export default class TexturePass{ 5 | constructor( mat, w=256, h=256 ){ 6 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 | // use 2u so verts are in the -1 to 1 range this allows 8 | // not needing to use projections in the vertex shader 9 | // as the values are the same as NDC 10 | const geo = new PlaneGeometry( 2, 2 ); 11 | const quad = new Mesh( geo, mat ); 12 | 13 | 14 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | // Setup render scene 16 | this.fbo = new WebGLRenderTarget( w, h ); // Frame Buffer to render to 17 | this.camera = new OrthographicCamera( -1, 1, 1, -1, 0, 1 ); // not needed but 3JS requires camera to render 18 | this.scene = new Scene(); 19 | this.scene.add( quad ); 20 | 21 | // this.fbo.texture.wrapS = THREE.RepeatWrapping; 22 | // this.fbo.texture.wrapT = THREE.RepeatWrapping; 23 | 24 | // this.fbo.texture.magFilter = THREE.NearestFilter; 25 | // this.fbo.texture.minFilter = THREE.LinearMipmapLinearFilter; //THREE.NearestFilter; 26 | } 27 | 28 | useRepeatWrapping(){ 29 | this.fbo.texture.wrapS = THREE.RepeatWrapping; 30 | this.fbo.texture.wrapT = THREE.RepeatWrapping; 31 | return this; 32 | } 33 | 34 | useMipmaps(){ 35 | // Use MSAA when rendering 36 | this.fbo.samples = 4; 37 | 38 | // Turn on feature & set filters that work well with it 39 | this.fbo.texture.generateMipmaps = true; 40 | this.fbo.texture.magFilter = THREE.NearestFilter; 41 | this.fbo.texture.minFilter = THREE.LinearMipmapLinearFilter; 42 | return this 43 | } 44 | 45 | get texture(){ return this.fbo.texture; } 46 | 47 | render( renderer ){ 48 | renderer.setRenderTarget( this.fbo ); 49 | renderer.render( this.scene, this.camera ); 50 | renderer.setRenderTarget( null ); 51 | return this; 52 | } 53 | } -------------------------------------------------------------------------------- /lib/thirdparty/draco/draco_decoder_gltf.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/lib/thirdparty/draco/draco_decoder_gltf.wasm -------------------------------------------------------------------------------- /lib/thirdparty/gl-matrix/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common utilities 3 | * @module glMatrix 4 | */ 5 | // Configuration Constants 6 | export var EPSILON = 0.000001; 7 | export var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; 8 | export var RANDOM = Math.random; 9 | export var ANGLE_ORDER = "zyx"; 10 | /** 11 | * Sets the type of array used when creating new vectors and matrices 12 | * 13 | * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array 14 | */ 15 | 16 | export function setMatrixArrayType(type) { 17 | ARRAY_TYPE = type; 18 | } 19 | var degree = Math.PI / 180; 20 | /** 21 | * Convert Degree To Radian 22 | * 23 | * @param {Number} a Angle in Degrees 24 | */ 25 | 26 | export function toRadian(a) { 27 | return a * degree; 28 | } 29 | /** 30 | * Tests whether or not the arguments have approximately the same value, within an absolute 31 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less 32 | * than or equal to 1.0, and a relative tolerance is used for larger values) 33 | * 34 | * @param {Number} a The first number to test. 35 | * @param {Number} b The second number to test. 36 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise. 37 | */ 38 | 39 | export function equals(a, b) { 40 | return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); 41 | } 42 | if (!Math.hypot) Math.hypot = function () { 43 | var y = 0, 44 | i = arguments.length; 45 | 46 | while (i--) { 47 | y += arguments[i] * arguments[i]; 48 | } 49 | 50 | return Math.sqrt(y); 51 | }; -------------------------------------------------------------------------------- /lib/thirdparty/gl-matrix/index.js: -------------------------------------------------------------------------------- 1 | import * as glMatrix from "./common.js"; 2 | import * as mat2 from "./mat2.js"; 3 | import * as mat2d from "./mat2d.js"; 4 | import * as mat3 from "./mat3.js"; 5 | import * as mat4 from "./mat4.js"; 6 | import * as quat from "./quat.js"; 7 | import * as quat2 from "./quat2.js"; 8 | import * as vec2 from "./vec2.js"; 9 | import * as vec3 from "./vec3.js"; 10 | import * as vec4 from "./vec4.js"; 11 | export { glMatrix, mat2, mat2d, mat3, mat4, quat, quat2, vec2, vec3, vec4 }; -------------------------------------------------------------------------------- /lib/thirdparty/gl-matrix/mat2.js: -------------------------------------------------------------------------------- 1 | import * as glMatrix from "./common.js"; 2 | /** 3 | * 2x2 Matrix 4 | * @module mat2 5 | */ 6 | 7 | /** 8 | * Creates a new identity mat2 9 | * 10 | * @returns {mat2} a new 2x2 matrix 11 | */ 12 | 13 | export function create() { 14 | var out = new glMatrix.ARRAY_TYPE(4); 15 | 16 | if (glMatrix.ARRAY_TYPE != Float32Array) { 17 | out[1] = 0; 18 | out[2] = 0; 19 | } 20 | 21 | out[0] = 1; 22 | out[3] = 1; 23 | return out; 24 | } 25 | /** 26 | * Creates a new mat2 initialized with values from an existing matrix 27 | * 28 | * @param {ReadonlyMat2} a matrix to clone 29 | * @returns {mat2} a new 2x2 matrix 30 | */ 31 | 32 | export function clone(a) { 33 | var out = new glMatrix.ARRAY_TYPE(4); 34 | out[0] = a[0]; 35 | out[1] = a[1]; 36 | out[2] = a[2]; 37 | out[3] = a[3]; 38 | return out; 39 | } 40 | /** 41 | * Copy the values from one mat2 to another 42 | * 43 | * @param {mat2} out the receiving matrix 44 | * @param {ReadonlyMat2} a the source matrix 45 | * @returns {mat2} out 46 | */ 47 | 48 | export function copy(out, a) { 49 | out[0] = a[0]; 50 | out[1] = a[1]; 51 | out[2] = a[2]; 52 | out[3] = a[3]; 53 | return out; 54 | } 55 | /** 56 | * Set a mat2 to the identity matrix 57 | * 58 | * @param {mat2} out the receiving matrix 59 | * @returns {mat2} out 60 | */ 61 | 62 | export function identity(out) { 63 | out[0] = 1; 64 | out[1] = 0; 65 | out[2] = 0; 66 | out[3] = 1; 67 | return out; 68 | } 69 | /** 70 | * Create a new mat2 with the given values 71 | * 72 | * @param {Number} m00 Component in column 0, row 0 position (index 0) 73 | * @param {Number} m01 Component in column 0, row 1 position (index 1) 74 | * @param {Number} m10 Component in column 1, row 0 position (index 2) 75 | * @param {Number} m11 Component in column 1, row 1 position (index 3) 76 | * @returns {mat2} out A new 2x2 matrix 77 | */ 78 | 79 | export function fromValues(m00, m01, m10, m11) { 80 | var out = new glMatrix.ARRAY_TYPE(4); 81 | out[0] = m00; 82 | out[1] = m01; 83 | out[2] = m10; 84 | out[3] = m11; 85 | return out; 86 | } 87 | /** 88 | * Set the components of a mat2 to the given values 89 | * 90 | * @param {mat2} out the receiving matrix 91 | * @param {Number} m00 Component in column 0, row 0 position (index 0) 92 | * @param {Number} m01 Component in column 0, row 1 position (index 1) 93 | * @param {Number} m10 Component in column 1, row 0 position (index 2) 94 | * @param {Number} m11 Component in column 1, row 1 position (index 3) 95 | * @returns {mat2} out 96 | */ 97 | 98 | export function set(out, m00, m01, m10, m11) { 99 | out[0] = m00; 100 | out[1] = m01; 101 | out[2] = m10; 102 | out[3] = m11; 103 | return out; 104 | } 105 | /** 106 | * Transpose the values of a mat2 107 | * 108 | * @param {mat2} out the receiving matrix 109 | * @param {ReadonlyMat2} a the source matrix 110 | * @returns {mat2} out 111 | */ 112 | 113 | export function transpose(out, a) { 114 | // If we are transposing ourselves we can skip a few steps but have to cache 115 | // some values 116 | if (out === a) { 117 | var a1 = a[1]; 118 | out[1] = a[2]; 119 | out[2] = a1; 120 | } else { 121 | out[0] = a[0]; 122 | out[1] = a[2]; 123 | out[2] = a[1]; 124 | out[3] = a[3]; 125 | } 126 | 127 | return out; 128 | } 129 | /** 130 | * Inverts a mat2 131 | * 132 | * @param {mat2} out the receiving matrix 133 | * @param {ReadonlyMat2} a the source matrix 134 | * @returns {mat2} out 135 | */ 136 | 137 | export function invert(out, a) { 138 | var a0 = a[0], 139 | a1 = a[1], 140 | a2 = a[2], 141 | a3 = a[3]; // Calculate the determinant 142 | 143 | var det = a0 * a3 - a2 * a1; 144 | 145 | if (!det) { 146 | return null; 147 | } 148 | 149 | det = 1.0 / det; 150 | out[0] = a3 * det; 151 | out[1] = -a1 * det; 152 | out[2] = -a2 * det; 153 | out[3] = a0 * det; 154 | return out; 155 | } 156 | /** 157 | * Calculates the adjugate of a mat2 158 | * 159 | * @param {mat2} out the receiving matrix 160 | * @param {ReadonlyMat2} a the source matrix 161 | * @returns {mat2} out 162 | */ 163 | 164 | export function adjoint(out, a) { 165 | // Caching this value is necessary if out == a 166 | var a0 = a[0]; 167 | out[0] = a[3]; 168 | out[1] = -a[1]; 169 | out[2] = -a[2]; 170 | out[3] = a0; 171 | return out; 172 | } 173 | /** 174 | * Calculates the determinant of a mat2 175 | * 176 | * @param {ReadonlyMat2} a the source matrix 177 | * @returns {Number} determinant of a 178 | */ 179 | 180 | export function determinant(a) { 181 | return a[0] * a[3] - a[2] * a[1]; 182 | } 183 | /** 184 | * Multiplies two mat2's 185 | * 186 | * @param {mat2} out the receiving matrix 187 | * @param {ReadonlyMat2} a the first operand 188 | * @param {ReadonlyMat2} b the second operand 189 | * @returns {mat2} out 190 | */ 191 | 192 | export function multiply(out, a, b) { 193 | var a0 = a[0], 194 | a1 = a[1], 195 | a2 = a[2], 196 | a3 = a[3]; 197 | var b0 = b[0], 198 | b1 = b[1], 199 | b2 = b[2], 200 | b3 = b[3]; 201 | out[0] = a0 * b0 + a2 * b1; 202 | out[1] = a1 * b0 + a3 * b1; 203 | out[2] = a0 * b2 + a2 * b3; 204 | out[3] = a1 * b2 + a3 * b3; 205 | return out; 206 | } 207 | /** 208 | * Rotates a mat2 by the given angle 209 | * 210 | * @param {mat2} out the receiving matrix 211 | * @param {ReadonlyMat2} a the matrix to rotate 212 | * @param {Number} rad the angle to rotate the matrix by 213 | * @returns {mat2} out 214 | */ 215 | 216 | export function rotate(out, a, rad) { 217 | var a0 = a[0], 218 | a1 = a[1], 219 | a2 = a[2], 220 | a3 = a[3]; 221 | var s = Math.sin(rad); 222 | var c = Math.cos(rad); 223 | out[0] = a0 * c + a2 * s; 224 | out[1] = a1 * c + a3 * s; 225 | out[2] = a0 * -s + a2 * c; 226 | out[3] = a1 * -s + a3 * c; 227 | return out; 228 | } 229 | /** 230 | * Scales the mat2 by the dimensions in the given vec2 231 | * 232 | * @param {mat2} out the receiving matrix 233 | * @param {ReadonlyMat2} a the matrix to rotate 234 | * @param {ReadonlyVec2} v the vec2 to scale the matrix by 235 | * @returns {mat2} out 236 | **/ 237 | 238 | export function scale(out, a, v) { 239 | var a0 = a[0], 240 | a1 = a[1], 241 | a2 = a[2], 242 | a3 = a[3]; 243 | var v0 = v[0], 244 | v1 = v[1]; 245 | out[0] = a0 * v0; 246 | out[1] = a1 * v0; 247 | out[2] = a2 * v1; 248 | out[3] = a3 * v1; 249 | return out; 250 | } 251 | /** 252 | * Creates a matrix from a given angle 253 | * This is equivalent to (but much faster than): 254 | * 255 | * mat2.identity(dest); 256 | * mat2.rotate(dest, dest, rad); 257 | * 258 | * @param {mat2} out mat2 receiving operation result 259 | * @param {Number} rad the angle to rotate the matrix by 260 | * @returns {mat2} out 261 | */ 262 | 263 | export function fromRotation(out, rad) { 264 | var s = Math.sin(rad); 265 | var c = Math.cos(rad); 266 | out[0] = c; 267 | out[1] = s; 268 | out[2] = -s; 269 | out[3] = c; 270 | return out; 271 | } 272 | /** 273 | * Creates a matrix from a vector scaling 274 | * This is equivalent to (but much faster than): 275 | * 276 | * mat2.identity(dest); 277 | * mat2.scale(dest, dest, vec); 278 | * 279 | * @param {mat2} out mat2 receiving operation result 280 | * @param {ReadonlyVec2} v Scaling vector 281 | * @returns {mat2} out 282 | */ 283 | 284 | export function fromScaling(out, v) { 285 | out[0] = v[0]; 286 | out[1] = 0; 287 | out[2] = 0; 288 | out[3] = v[1]; 289 | return out; 290 | } 291 | /** 292 | * Returns a string representation of a mat2 293 | * 294 | * @param {ReadonlyMat2} a matrix to represent as a string 295 | * @returns {String} string representation of the matrix 296 | */ 297 | 298 | export function str(a) { 299 | return "mat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")"; 300 | } 301 | /** 302 | * Returns Frobenius norm of a mat2 303 | * 304 | * @param {ReadonlyMat2} a the matrix to calculate Frobenius norm of 305 | * @returns {Number} Frobenius norm 306 | */ 307 | 308 | export function frob(a) { 309 | return Math.hypot(a[0], a[1], a[2], a[3]); 310 | } 311 | /** 312 | * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix 313 | * @param {ReadonlyMat2} L the lower triangular matrix 314 | * @param {ReadonlyMat2} D the diagonal matrix 315 | * @param {ReadonlyMat2} U the upper triangular matrix 316 | * @param {ReadonlyMat2} a the input matrix to factorize 317 | */ 318 | 319 | export function LDU(L, D, U, a) { 320 | L[2] = a[2] / a[0]; 321 | U[0] = a[0]; 322 | U[1] = a[1]; 323 | U[3] = a[3] - L[2] * U[1]; 324 | return [L, D, U]; 325 | } 326 | /** 327 | * Adds two mat2's 328 | * 329 | * @param {mat2} out the receiving matrix 330 | * @param {ReadonlyMat2} a the first operand 331 | * @param {ReadonlyMat2} b the second operand 332 | * @returns {mat2} out 333 | */ 334 | 335 | export function add(out, a, b) { 336 | out[0] = a[0] + b[0]; 337 | out[1] = a[1] + b[1]; 338 | out[2] = a[2] + b[2]; 339 | out[3] = a[3] + b[3]; 340 | return out; 341 | } 342 | /** 343 | * Subtracts matrix b from matrix a 344 | * 345 | * @param {mat2} out the receiving matrix 346 | * @param {ReadonlyMat2} a the first operand 347 | * @param {ReadonlyMat2} b the second operand 348 | * @returns {mat2} out 349 | */ 350 | 351 | export function subtract(out, a, b) { 352 | out[0] = a[0] - b[0]; 353 | out[1] = a[1] - b[1]; 354 | out[2] = a[2] - b[2]; 355 | out[3] = a[3] - b[3]; 356 | return out; 357 | } 358 | /** 359 | * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) 360 | * 361 | * @param {ReadonlyMat2} a The first matrix. 362 | * @param {ReadonlyMat2} b The second matrix. 363 | * @returns {Boolean} True if the matrices are equal, false otherwise. 364 | */ 365 | 366 | export function exactEquals(a, b) { 367 | return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; 368 | } 369 | /** 370 | * Returns whether or not the matrices have approximately the same elements in the same position. 371 | * 372 | * @param {ReadonlyMat2} a The first matrix. 373 | * @param {ReadonlyMat2} b The second matrix. 374 | * @returns {Boolean} True if the matrices are equal, false otherwise. 375 | */ 376 | 377 | export function equals(a, b) { 378 | var a0 = a[0], 379 | a1 = a[1], 380 | a2 = a[2], 381 | a3 = a[3]; 382 | var b0 = b[0], 383 | b1 = b[1], 384 | b2 = b[2], 385 | b3 = b[3]; 386 | return Math.abs(a0 - b0) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)); 387 | } 388 | /** 389 | * Multiply each element of the matrix by a scalar. 390 | * 391 | * @param {mat2} out the receiving matrix 392 | * @param {ReadonlyMat2} a the matrix to scale 393 | * @param {Number} b amount to scale the matrix's elements by 394 | * @returns {mat2} out 395 | */ 396 | 397 | export function multiplyScalar(out, a, b) { 398 | out[0] = a[0] * b; 399 | out[1] = a[1] * b; 400 | out[2] = a[2] * b; 401 | out[3] = a[3] * b; 402 | return out; 403 | } 404 | /** 405 | * Adds two mat2's after multiplying each element of the second operand by a scalar value. 406 | * 407 | * @param {mat2} out the receiving vector 408 | * @param {ReadonlyMat2} a the first operand 409 | * @param {ReadonlyMat2} b the second operand 410 | * @param {Number} scale the amount to scale b's elements by before adding 411 | * @returns {mat2} out 412 | */ 413 | 414 | export function multiplyScalarAndAdd(out, a, b, scale) { 415 | out[0] = a[0] + b[0] * scale; 416 | out[1] = a[1] + b[1] * scale; 417 | out[2] = a[2] + b[2] * scale; 418 | out[3] = a[3] + b[3] * scale; 419 | return out; 420 | } 421 | /** 422 | * Alias for {@link mat2.multiply} 423 | * @function 424 | */ 425 | 426 | export var mul = multiply; 427 | /** 428 | * Alias for {@link mat2.subtract} 429 | * @function 430 | */ 431 | 432 | export var sub = subtract; -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/CopyShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Full-screen textured quad shader 3 | */ 4 | 5 | const CopyShader = { 6 | 7 | uniforms: { 8 | 9 | 'tDiffuse': { value: null }, 10 | 'opacity': { value: 1.0 } 11 | 12 | }, 13 | 14 | vertexShader: /* glsl */` 15 | 16 | varying vec2 vUv; 17 | 18 | void main() { 19 | 20 | vUv = uv; 21 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 22 | 23 | }`, 24 | 25 | fragmentShader: /* glsl */` 26 | 27 | uniform float opacity; 28 | 29 | uniform sampler2D tDiffuse; 30 | 31 | varying vec2 vUv; 32 | 33 | void main() { 34 | 35 | gl_FragColor = texture2D( tDiffuse, vUv ); 36 | gl_FragColor.a *= opacity; 37 | 38 | 39 | }` 40 | 41 | }; 42 | 43 | export { CopyShader }; 44 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/EffectComposer.js: -------------------------------------------------------------------------------- 1 | import { 2 | Clock, 3 | Vector2, 4 | WebGLRenderTarget 5 | } from 'three'; 6 | import { CopyShader } from './CopyShader.js'; 7 | import { ShaderPass } from './ShaderPass.js'; 8 | import { MaskPass } from './MaskPass.js'; 9 | import { ClearMaskPass } from './MaskPass.js'; 10 | 11 | class EffectComposer { 12 | 13 | constructor( renderer, renderTarget ) { 14 | 15 | this.renderer = renderer; 16 | 17 | if ( renderTarget === undefined ) { 18 | 19 | const size = renderer.getSize( new Vector2() ); 20 | this._pixelRatio = renderer.getPixelRatio(); 21 | this._width = size.width; 22 | this._height = size.height; 23 | 24 | renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio ); 25 | renderTarget.texture.name = 'EffectComposer.rt1'; 26 | 27 | } else { 28 | 29 | this._pixelRatio = 1; 30 | this._width = renderTarget.width; 31 | this._height = renderTarget.height; 32 | 33 | } 34 | 35 | this.renderTarget1 = renderTarget; 36 | this.renderTarget2 = renderTarget.clone(); 37 | this.renderTarget2.texture.name = 'EffectComposer.rt2'; 38 | 39 | this.writeBuffer = this.renderTarget1; 40 | this.readBuffer = this.renderTarget2; 41 | 42 | this.renderToScreen = true; 43 | 44 | this.passes = []; 45 | 46 | this.copyPass = new ShaderPass( CopyShader ); 47 | 48 | this.clock = new Clock(); 49 | 50 | } 51 | 52 | swapBuffers() { 53 | 54 | const tmp = this.readBuffer; 55 | this.readBuffer = this.writeBuffer; 56 | this.writeBuffer = tmp; 57 | 58 | } 59 | 60 | addPass( pass ) { 61 | 62 | this.passes.push( pass ); 63 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 64 | 65 | } 66 | 67 | insertPass( pass, index ) { 68 | 69 | this.passes.splice( index, 0, pass ); 70 | pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 71 | 72 | } 73 | 74 | removePass( pass ) { 75 | 76 | const index = this.passes.indexOf( pass ); 77 | 78 | if ( index !== - 1 ) { 79 | 80 | this.passes.splice( index, 1 ); 81 | 82 | } 83 | 84 | } 85 | 86 | isLastEnabledPass( passIndex ) { 87 | 88 | for ( let i = passIndex + 1; i < this.passes.length; i ++ ) { 89 | 90 | if ( this.passes[ i ].enabled ) { 91 | 92 | return false; 93 | 94 | } 95 | 96 | } 97 | 98 | return true; 99 | 100 | } 101 | 102 | render( deltaTime ) { 103 | 104 | // deltaTime value is in seconds 105 | 106 | if ( deltaTime === undefined ) { 107 | 108 | deltaTime = this.clock.getDelta(); 109 | 110 | } 111 | 112 | const currentRenderTarget = this.renderer.getRenderTarget(); 113 | 114 | let maskActive = false; 115 | 116 | for ( let i = 0, il = this.passes.length; i < il; i ++ ) { 117 | 118 | const pass = this.passes[ i ]; 119 | 120 | if ( pass.enabled === false ) continue; 121 | 122 | pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); 123 | pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); 124 | 125 | if ( pass.needsSwap ) { 126 | 127 | if ( maskActive ) { 128 | 129 | const context = this.renderer.getContext(); 130 | const stencil = this.renderer.state.buffers.stencil; 131 | 132 | //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); 133 | stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); 134 | 135 | this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); 136 | 137 | //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); 138 | stencil.setFunc( context.EQUAL, 1, 0xffffffff ); 139 | 140 | } 141 | 142 | this.swapBuffers(); 143 | 144 | } 145 | 146 | if ( MaskPass !== undefined ) { 147 | 148 | if ( pass instanceof MaskPass ) { 149 | 150 | maskActive = true; 151 | 152 | } else if ( pass instanceof ClearMaskPass ) { 153 | 154 | maskActive = false; 155 | 156 | } 157 | 158 | } 159 | 160 | } 161 | 162 | this.renderer.setRenderTarget( currentRenderTarget ); 163 | 164 | } 165 | 166 | reset( renderTarget ) { 167 | 168 | if ( renderTarget === undefined ) { 169 | 170 | const size = this.renderer.getSize( new Vector2() ); 171 | this._pixelRatio = this.renderer.getPixelRatio(); 172 | this._width = size.width; 173 | this._height = size.height; 174 | 175 | renderTarget = this.renderTarget1.clone(); 176 | renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); 177 | 178 | } 179 | 180 | this.renderTarget1.dispose(); 181 | this.renderTarget2.dispose(); 182 | this.renderTarget1 = renderTarget; 183 | this.renderTarget2 = renderTarget.clone(); 184 | 185 | this.writeBuffer = this.renderTarget1; 186 | this.readBuffer = this.renderTarget2; 187 | 188 | } 189 | 190 | setSize( width, height ) { 191 | 192 | this._width = width; 193 | this._height = height; 194 | 195 | const effectiveWidth = this._width * this._pixelRatio; 196 | const effectiveHeight = this._height * this._pixelRatio; 197 | 198 | this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); 199 | this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); 200 | 201 | for ( let i = 0; i < this.passes.length; i ++ ) { 202 | 203 | this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); 204 | 205 | } 206 | 207 | } 208 | 209 | setPixelRatio( pixelRatio ) { 210 | 211 | this._pixelRatio = pixelRatio; 212 | 213 | this.setSize( this._width, this._height ); 214 | 215 | } 216 | 217 | dispose() { 218 | 219 | this.renderTarget1.dispose(); 220 | this.renderTarget2.dispose(); 221 | 222 | this.copyPass.dispose(); 223 | 224 | } 225 | 226 | } 227 | 228 | export { EffectComposer }; 229 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/LuminosityHighPassShader.js: -------------------------------------------------------------------------------- 1 | import { 2 | Color 3 | } from 'three'; 4 | 5 | /** 6 | * Luminosity 7 | * http://en.wikipedia.org/wiki/Luminosity 8 | */ 9 | 10 | const LuminosityHighPassShader = { 11 | 12 | shaderID: 'luminosityHighPass', 13 | 14 | uniforms: { 15 | 16 | 'tDiffuse': { value: null }, 17 | 'luminosityThreshold': { value: 1.0 }, 18 | 'smoothWidth': { value: 1.0 }, 19 | 'defaultColor': { value: new Color( 0x000000 ) }, 20 | 'defaultOpacity': { value: 0.0 } 21 | 22 | }, 23 | 24 | vertexShader: /* glsl */` 25 | 26 | varying vec2 vUv; 27 | 28 | void main() { 29 | 30 | vUv = uv; 31 | 32 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 33 | 34 | }`, 35 | 36 | fragmentShader: /* glsl */` 37 | 38 | uniform sampler2D tDiffuse; 39 | uniform vec3 defaultColor; 40 | uniform float defaultOpacity; 41 | uniform float luminosityThreshold; 42 | uniform float smoothWidth; 43 | 44 | varying vec2 vUv; 45 | 46 | void main() { 47 | 48 | vec4 texel = texture2D( tDiffuse, vUv ); 49 | 50 | vec3 luma = vec3( 0.299, 0.587, 0.114 ); 51 | 52 | float v = dot( texel.xyz, luma ); 53 | 54 | vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity ); 55 | 56 | float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v ); 57 | 58 | gl_FragColor = mix( outputColor, texel, alpha ); 59 | 60 | }` 61 | 62 | }; 63 | 64 | export { LuminosityHighPassShader }; 65 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/MaskPass.js: -------------------------------------------------------------------------------- 1 | import { Pass } from './Pass.js'; 2 | 3 | class MaskPass extends Pass { 4 | 5 | constructor( scene, camera ) { 6 | 7 | super(); 8 | 9 | this.scene = scene; 10 | this.camera = camera; 11 | 12 | this.clear = true; 13 | this.needsSwap = false; 14 | 15 | this.inverse = false; 16 | 17 | } 18 | 19 | render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { 20 | 21 | const context = renderer.getContext(); 22 | const state = renderer.state; 23 | 24 | // don't update color or depth 25 | 26 | state.buffers.color.setMask( false ); 27 | state.buffers.depth.setMask( false ); 28 | 29 | // lock buffers 30 | 31 | state.buffers.color.setLocked( true ); 32 | state.buffers.depth.setLocked( true ); 33 | 34 | // set up stencil 35 | 36 | let writeValue, clearValue; 37 | 38 | if ( this.inverse ) { 39 | 40 | writeValue = 0; 41 | clearValue = 1; 42 | 43 | } else { 44 | 45 | writeValue = 1; 46 | clearValue = 0; 47 | 48 | } 49 | 50 | state.buffers.stencil.setTest( true ); 51 | state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); 52 | state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); 53 | state.buffers.stencil.setClear( clearValue ); 54 | state.buffers.stencil.setLocked( true ); 55 | 56 | // draw into the stencil buffer 57 | 58 | renderer.setRenderTarget( readBuffer ); 59 | if ( this.clear ) renderer.clear(); 60 | renderer.render( this.scene, this.camera ); 61 | 62 | renderer.setRenderTarget( writeBuffer ); 63 | if ( this.clear ) renderer.clear(); 64 | renderer.render( this.scene, this.camera ); 65 | 66 | // unlock color and depth buffer for subsequent rendering 67 | 68 | state.buffers.color.setLocked( false ); 69 | state.buffers.depth.setLocked( false ); 70 | 71 | // only render where stencil is set to 1 72 | 73 | state.buffers.stencil.setLocked( false ); 74 | state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 75 | state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); 76 | state.buffers.stencil.setLocked( true ); 77 | 78 | } 79 | 80 | } 81 | 82 | class ClearMaskPass extends Pass { 83 | 84 | constructor() { 85 | 86 | super(); 87 | 88 | this.needsSwap = false; 89 | 90 | } 91 | 92 | render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 93 | 94 | renderer.state.buffers.stencil.setLocked( false ); 95 | renderer.state.buffers.stencil.setTest( false ); 96 | 97 | } 98 | 99 | } 100 | 101 | export { MaskPass, ClearMaskPass }; 102 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/Pass.js: -------------------------------------------------------------------------------- 1 | import { 2 | BufferGeometry, 3 | Float32BufferAttribute, 4 | OrthographicCamera, 5 | Mesh 6 | } from 'three'; 7 | 8 | class Pass { 9 | 10 | constructor() { 11 | 12 | // if set to true, the pass is processed by the composer 13 | this.enabled = true; 14 | 15 | // if set to true, the pass indicates to swap read and write buffer after rendering 16 | this.needsSwap = true; 17 | 18 | // if set to true, the pass clears its buffer before rendering 19 | this.clear = false; 20 | 21 | // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. 22 | this.renderToScreen = false; 23 | 24 | } 25 | 26 | setSize( /* width, height */ ) {} 27 | 28 | render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { 29 | 30 | console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); 31 | 32 | } 33 | 34 | dispose() {} 35 | 36 | } 37 | 38 | // Helper for passes that need to fill the viewport with a single quad. 39 | 40 | const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); 41 | 42 | // https://github.com/mrdoob/three.js/pull/21358 43 | 44 | const _geometry = new BufferGeometry(); 45 | _geometry.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); 46 | _geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); 47 | 48 | class FullScreenQuad { 49 | 50 | constructor( material ) { 51 | 52 | this._mesh = new Mesh( _geometry, material ); 53 | 54 | } 55 | 56 | dispose() { 57 | 58 | this._mesh.geometry.dispose(); 59 | 60 | } 61 | 62 | render( renderer ) { 63 | 64 | renderer.render( this._mesh, _camera ); 65 | 66 | } 67 | 68 | get material() { 69 | 70 | return this._mesh.material; 71 | 72 | } 73 | 74 | set material( value ) { 75 | 76 | this._mesh.material = value; 77 | 78 | } 79 | 80 | } 81 | 82 | export { Pass, FullScreenQuad }; 83 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/RenderPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | Color 3 | } from 'three'; 4 | import { Pass } from './Pass.js'; 5 | 6 | class RenderPass extends Pass { 7 | 8 | constructor( scene, camera, overrideMaterial, clearColor, clearAlpha ) { 9 | 10 | super(); 11 | 12 | this.scene = scene; 13 | this.camera = camera; 14 | 15 | this.overrideMaterial = overrideMaterial; 16 | 17 | this.clearColor = clearColor; 18 | this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0; 19 | 20 | this.clear = true; 21 | this.clearDepth = false; 22 | this.needsSwap = false; 23 | this._oldClearColor = new Color(); 24 | 25 | } 26 | 27 | render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { 28 | 29 | const oldAutoClear = renderer.autoClear; 30 | renderer.autoClear = false; 31 | 32 | let oldClearAlpha, oldOverrideMaterial; 33 | 34 | if ( this.overrideMaterial !== undefined ) { 35 | 36 | oldOverrideMaterial = this.scene.overrideMaterial; 37 | 38 | this.scene.overrideMaterial = this.overrideMaterial; 39 | 40 | } 41 | 42 | if ( this.clearColor ) { 43 | 44 | renderer.getClearColor( this._oldClearColor ); 45 | oldClearAlpha = renderer.getClearAlpha(); 46 | 47 | renderer.setClearColor( this.clearColor, this.clearAlpha ); 48 | 49 | } 50 | 51 | if ( this.clearDepth ) { 52 | 53 | renderer.clearDepth(); 54 | 55 | } 56 | 57 | renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); 58 | 59 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 60 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); 61 | renderer.render( this.scene, this.camera ); 62 | 63 | if ( this.clearColor ) { 64 | 65 | renderer.setClearColor( this._oldClearColor, oldClearAlpha ); 66 | 67 | } 68 | 69 | if ( this.overrideMaterial !== undefined ) { 70 | 71 | this.scene.overrideMaterial = oldOverrideMaterial; 72 | 73 | } 74 | 75 | renderer.autoClear = oldAutoClear; 76 | 77 | } 78 | 79 | } 80 | 81 | export { RenderPass }; 82 | -------------------------------------------------------------------------------- /lib/thirdparty/threePostProcess/ShaderPass.js: -------------------------------------------------------------------------------- 1 | import { 2 | ShaderMaterial, 3 | UniformsUtils 4 | } from 'three'; 5 | import { Pass, FullScreenQuad } from './Pass.js'; 6 | 7 | class ShaderPass extends Pass { 8 | 9 | constructor( shader, textureID ) { 10 | 11 | super(); 12 | 13 | this.textureID = ( textureID !== undefined ) ? textureID : 'tDiffuse'; 14 | 15 | if ( shader instanceof ShaderMaterial ) { 16 | 17 | this.uniforms = shader.uniforms; 18 | 19 | this.material = shader; 20 | 21 | } else if ( shader ) { 22 | 23 | this.uniforms = UniformsUtils.clone( shader.uniforms ); 24 | 25 | this.material = new ShaderMaterial( { 26 | 27 | defines: Object.assign( {}, shader.defines ), 28 | uniforms: this.uniforms, 29 | vertexShader: shader.vertexShader, 30 | fragmentShader: shader.fragmentShader 31 | 32 | } ); 33 | 34 | } 35 | 36 | this.fsQuad = new FullScreenQuad( this.material ); 37 | 38 | } 39 | 40 | render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { 41 | 42 | if ( this.uniforms[ this.textureID ] ) { 43 | 44 | this.uniforms[ this.textureID ].value = readBuffer.texture; 45 | 46 | } 47 | 48 | this.fsQuad.material = this.material; 49 | 50 | if ( this.renderToScreen ) { 51 | 52 | renderer.setRenderTarget( null ); 53 | this.fsQuad.render( renderer ); 54 | 55 | } else { 56 | 57 | renderer.setRenderTarget( writeBuffer ); 58 | // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 59 | if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); 60 | this.fsQuad.render( renderer ); 61 | 62 | } 63 | 64 | } 65 | 66 | dispose() { 67 | 68 | this.material.dispose(); 69 | 70 | this.fsQuad.dispose(); 71 | 72 | } 73 | 74 | } 75 | 76 | export { ShaderPass }; 77 | -------------------------------------------------------------------------------- /lib/thirdparty/vector_tile_v2_1.proto: -------------------------------------------------------------------------------- 1 | package vector_tile; 2 | 3 | option optimize_for = LITE_RUNTIME; 4 | 5 | message Tile { 6 | 7 | // GeomType is described in section 4.3.4 of the specification 8 | enum GeomType { 9 | UNKNOWN = 0; 10 | POINT = 1; 11 | LINESTRING = 2; 12 | POLYGON = 3; 13 | } 14 | 15 | // Variant type encoding 16 | // The use of values is described in section 4.1 of the specification 17 | message Value { 18 | // Exactly one of these values must be present in a valid message 19 | optional string string_value = 1; 20 | optional float float_value = 2; 21 | optional double double_value = 3; 22 | optional int64 int_value = 4; 23 | optional uint64 uint_value = 5; 24 | optional sint64 sint_value = 6; 25 | optional bool bool_value = 7; 26 | 27 | extensions 8 to max; 28 | } 29 | 30 | // Features are described in section 4.2 of the specification 31 | message Feature { 32 | optional uint64 id = 1 [ default = 0 ]; 33 | 34 | // Tags of this feature are encoded as repeated pairs of 35 | // integers. 36 | // A detailed description of tags is located in sections 37 | // 4.2 and 4.4 of the specification 38 | repeated uint32 tags = 2 [ packed = true ]; 39 | 40 | // The type of geometry stored in this feature. 41 | optional GeomType type = 3 [ default = UNKNOWN ]; 42 | 43 | // Contains a stream of commands and parameters (vertices). 44 | // A detailed description on geometry encoding is located in 45 | // section 4.3 of the specification. 46 | repeated uint32 geometry = 4 [ packed = true ]; 47 | } 48 | 49 | // Layers are described in section 4.1 of the specification 50 | message Layer { 51 | // Any compliant implementation must first read the version 52 | // number encoded in this message and choose the correct 53 | // implementation for this version number before proceeding to 54 | // decode other parts of this message. 55 | required uint32 version = 15 [ default = 1 ]; 56 | 57 | required string name = 1; 58 | 59 | // The actual features in this tile. 60 | repeated Feature features = 2; 61 | 62 | // Dictionary encoding for keys 63 | repeated string keys = 3; 64 | 65 | // Dictionary encoding for values 66 | repeated Value values = 4; 67 | 68 | // Although this is an "optional" field it is required by the specification. 69 | // See https://github.com/mapbox/vector-tile-spec/issues/47 70 | optional uint32 extent = 5 [ default = 4096 ]; 71 | 72 | extensions 16 to max; 73 | } 74 | 75 | repeated Layer layers = 3; 76 | 77 | extensions 16 to 8191; 78 | } -------------------------------------------------------------------------------- /lib/useThreeComposer.js: -------------------------------------------------------------------------------- 1 | // #region IMPORTS 2 | import * as THREE from 'three'; 3 | import { OrbitControls } from 'OrbitControls'; 4 | import { EffectComposer } from 'postprocess/EffectComposer.js'; 5 | import { RenderPass } from 'postprocess/RenderPass.js'; 6 | export { THREE }; 7 | 8 | // #endregion 9 | 10 | /* 11 | 15 | 16 | const App = useThreeWebGL2(); 17 | App.scene.add( facedCube( [0,3,0], 6 ) ); 18 | App 19 | .sphericalLook( 45, 35, 40 ) 20 | .renderLoop(); 21 | */ 22 | 23 | 24 | // #region OPTIONS 25 | export function useDarkScene( tjs ){ 26 | // Light 27 | const light = new THREE.DirectionalLight( 0xffffff, 0.8 ); 28 | light.position.set( 4, 10, 1 ); 29 | tjs.scene.add( light ); 30 | 31 | tjs.scene.add( new THREE.AmbientLight( 0x404040 ) ); 32 | 33 | // Floor 34 | tjs.scene.add( new THREE.GridHelper( 20, 20, 0x0c610c, 0x444444 ) ); 35 | 36 | // Renderer 37 | // tjs.renderer.setClearColor( 0x3a3a3a, 1 ); 38 | return tjs; 39 | }; 40 | 41 | export async function useVisualDebug( tjs ){ 42 | const ary = await Promise.all([ 43 | import( './meshes/DynLineMesh.js' ), 44 | import( './meshes/ShapePointsMesh.js' ), 45 | ]); 46 | 47 | const o = {}; 48 | tjs.scene.add( ( o.ln = new ary[ 0 ].default ) ); 49 | tjs.scene.add( ( o.pnt = new ary[ 1 ].default ) ); 50 | return o; 51 | } 52 | // #endregion 53 | 54 | // #region MAIN 55 | export default function useThreeComposer(){ 56 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | // RENDERER 58 | const options = { 59 | antialias : true, 60 | alpha : true, 61 | }; 62 | 63 | const canvas = document.createElement( 'canvas' ); 64 | options.canvas = canvas; 65 | options.context = canvas.getContext( 'webgl2' ); 66 | 67 | const renderer = new THREE.WebGLRenderer( options ); 68 | renderer.setPixelRatio( window.devicePixelRatio ); 69 | renderer.setClearColor( 0x000000, 0 ); //0x3a3a3a 70 | // renderer.toneMapping = THREE.ReinhardToneMapping; 71 | document.body.appendChild( renderer.domElement ); 72 | 73 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 74 | // CORE 75 | const scene = new THREE.Scene(); 76 | const clock = new THREE.Clock(); 77 | clock.start(); 78 | 79 | const camera = new THREE.PerspectiveCamera( 45, 1.0, 0.01, 5000 ); 80 | camera.position.set( 0, 5, 20 ); 81 | 82 | const camCtrl = new OrbitControls( camera, renderer.domElement ); 83 | 84 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 85 | // POST EFFECTS 86 | const composer = new EffectComposer( renderer ); 87 | 88 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 89 | // METHODS 90 | let self; // Need to declare before methods for it to be useable 91 | 92 | const render = ( onPreRender=null, onPostRender=null ) =>{ 93 | const deltaTime = clock.getDelta(); 94 | const ellapseTime = clock.getElapsedTime(); 95 | 96 | if( onPreRender ) onPreRender( deltaTime, ellapseTime ); 97 | 98 | composer.render(); //renderer.render( scene, camera ); 99 | 100 | if( onPostRender ) onPostRender( deltaTime, ellapseTime ); 101 | return self; 102 | }; 103 | 104 | const renderLoop = ()=>{ 105 | window.requestAnimationFrame( renderLoop ); 106 | render(); 107 | return self; 108 | }; 109 | 110 | const sphericalLook = ( lon, lat, radius, target=null )=>{ 111 | const phi = ( 90 - lat ) * Math.PI / 180; 112 | const theta = ( lon + 180 ) * Math.PI / 180; 113 | 114 | camera.position.set( 115 | -(radius * Math.sin( phi ) * Math.sin(theta)), 116 | radius * Math.cos( phi ), 117 | -(radius * Math.sin( phi ) * Math.cos(theta)) 118 | ); 119 | 120 | if( target ) camCtrl.target.fromArray( target ); 121 | camCtrl.update(); 122 | return self; 123 | }; 124 | 125 | const resize = ( w=0, h=0 )=>{ 126 | const W = w || window.innerWidth; 127 | const H = h || window.innerHeight; 128 | 129 | renderer.setSize( W, H ); // Update Renderer 130 | composer.setSize( W, H ); 131 | 132 | camera.aspect = W / H; // Update Camera 133 | camera.updateProjectionMatrix(); 134 | 135 | return self; 136 | }; 137 | 138 | const getRendererSize = ()=>{ return renderer.getSize( new THREE.Vector2() ).toArray(); } 139 | 140 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 141 | 142 | window.addEventListener( 'resize', ()=>resize() ); 143 | resize(); 144 | 145 | return self = { 146 | renderer, 147 | scene, 148 | camera, 149 | camCtrl, 150 | composer, 151 | 152 | render, 153 | renderLoop, 154 | sphericalLook, 155 | resize, 156 | getRendererSize, 157 | 158 | addRenderPass : ( s=null, c=null )=>{ 159 | composer.addPass( new RenderPass( s || scene, c || camera ) ); 160 | return self; 161 | }, 162 | }; 163 | } 164 | // #endregion -------------------------------------------------------------------------------- /lib/useThreeWebGL2.js: -------------------------------------------------------------------------------- 1 | // #region IMPORTS 2 | import * as THREE from 'three'; 3 | import { OrbitControls } from 'OrbitControls'; 4 | export { THREE }; 5 | // #endregion 6 | 7 | /* 8 | 12 | 13 | const App = useThreeWebGL2(); 14 | App.scene.add( facedCube( [0,3,0], 6 ) ); 15 | App 16 | .sphericalLook( 45, 35, 40 ) 17 | .renderLoop(); 18 | */ 19 | 20 | 21 | // #region OPTIONS 22 | export function useDarkScene( tjs ){ 23 | // Light 24 | const light = new THREE.DirectionalLight( 0xffffff, 0.8 ); 25 | light.position.set( 4, 10, 1 ); 26 | tjs.scene.add( light ); 27 | 28 | tjs.scene.add( new THREE.AmbientLight( 0x404040 ) ); 29 | 30 | // Floor 31 | tjs.scene.add( new THREE.GridHelper( 20, 20, 0x0c610c, 0x444444 ) ); 32 | 33 | // Renderer 34 | tjs.renderer.setClearColor( 0x3a3a3a, 1 ); 35 | return tjs; 36 | }; 37 | 38 | export async function useVisualDebug( tjs ){ 39 | const ary = await Promise.all([ 40 | import( './meshes/DynLineMesh.js' ), 41 | import( './meshes/ShapePointsMesh.js' ), 42 | ]); 43 | 44 | const o = {}; 45 | tjs.scene.add( ( o.ln = new ary[ 0 ].default ) ); 46 | tjs.scene.add( ( o.pnt = new ary[ 1 ].default ) ); 47 | return o; 48 | } 49 | // #endregion 50 | 51 | // #region MAIN 52 | export default function useThreeWebGL2( props={} ){ 53 | props = Object.assign( { 54 | colorModeR3F : false, 55 | shadows : false, 56 | }, props ); 57 | 58 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 59 | // RENDERER 60 | const options = { 61 | antialias : true, 62 | alpha : true, 63 | // powerPreference: 'high-performance', 64 | }; 65 | const canvas = document.createElement( 'canvas' ); 66 | options.canvas = canvas; 67 | options.context = canvas.getContext( 'webgl2' ); 68 | 69 | const renderer = new THREE.WebGLRenderer( options ); 70 | renderer.setPixelRatio( window.devicePixelRatio ); 71 | renderer.setClearColor( 0x3a3a3a, 1 ); 72 | 73 | if( props.colorModeR3F ){ 74 | // React-Fiber changes the default settings which causes big issues trying to map colors 1:1 75 | // https://docs.pmnd.rs/react-three-fiber/api/canvas#render-defaults 76 | // https://threejs.org/docs/#manual/en/introduction/Color-management 77 | 78 | renderer.outputEncoding = THREE.sRGBEncoding; // Turns on sRGB Encoding & Gamma Correction :: THREE.LinearEncoding 79 | renderer.toneMapping = THREE.ACESFilmicToneMapping; // Try to make it close to HDR :: THREE.NoToneMapping 80 | THREE.ColorManagement.legacyMode = false; // Turns old 3JS's old color manager :: true 81 | } 82 | 83 | if( props.shadows ){ 84 | renderer.shadowMap.enabled = true; 85 | renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap 86 | } 87 | 88 | document.body.appendChild( renderer.domElement ); 89 | 90 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 91 | // CORE 92 | const scene = new THREE.Scene(); 93 | const clock = new THREE.Clock(); 94 | clock.start(); 95 | 96 | const camera = new THREE.PerspectiveCamera( 45, 1.0, 0.01, 5000 ); 97 | camera.position.set( 0, 5, 20 ); 98 | 99 | const camCtrl = new OrbitControls( camera, renderer.domElement ); 100 | 101 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 102 | // METHODS 103 | let self; // Need to declare before methods for it to be useable 104 | 105 | const render = ( onPreRender=null, onPostRender=null ) =>{ 106 | const deltaTime = clock.getDelta(); 107 | const ellapseTime = clock.getElapsedTime(); 108 | 109 | if( onPreRender ) onPreRender( deltaTime, ellapseTime ); 110 | renderer.render( scene, camera ); 111 | if( onPostRender ) onPostRender( deltaTime, ellapseTime ); 112 | return self; 113 | }; 114 | 115 | const renderLoop = ()=>{ 116 | window.requestAnimationFrame( renderLoop ); 117 | render(); 118 | return self; 119 | }; 120 | 121 | const createRenderLoop = ( fnPreRender=null, fnPostRender=null )=>{ 122 | let reqId = 0; 123 | 124 | const onRender = ()=>{ 125 | render( fnPreRender, fnPostRender ); 126 | reqId = window.requestAnimationFrame( onRender ); 127 | }; 128 | 129 | return { 130 | stop : () => window.cancelAnimationFrame( reqId ), 131 | start : () => onRender(), 132 | }; 133 | }; 134 | 135 | const sphericalLook = ( lon, lat, radius, target=null )=>{ 136 | const phi = ( 90 - lat ) * Math.PI / 180; 137 | const theta = ( lon + 180 ) * Math.PI / 180; 138 | 139 | camera.position.set( 140 | -(radius * Math.sin( phi ) * Math.sin(theta)), 141 | radius * Math.cos( phi ), 142 | -(radius * Math.sin( phi ) * Math.cos(theta)) 143 | ); 144 | 145 | if( target ) camCtrl.target.fromArray( target ); 146 | camCtrl.update(); 147 | return self; 148 | }; 149 | 150 | const resize = ( w=0, h=0 )=>{ 151 | const W = w || window.innerWidth; 152 | const H = h || window.innerHeight; 153 | 154 | renderer.setSize( W, H ); // Update Renderer 155 | 156 | camera.aspect = W / H; // Update Camera 157 | camera.updateProjectionMatrix(); 158 | return self; 159 | }; 160 | 161 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 162 | 163 | window.addEventListener( 'resize', ()=>resize() ); 164 | resize(); 165 | 166 | return self = { 167 | renderer, 168 | scene, 169 | camera, 170 | camCtrl, 171 | 172 | render, 173 | renderLoop, 174 | createRenderLoop, 175 | sphericalLook, 176 | resize, 177 | }; 178 | } 179 | // #endregion -------------------------------------------------------------------------------- /lib/useTransformControl.js: -------------------------------------------------------------------------------- 1 | import { AxesHelper } from 'three'; 2 | import { TransformControls } from 'TransformControls'; 3 | 4 | export default function useTransformControl( tjs ){ 5 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | const gizmo = new TransformControls( tjs.camera, tjs.renderer.domElement ); 7 | gizmo.setSpace( 'local' ); 8 | tjs.scene.add( gizmo ); 9 | 10 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | const self = { 12 | o : gizmo, 13 | onRotate : null, 14 | onMove : null, 15 | onStart : null, 16 | onStop : null, 17 | 18 | attach : o=>gizmo.attach( o ), 19 | detach : ()=>{ 20 | gizmo.detach(); 21 | if( self.axes ) self.axes.visible = false; 22 | }, 23 | 24 | toTranslate : ()=>gizmo.setMode( 'translate' ), 25 | toRotate : ()=>gizmo.setMode( 'rotate' ), 26 | 27 | setPos : p=>{ 28 | if( gizmo.object ) gizmo.object.position.fromArray( p ); 29 | return this; 30 | }, 31 | 32 | useAxes : ( s=0.5 )=>{ 33 | if( !self.axes ){ 34 | self.axes = new AxesHelper(); 35 | self.axes.scale.setScalar( s ); 36 | tjs.scene.add( self.axes ); 37 | } 38 | 39 | self.axes.visible = true; 40 | gizmo.attach( self.axes ); 41 | return self; 42 | }, 43 | }; 44 | 45 | const onDragChange = e=>{ 46 | if( tjs.camCtrl ) tjs.camCtrl.enabled = !e.value; 47 | 48 | if( e.value && self.onStart ) self.onStart(); 49 | else if( !e.value && self.onStop ) self.onStop(); 50 | }; 51 | 52 | const onChange = ()=>{ 53 | const o = gizmo.object; 54 | if(! (o && gizmo.dragging) ) return; 55 | 56 | switch( gizmo.mode ){ 57 | case 'translate': 58 | if( self.onMove ) self.onMove( o.position.toArray() ); 59 | break; 60 | 61 | case 'rotate': 62 | if( self.onRotate ) self.onRotate( o.quaternion.toArray() ); 63 | break; 64 | } 65 | }; 66 | 67 | gizmo.addEventListener( 'dragging-changed', onDragChange ); 68 | gizmo.addEventListener( 'change', onChange ); 69 | 70 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 | return self; 72 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "mapping", 3 | "version" : "0.0.0.1", 4 | "description" : "", 5 | "keywords" : [ "webgl", "threejs" ], 6 | "repository" : { "url": "https://github.com/sketchpunklabs/mapping.git", "type": "git" }, 7 | "author" : { "name": "Sketchpunk", "email": "tmp@tmp.com", "url": "http://tmp" }, 8 | "license" : "MIT", 9 | 10 | "devDependencies": { 11 | "browser-sync" : "^2.27.5" 12 | }, 13 | "scripts": { 14 | "start" : "browser-sync start --config bs-config.js" 15 | } 16 | } -------------------------------------------------------------------------------- /prototypes/001_slippy_mechanics.html: -------------------------------------------------------------------------------- 1 | 216 | 217 |
218 | [ Arrows ] to move tiles, [ W ] to Zoom In, [ S ] to Zoom out
219 | Note: Spin the scene around to see all the zoomed tiles hiding :) 220 |
221 | -------------------------------------------------------------------------------- /prototypes/002_texture_tile.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/003_building_tile.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/004_3d_terrain_tile.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/005_floating_origin.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/007_topographic.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/007_topographic_bloom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /prototypes/009_2dcanvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /prototypes/011_cubes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 314 | 315 |
316 |
Flatten Water:
317 | 318 | 319 |
Height:
320 | 321 | 322 | 323 |
Freq:
324 | 325 | 326 | 327 |
FBM:persistance
328 | 329 | 330 | 331 |
FBM:lacunarity
332 | 333 | 334 |
FBM:redistribution
335 | 336 | 337 |
FBM:octaves
338 | 339 |
340 | 341 | -------------------------------------------------------------------------------- /prototypes/012_heightmap_stamp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /prototypes/099_mapgl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 67 |
68 | 69 | -------------------------------------------------------------------------------- /prototypes/_template.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prototypes/info.txt: -------------------------------------------------------------------------------- 1 | https://cloud.google.com/blog/products/maps-platform/create-immersive-3d-map-experiences-photorealistic-3d-tiles 2 | 3 | https://developers.google.com/maps/documentation/tile/3d-tiles-overview 4 | 5 | https://developers.google.com/maps/documentation/tile/cloud-setup#console_1 6 | https://developers.google.com/maps/documentation/tile/get-api-key#whats-next 7 | https://console.cloud.google.com/welcome/new?project=third-expanse-387812 8 | 9 | https://developers.google.com/maps/documentation/tile/create-renderer 10 | 11 | API-Key 12 | AIzaSyAnd4qd3PGxOFc7g7F-33NHCqqPgXyf7U4 13 | 14 | https://www.youtube.com/watch?v=kxAwkT9M6rM -------------------------------------------------------------------------------- /prototypes/lib/CanvasOffscreen.js: -------------------------------------------------------------------------------- 1 | export default function CanvasOffscreen( w=256, h=256, pixelReading=true ){ 2 | // #region MAIN 3 | const WIDTH = w; 4 | const HEIGHT = h; 5 | const canvas = new OffscreenCanvas( w, h ); 6 | const ctx = canvas.getContext( '2d', { willReadFrequently:pixelReading } ); 7 | const self = { canvas, ctx, }; 8 | // #endregion 9 | 10 | // #region METHODS 11 | self.loadImage = ( img )=>{ ctx.drawImage( img, 0, 0, WIDTH, HEIGHT ); return self; } 12 | 13 | self.pixelAtUV = ( u, v )=>{ 14 | const x = Math.min( WIDTH-1, Math.max( Math.floor( WIDTH * u ), 0 ) ); 15 | const y = Math.min( HEIGHT-1, Math.max( Math.floor( HEIGHT * ( 1.0 - v ) ), 0 ) ); 16 | const px = ctx.getImageData( x, y, 1, 1 ).data; 17 | return px; 18 | }; 19 | // #endregion 20 | 21 | return self; 22 | } -------------------------------------------------------------------------------- /prototypes/lib/FetchBatch.js: -------------------------------------------------------------------------------- 1 | function nanoID( t=21 ){ 2 | const r = crypto.getRandomValues( new Uint8Array( t ) ); 3 | let n, e = ''; 4 | for( ;t--; ){ 5 | n = 63 & r[ t ]; 6 | e += ( n < 36 )? n.toString( 36 ) : 7 | ( n < 62 )? ( n - 26 ).toString( 36 ).toUpperCase() : 8 | ( n < 63 )? '_' : '-'; 9 | } 10 | return e; 11 | } 12 | 13 | /* 14 | const fetchQueue = new FetchQueue(); 15 | fetchQueue.push( 16 | 'http://blabla.bin', 17 | 'buffer', 18 | ( payload, url, extra )=>{}, 19 | 'extraDataForCallback' 20 | ); 21 | */ 22 | export default class FetchBatch{ 23 | // #region MAIN 24 | queue = []; // List of items to download 25 | listComplete = []; // List of items that are done 26 | asyncLimit = 2; // How many active downloads 27 | onDownload = null; // Callback for each download 28 | onComplete = null; // Callback when all the downloads are done 29 | _abortStack = new Map(); // Save Abort Controllers, Also doubles as how many fetches are currently active 30 | _itemCount = 0; // How many items in the batch 31 | _doneCount = 0; // How many items done being processed 32 | _isRunning = false; // Is Downloading queue currently running 33 | // #endregion 34 | 35 | // #region METHODS 36 | getSize(){ return this.queue.length; }; 37 | 38 | abort(){ 39 | for( const v of this._abortStack.values() ) v.abort(); 40 | this._abortStack.clear(); 41 | return this; 42 | } 43 | 44 | /** type = buffer | json */ 45 | add( url, type, extra=null ){ 46 | if( !this._isRunning ){ 47 | this.queue.push( { url, type, extra, payload:null } ); // Save to the queue 48 | this._itemCount++; 49 | } 50 | return this; 51 | } 52 | 53 | start(){ 54 | if( this._isRunning ) return this; 55 | this._isRunning = true; 56 | 57 | const min = Math.min( this.asyncLimit, this.queue.length ); 58 | for( let i = 0; i < min; i++ ){ 59 | this._next( this.queue.shift() ); 60 | } 61 | 62 | return this; 63 | } 64 | // #endregion 65 | 66 | async _next( itm ){ 67 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68 | if( !itm ){ 69 | if( this._doneCount === this._itemCount ){ 70 | this._isRunning = false; 71 | if( this.onComplete ) this.onComplete( this.listComplete ); 72 | } 73 | return; 74 | } 75 | 76 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 77 | const abortId = nanoID( 12 ); 78 | const abortCtrl = new AbortController(); 79 | this._abortStack.set( abortId, abortCtrl ); 80 | 81 | try{ 82 | const res = await fetch( itm.url, { signal: abortCtrl.signal } ); 83 | if( res.status === 200 ){ 84 | let payload; 85 | 86 | switch( itm.type ){ 87 | case 'buffer' : itm.payload = await res.arrayBuffer(); break; 88 | case 'json' : itm.payload = await res.json(); break; 89 | } 90 | 91 | this.listComplete.push( itm ); 92 | if( this.onDownload ) this.onDownload( itm ); 93 | }else{ 94 | console.error( 'Error Downloading: %s for %s', res.status, itm.url ); 95 | } 96 | }catch( ex ){ 97 | if( ex.name !== 'AbortError' ){ 98 | console.error( 'Error downloading arraybuffer/json :', ex.message ); 99 | console.error( '---', itm.url ); 100 | console.error( '--- stack trace', ex.stack ); 101 | } 102 | } 103 | 104 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 105 | // Download the next item on the queue. 106 | this._doneCount++; 107 | this._abortStack.delete( abortId ); 108 | this._next( this.queue.shift() ); 109 | } 110 | } -------------------------------------------------------------------------------- /prototypes/lib/FetchQueue.js: -------------------------------------------------------------------------------- 1 | function nanoID( t=21 ){ 2 | const r = crypto.getRandomValues( new Uint8Array( t ) ); 3 | let n, e = ''; 4 | for( ;t--; ){ 5 | n = 63 & r[ t ]; 6 | e += ( n < 36 )? n.toString( 36 ) : 7 | ( n < 62 )? ( n - 26 ).toString( 36 ).toUpperCase() : 8 | ( n < 63 )? '_' : '-'; 9 | } 10 | return e; 11 | } 12 | 13 | /* 14 | const fetchQueue = new FetchQueue(); 15 | fetchQueue.push( 16 | 'http://blabla.bin', 17 | 'buffer', 18 | ( payload, url, extra )=>{}, 19 | 'extraDataForCallback' 20 | ); 21 | */ 22 | export default class FetchQueue{ 23 | // #region MAIN 24 | queue = []; // List of items to download 25 | asyncLimit = 2; // How many active downloads 26 | _abortStack = new Map(); // Save Abort Controllers, Also doubles as how many fetches are currently active 27 | // #endregion 28 | 29 | // #region METHODS 30 | getSize(){ return this.queue.length; }; 31 | 32 | abort(){ 33 | for( const v of this._abortStack.values() ) v.abort(); 34 | this._abortStack.clear(); 35 | return this; 36 | } 37 | 38 | clearQueue(){ this.queue.length = 0; return this; } 39 | 40 | /** type = buffer | json */ 41 | push( url, type, cb, extra=null ){ 42 | this.queue.push( { url, type, cb, extra } ); // Save to the queue 43 | this._next(); // Start download process if one hasn't started 44 | return this; 45 | } 46 | // #endregion 47 | 48 | async _next(){ 49 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 50 | if( this._abortStack.size >= this.asyncLimit || this.queue.length === 0 ) return; 51 | 52 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 53 | const abortId = nanoID( 12 ); 54 | const abortCtrl = new AbortController(); 55 | this._abortStack.set( abortId, abortCtrl ); 56 | 57 | let itm; 58 | try{ 59 | itm = this.queue.shift(); 60 | const res = await fetch( itm.url, { signal: abortCtrl.signal } ); 61 | if( res.status === 200 ){ 62 | let payload; 63 | 64 | switch( itm.type ){ 65 | case 'buffer' : payload = await res.arrayBuffer(); break; 66 | case 'json' : payload = await res.json(); break; 67 | } 68 | 69 | itm.cb( payload, itm.url, itm.extra ); 70 | }else{ 71 | console.error( 'Error Downloading: %s for %s', res.status, itm.url ); 72 | } 73 | }catch( ex ){ 74 | if( ex.name !== 'AbortError' ){ 75 | console.error( 'Error downloading arraybuffer/json :', ex.message ); 76 | console.error( '---', itm.url ); 77 | console.error( '--- stack trace', ex.stack ); 78 | } 79 | } 80 | 81 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 82 | // Download the next item on the queue. 83 | this._abortStack.delete( abortId ); 84 | this._next(); 85 | } 86 | } -------------------------------------------------------------------------------- /prototypes/lib/MeshUtil.js: -------------------------------------------------------------------------------- 1 | import { 2 | DoubleSide, 3 | MeshBasicMaterial, 4 | PlaneGeometry, BufferGeometry, BufferAttribute, 5 | Mesh, 6 | } from 'three'; 7 | 8 | export default class MeshUtil{ 9 | 10 | static quad( mat, size=1, isUp=false, isCenter=true ){ 11 | const geo = new PlaneGeometry( size, size, 1, 1 ); 12 | if( isUp ) geo.rotateX( Math.PI * -0.5 ); 13 | 14 | if( !isCenter ){ 15 | if( isUp ) geo.translate( size*0.5, 0.0, size*0.5 ); 16 | else geo.translate( size*0.5, size*0.5, 0.0 ); 17 | } 18 | // 19 | 20 | mat = mat || new MeshBasicMaterial( { color:0x00ffff, side: DoubleSide } ); 21 | return new Mesh( geo, mat ); 22 | } 23 | 24 | static geo( { vertices=null, indices=null, normal=null, uv=null, color=null, skinWeight=null, skinIndex=null, skinSize=4 } = props ){ 25 | const geo = new BufferGeometry(); 26 | 27 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | // Data must be a TypeArray 29 | if( !( vertices instanceof Float32Array ) ) vertices = new Float32Array( vertices ); 30 | if( color && !( color instanceof Float32Array ) ) color = new Float32Array( color ); 31 | if( normal && !( normal instanceof Float32Array ) ) normal = new Float32Array( normal ); 32 | if( uv && !( uv instanceof Float32Array ) ) uv = new Float32Array( uv ); 33 | if( indices && !( indices instanceof Uint16Array ) ) indices = new Uint16Array( indices ); 34 | 35 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 | geo.setAttribute( 'position', new BufferAttribute( vertices, 3 ) ); 37 | 38 | if( indices ) geo.setIndex( new BufferAttribute( indices, 1 ) ); 39 | if( normal ) geo.setAttribute( 'normal', new BufferAttribute( norm, 3 ) ); 40 | if( uv ) geo.setAttribute( 'uv', new BufferAttribute( uv, 2 ) ); 41 | if( color ) geo.setAttribute( 'color', new BufferAttribute( color, 3 ) ); 42 | if( skinWeight ) geo.setAttribute( 'skinWeight', new BufferAttribute( skinWeight, skinSize ) ); 43 | if( skinIndex ) geo.setAttribute( 'skinIndex', new BufferAttribute( skinIndex, skinSize ) ); 44 | 45 | return geo; 46 | } 47 | 48 | static flipTriangleIndices( indices ){ 49 | let tmp; 50 | for( let i=0; i < indices.length; i+=3 ){ 51 | tmp = indices[ i ]; 52 | indices[ i ] = indices[ i+2 ]; 53 | indices[ i+2 ] = tmp; 54 | } 55 | } 56 | 57 | static reverseVertexLoop( vertices ){ 58 | const rtn = new Float32Array( vertices.length ); 59 | let ii = 0; 60 | for( let i = vertices.length-1; i >= 0; i -= 3 ){ 61 | rtn[ ii+0 ] = vertices[ i-2 ]; 62 | rtn[ ii+1 ] = vertices[ i-1 ]; 63 | rtn[ ii+2 ] = vertices[ i-0 ]; 64 | ii += 3; 65 | } 66 | return rtn; 67 | } 68 | 69 | static swopYZ( vertices ){ 70 | let tmp; 71 | for( let i=0; i < vertices.length; i+=3 ){ 72 | tmp = vertices[ i+1 ]; // Y 73 | vertices[ i+1 ] = vertices[ i+2 ]; // Z >> Y 74 | vertices[ i+2 ] = tmp; // Y >> Z 75 | } 76 | } 77 | 78 | static rotateVertsXN90( vertices ){ 79 | let tmp; 80 | for( let i=0; i < vertices.length; i+=3 ){ 81 | tmp = vertices[ i+1 ]; // Y 82 | vertices[ i+1 ] = vertices[ i+2 ]; // Z >> Y 83 | vertices[ i+2 ] = -tmp; // Y >> Z 84 | } 85 | } 86 | 87 | 88 | static isVertLoopClockwise( verts, useY=true ){ 89 | let sum = 0; 90 | const a = ( useY )? 1 : 2; 91 | const b = a + 3; 92 | for( let i=0; i < verts.length-3; i +=3 ){ 93 | sum += ( verts[i+3] - verts[i+0] ) * 94 | ( verts[i+b] + verts[i+a] ); 95 | } 96 | return !!( sum > 0 ); 97 | } 98 | } -------------------------------------------------------------------------------- /prototypes/lib/fetchArrayBuffer.js: -------------------------------------------------------------------------------- 1 | export default function fetchArrayBuffer( url ){ 2 | return new Promise( async ( resolve, reject )=>{ 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | // Get response 5 | const res = await fetch( url ); 6 | if( !res.ok ){ reject( res.status ); return; } 7 | 8 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | // Download Binary 10 | const payload = await res.arrayBuffer(); 11 | if( !payload ){ reject( 'Unable to download arraybuffer' ); return; } 12 | 13 | resolve( payload ); 14 | }); 15 | } -------------------------------------------------------------------------------- /prototypes/lib/fetchAsyncTexture.js: -------------------------------------------------------------------------------- 1 | import { Texture, ClampToEdgeWrapping, RepeatWrapping } from 'three'; 2 | import fetchImage from './fetchImage.js'; 3 | 4 | export default function fetchAsyncTexture( url, flipY=true, isRepeat=false ){ 5 | const tex = new Texture(); 6 | tex.wrapT = tex.wrapS = ( isRepeat )? ClampToEdgeWrapping : RepeatWrapping; 7 | tex.flipY = flipY; 8 | 9 | fetchImage( url ).then( ( img )=>{ 10 | tex.image = img; 11 | tex.needsUpdate = true; 12 | }); 13 | 14 | return tex; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /prototypes/lib/fetchImage.js: -------------------------------------------------------------------------------- 1 | export default function fetchImage( url ){ 2 | return new Promise( async ( resolve, reject )=>{ 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | // Get response 5 | const res = await fetch( url ); 6 | if( !res.ok ){ reject( res.status ); return; } 7 | 8 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | // Download Binary 10 | const blob = await res.blob(); 11 | if( !blob ){ reject( 'Unable to download image blob' ); return; } 12 | 13 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | // Convert to image 15 | const obj = URL.createObjectURL( blob ); 16 | const img = new Image(); 17 | 18 | img.crossOrigin = 'anonymous'; 19 | img.onload = ()=>{ URL.revokeObjectURL( obj ); resolve( img ); }; 20 | img.onerror = ()=>{ URL.revokeObjectURL( obj ); reject( 'Error loading object url into image' ); }; 21 | img.src = obj; 22 | }); 23 | } -------------------------------------------------------------------------------- /prototypes/lib/fetchJson.js: -------------------------------------------------------------------------------- 1 | export default function fetchJson( url ){ 2 | return new Promise( async ( resolve, reject )=>{ 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | // Get response 5 | const res = await fetch( url ); 6 | if( !res.ok ){ reject( res.status ); return; } 7 | 8 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | // Download Binary 10 | const json = await res.json(); 11 | if( !json ){ reject( 'Unable to download json' ); return; } 12 | 13 | resolve( json ); 14 | }); 15 | } -------------------------------------------------------------------------------- /prototypes/lib/fetchTexture.js: -------------------------------------------------------------------------------- 1 | import { Texture, ClampToEdgeWrapping, RepeatWrapping } from 'three'; 2 | import fetchImage from './fetchImage.js'; 3 | 4 | export default async function fetchTexture( url, flipY=true, isRepeat=false ){ 5 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | // Download image 7 | const img = await fetchImage( url ); 8 | if( !img ) return null; 9 | 10 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | // Make it a texture 12 | const tex = new Texture( img ); 13 | tex.wrapT = tex.wrapS = ( isRepeat )? RepeatWrapping : ClampToEdgeWrapping; 14 | tex.flipY = flipY; 15 | tex.needsUpdate = true; // Needed, else it may render as black 16 | return tex; 17 | } -------------------------------------------------------------------------------- /prototypes/res/heightmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/prototypes/res/heightmap.jpg -------------------------------------------------------------------------------- /prototypes/sweepWave.txt: -------------------------------------------------------------------------------- 1 | https://nodematerial-editor.babylonjs.com/ 2 | https://playground.babylonjs.com/#4QH8JM 3 | 4 | https://patrickryanms.github.io/BabylonJStextures/Demos/waveShader/assets/gltf/bars.glb 5 | 6 | let animZ = lerp( maxZ, minZ, animationTime ); 7 | let rngZ = animZ - fragZ; 8 | 9 | leadEdgeA = rngZ remap -0.3,0.5 to 1.0,0.0 10 | trailingEdgeA = rngZ remap -0.9,-0.1 to 1.0,0.0 -------------------------------------------------------------------------------- /tiles/0_2.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketchpunklabs/mapping/0940dbb8fd13e1330852abb9b47f593ece8157f0/tiles/0_2.glb -------------------------------------------------------------------------------- /tiles/root.json: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "version": "1.0" 4 | }, 5 | "geometricError": 1e+100, 6 | "root": { 7 | "boundingVolume": { 8 | "box": [ 9 | 0, 10 | 0, 11 | 0, 12 | 7972671.25, 13 | 0, 14 | 0, 15 | 0, 16 | 7972671.25, 17 | 0, 18 | 0, 19 | 0, 20 | 7945940.392806463 21 | ] 22 | }, 23 | "geometricError": 1e+100, 24 | "refine": "REPLACE", 25 | "transform": [ 26 | 1, 27 | 0, 28 | 0, 29 | 0, 30 | 0, 31 | 1, 32 | 0, 33 | 0, 34 | 0, 35 | 0, 36 | 1, 37 | 0, 38 | 0, 39 | 0, 40 | 0, 41 | 1 42 | ], 43 | "children": [ 44 | { 45 | "boundingVolume": { 46 | "box": [ 47 | 0, 48 | 0, 49 | 0, 50 | 7972671.25, 51 | 0, 52 | 0, 53 | 0, 54 | 7972671.25, 55 | 0, 56 | 0, 57 | 0, 58 | 7945940.392806463 59 | ] 60 | }, 61 | "geometricError": 1e+100, 62 | "refine": "REPLACE", 63 | "children": [ 64 | { 65 | "boundingVolume": { 66 | "box": [ 67 | 0, 68 | 0, 69 | 0, 70 | 7972671.25, 71 | 0, 72 | 0, 73 | 0, 74 | 7972671.25, 75 | 0, 76 | 0, 77 | 0, 78 | 7945940.392806463 79 | ] 80 | }, 81 | "geometricError": 1e+100, 82 | "content": { 83 | "uri": "/v1/3dtiles/datasets/CgA/files/UlRPVEYuYnVsa21ldGFkYXRhLnBsYW5ldG9pZD1lYXJ0aCxidWxrX21ldGFkYXRhX2Vwb2NoPTk1MyxwYXRoPSxjYWNoZV92ZXJzaW9uPTY.json?session=CMHIlMvhgMOP4QEQ18j_pQY" 84 | } 85 | } 86 | ], 87 | "extras": { 88 | "comment": "path = ''" 89 | } 90 | } 91 | ] 92 | }, 93 | "extensionsUsed": [ 94 | "3DTILES_content_gltf" 95 | ], 96 | "extensionsRequired": [ 97 | "3DTILES_content_gltf" 98 | ] 99 | } --------------------------------------------------------------------------------