├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── package-lock.json ├── package.json └── src └── client ├── api.ts ├── assets ├── act │ ├── annedepthmap.json.gz │ ├── annedepthmap.png │ ├── anneoliviadepth1.json.gz │ ├── anneoliviadepth1.png │ ├── anneoliviadepth10.json.gz │ ├── anneoliviadepth10.png │ ├── anneoliviadepth2.json.gz │ ├── anneoliviadepth2.png │ ├── anneoliviadepth3.json.gz │ ├── anneoliviadepth3.png │ ├── anneoliviadepth4.json.gz │ ├── anneoliviadepth4.png │ ├── anneoliviadepth5.json.gz │ ├── anneoliviadepth5.png │ ├── anneoliviadepth6.json.gz │ ├── anneoliviadepth6.png │ ├── anneoliviadepth7.json.gz │ ├── anneoliviadepth7.png │ ├── anneoliviadepth8.json.gz │ ├── anneoliviadepth8.png │ ├── anneoliviadepth9.json.gz │ ├── anneoliviadepth9.png │ ├── anneoriginal.jpg │ ├── anneoriginal.json.gz │ ├── morph1.jpg │ ├── morph1.json.gz │ ├── morph10.jpg │ ├── morph10.json.gz │ ├── morph2.jpg │ ├── morph2.json.gz │ ├── morph3.jpg │ ├── morph3.json.gz │ ├── morph4.jpg │ ├── morph4.json.gz │ ├── morph5.jpg │ ├── morph5.json.gz │ ├── morph6.jpg │ ├── morph6.json.gz │ ├── morph7.jpg │ ├── morph7.json.gz │ ├── morph8.jpg │ ├── morph8.json.gz │ ├── morph9.jpg │ ├── morph9.json.gz │ ├── oliviadepthmap.json.gz │ ├── oliviadepthmap.png │ ├── oliviaoriginal.jpeg │ └── oliviaoriginal.json.gz ├── ai │ ├── cand1.jpg │ ├── cand1.json.gz │ ├── cand3.jpg │ ├── cand3.json.gz │ ├── cand4.jpg │ ├── cand4.json.gz │ ├── canddepth1.jpg │ ├── canddepth1.json.gz │ ├── canddepth3.jpg │ ├── canddepth3.json.gz │ ├── canddepth4.jpg │ ├── canddepth4.json.gz │ ├── ryska │ │ ├── bus.jpg │ │ ├── bus.json.gz │ │ ├── busdepth.jpg │ │ ├── busdepth.json.gz │ │ ├── resistance.jpg │ │ ├── resistance.json.gz │ │ ├── resistancecity.jpg │ │ ├── resistancecity.json.gz │ │ ├── resistancecitydepth.jpg │ │ ├── resistancecitydepth.json.gz │ │ ├── resistancedepth.jpg │ │ ├── resistancedepth.json.gz │ │ ├── worm.jpg │ │ ├── worm.json.gz │ │ ├── wormdepth.jpg │ │ └── wormdepth.json.gz │ ├── stable1.jpg │ ├── stable1.json.gz │ ├── stable1depth.jpg │ └── stable1depth.json.gz ├── house │ ├── houseapic.json.gz │ ├── housemap.json.gz │ ├── housemap.png │ └── housepic.jpg └── koala │ ├── koala1.jpeg │ ├── koala1.json.gz │ ├── koala2.jpg │ ├── koala2.json.gz │ ├── morph1.jpg │ ├── morph10.jpg │ ├── morph2.jpg │ ├── morph3.jpg │ ├── morph4.jpg │ ├── morph5.jpg │ ├── morph6.jpg │ ├── morph7.jpg │ ├── morph8.jpg │ ├── morph9.jpg │ ├── morphoutput1.json.gz │ ├── morphoutput10.json.gz │ ├── morphoutput2.json.gz │ ├── morphoutput3.json.gz │ ├── morphoutput4.json.gz │ ├── morphoutput5.json.gz │ ├── morphoutput6.json.gz │ ├── morphoutput7.json.gz │ ├── morphoutput8.json.gz │ └── morphoutput9.json.gz ├── client.ts ├── cloudPointRenderer.ts ├── geometryHelper.ts ├── geometryUtil.ts ├── guiWrapper.ts ├── shader.ts ├── tsconfig.json ├── types.d.ts ├── webgl2 ├── WebGL2Renderer.ts ├── cameraControls.ts ├── drawObject │ ├── drawObject.ts │ ├── flightDrawObject.ts │ ├── flightVectorFieldDrawObject.ts │ ├── flowDrawObject.ts │ ├── simplexDrawObject.ts │ └── vectorFieldDrawObject.ts ├── flightHelper.ts ├── flowShaders.ts ├── glutils.ts ├── interpolationHelper.ts ├── interpolationWorker.ts ├── logger.ts ├── mathUtils.ts ├── program │ ├── arrowProgram.ts │ ├── flightProgram.ts │ ├── flowCalculationProgram.ts │ ├── flowHistoryProgram.ts │ ├── flowUpdateProgram.ts │ ├── program.ts │ ├── sampleSimplexProgram.ts │ └── simplexProgram.ts ├── scene.ts └── webglgui.ts ├── webpack.common.js ├── webpack.dev.js ├── webpack.prod.js └── worker.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "embeddedLanguageFormatting": "auto", 5 | "htmlWhitespaceSensitivity": "css", 6 | "insertPragma": false, 7 | "jsxBracketSameLine": false, 8 | "jsxSingleQuote": false, 9 | "printWidth": 100, 10 | "proseWrap": "preserve", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": false, 14 | "singleQuote": true, 15 | "tabWidth": 4, 16 | "trailingComma": "es5", 17 | "useTabs": false 18 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 adamblack 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Point clouds visualization with Three.js 2 | 3 | Showcase project about point clouds, depth maps, image morphing and Three.js. You can find more information about the project's background in my [blog post](https://medium.com/@adamias/point-clouds-visualization-with-three-js-5ef2a5e24587). 4 | 5 | Project also [contains examples](https://adamblack.github.io/point-cloud-demo/dist/client/vector.html) of how to compute / select the best path within the vector field. More detailed description is in [another blog post](https://medium.com/@adamias/vector-fields-and-fuel-consumption-wrapped-in-3d-475a8a9fd57c). 6 | 7 | ## Usage - Point clouds 8 | 9 | Live demo is already hosted at [Github pages](https://adamblack.github.io/point-cloud-demo/dist/client/index.html). 10 | 11 | In case you want to run project locally: 12 | 13 | ``` bash 14 | git clone git@github.com:adamblack/point-cloud-visualizer.git 15 | cd ./point-cloud-visualizer 16 | npm i 17 | npm run dev 18 | ``` 19 | 20 | Default scenario uses the same resources as the Live demo. If you want to use raw data for better quality, set variables in **geometryHelper.ts** as: 21 | 22 | ``` typescript 23 | // true and false - prod 24 | // true | false and true - dev -> i.e. provide images from your server in api pathPrefix settings 25 | // do not forget to edit imports 26 | useLocalAssets = true 27 | useRawData = true 28 | 29 | // if true, mesh generation is offloaded to worker and useLocalAssets/useRawData is ignored 30 | // used in prod, behaves like useLocalAssets = true, useRawData = false 31 | useWorker = false 32 | ``` 33 | 34 | Also, you have to use different set of imports (gzip ones) at **api.ts**. This rather clumsy setup process will be streamlined later. 35 | 36 | ## Usage - Vector fields 37 | 38 | ``` bash 39 | git clone git@github.com:adamblack/point-cloud-visualizer.git 40 | cd ./point-cloud-visualizer 41 | npm i 42 | npm run dev 43 | ``` 44 | 45 | In **client.ts**, just comment out init of CloudPointRenderer. 46 | 47 | ## Future plans 48 | 49 | - probably merge geometry generators methods 50 | - import of local resources via webpack should be streamlined 51 | - move even more operations to Web worker 52 | - investigate OffscreenCanvas and improve performance 53 | - a lot of new features 54 | 55 | 56 | ## Known issues 57 | 58 | - Some scenarios cause page reload on the iPhone while using Safari or Chrome 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "point-cloud-based-visualizer", 3 | "version": "0.1.0", 4 | "description": "", 5 | "scripts": { 6 | "build": "webpack --config ./src/client/webpack.prod.js", 7 | "dev": "webpack serve --config ./src/client/webpack.dev.js" 8 | }, 9 | "author": "adamblack", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.7", 13 | "@types/node": "^20.1.5", 14 | "@types/three": "^0.146.0", 15 | "@webgpu/types": "^0.1.31", 16 | "dat.gui": "^0.7.9", 17 | "three": "^0.146.0", 18 | "ts-loader": "^9.4.1", 19 | "typescript": "^4.8.4", 20 | "webpack": "^5.75.0", 21 | "webpack-cli": "^4.10.0", 22 | "webpack-dev-server": "^4.11.1", 23 | "webpack-merge": "^5.8.0" 24 | }, 25 | "dependencies": { 26 | "@types/offscreencanvas": "^2019.7.0", 27 | "@types/pako": "^2.0.0", 28 | "@webgpu/types": "^0.1.31", 29 | "gl-matrix": "^3.4.3", 30 | "nerdamer": "^1.1.13", 31 | "pako": "^2.1.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/client/api.ts: -------------------------------------------------------------------------------- 1 | import * as pako from 'pako' 2 | import * as THREE from 'three' 3 | 4 | // HD data source, local only 5 | 6 | // //farm 7 | // import housepic from "./assets/house/houseapic.json.gz" 8 | // import housemapzip from "./assets/house/housemap.json.gz" 9 | 10 | // //ai 11 | // import ai1 from "./assets/ai/stable1.json.gz" 12 | // import ai2 from "./assets/ai/stable1depth.json.gz" 13 | // import ai3 from "./assets/ai/ryska/worm.json.gz" 14 | // import ai4 from "./assets/ai/ryska/wormdepth.json.gz" 15 | // import ai5 from "./assets/ai/cand1.json.gz" 16 | // import ai6 from "./assets/ai/canddepth1.json.gz" 17 | // import ai7 from "./assets/ai/ryska/resistancecity.json.gz" 18 | // import ai8 from "./assets/ai/ryska/resistancecitydepth.json.gz" 19 | // import ai9 from "./assets/ai/cand3.json.gz" 20 | // import ai10 from "./assets/ai/canddepth3.json.gz" 21 | // import ai11 from "./assets/ai/cand4.json.gz" 22 | // import ai12 from "./assets/ai/canddepth4.json.gz" 23 | // import ai13 from "./assets/ai/ryska/bus.json.gz" 24 | // import ai14 from "./assets/ai/ryska/busdepth.json.gz" 25 | // import ai15 from "./assets/ai/ryska/resistance.json.gz" 26 | // import ai16 from "./assets/ai/ryska/resistancedepth.json.gz" 27 | 28 | // //act 29 | // import act1 from "./assets/act/anneoriginal.json.gz" 30 | // import act2 from "./assets/act/annedepthmap.json.gz" 31 | // import act3 from "./assets/act/oliviaoriginal.json.gz" 32 | // import act4 from "./assets/act/oliviadepthmap.json.gz" 33 | 34 | // import act5 from "./assets/act/morph1.json.gz" 35 | // import act6 from "./assets/act/morph2.json.gz" 36 | // import act7 from "./assets/act/morph3.json.gz" 37 | // import act8 from "./assets/act/morph4.json.gz" 38 | // import act9 from "./assets/act/morph5.json.gz" 39 | // import act10 from "./assets/act/morph6.json.gz" 40 | // import act11 from "./assets/act/morph7.json.gz" 41 | // import act12 from "./assets/act/morph8.json.gz" 42 | // import act13 from "./assets/act/morph9.json.gz" 43 | // import act14 from "./assets/act/morph10.json.gz" 44 | // import act15 from "./assets/act/anneoliviadepth1.json.gz" 45 | // import act16 from "./assets/act/anneoliviadepth2.json.gz" 46 | // import act17 from "./assets/act/anneoliviadepth3.json.gz" 47 | // import act18 from "./assets/act/anneoliviadepth4.json.gz" 48 | // import act19 from "./assets/act/anneoliviadepth5.json.gz" 49 | // import act20 from "./assets/act/anneoliviadepth6.json.gz" 50 | // import act21 from "./assets/act/anneoliviadepth7.json.gz" 51 | // import act22 from "./assets/act/anneoliviadepth8.json.gz" 52 | // import act23 from "./assets/act/anneoliviadepth9.json.gz" 53 | // import act24 from "./assets/act/anneoliviadepth10.json.gz" 54 | 55 | // //koala 56 | // import koala1 from "./assets/koala/koala1.json.gz" 57 | // import koala2 from "./assets/koala/koala2.json.gz" 58 | // import koala3 from "./assets/koala/morphoutput1.json.gz" 59 | // import koala4 from "./assets/koala/morphoutput2.json.gz" 60 | // import koala5 from "./assets/koala/morphoutput3.json.gz" 61 | // import koala6 from "./assets/koala/morphoutput4.json.gz" 62 | // import koala7 from "./assets/koala/morphoutput5.json.gz" 63 | // import koala8 from "./assets/koala/morphoutput6.json.gz" 64 | // import koala9 from "./assets/koala/morphoutput7.json.gz" 65 | // import koala10 from "./assets/koala/morphoutput8.json.gz" 66 | // import koala11 from "./assets/koala/morphoutput9.json.gz" 67 | // import koala12 from "./assets/koala/morphoutput10.json.gz" 68 | 69 | 70 | // Web friendly data source 71 | 72 | //farm 73 | import housepic from "./assets/house/housepic.jpg" 74 | import housemapzip from "./assets/house/housemap.png" 75 | 76 | 77 | //ai 78 | import ai1 from "./assets/ai/stable1.jpg" 79 | import ai2 from "./assets/ai/stable1depth.jpg" 80 | import ai3 from "./assets/ai/ryska/worm.jpg" 81 | import ai4 from "./assets/ai/ryska/wormdepth.jpg" 82 | import ai5 from "./assets/ai/cand1.jpg" 83 | import ai6 from "./assets/ai/canddepth1.jpg" 84 | import ai7 from "./assets/ai/ryska/resistancecity.jpg" 85 | import ai8 from "./assets/ai/ryska/resistancecitydepth.jpg" 86 | import ai9 from "./assets/ai/cand3.jpg" 87 | import ai10 from "./assets/ai/canddepth3.jpg" 88 | import ai11 from "./assets/ai/cand4.jpg" 89 | import ai12 from "./assets/ai/canddepth4.jpg" 90 | import ai13 from "./assets/ai/ryska/bus.jpg" 91 | import ai14 from "./assets/ai/ryska/busdepth.jpg" 92 | import ai15 from "./assets/ai/ryska/resistance.jpg" 93 | import ai16 from "./assets/ai/ryska/resistancedepth.jpg" 94 | 95 | //act 96 | import act1 from "./assets/act/anneoriginal.jpg" 97 | import act2 from "./assets/act/annedepthmap.png" 98 | import act3 from "./assets/act/oliviaoriginal.jpeg" 99 | import act4 from "./assets/act/oliviadepthmap.png" 100 | 101 | import act5 from "./assets/act/morph1.jpg" 102 | import act6 from "./assets/act/morph2.jpg" 103 | import act7 from "./assets/act/morph3.jpg" 104 | import act8 from "./assets/act/morph4.jpg" 105 | import act9 from "./assets/act/morph5.jpg" 106 | import act10 from "./assets/act/morph6.jpg" 107 | import act11 from "./assets/act/morph7.jpg" 108 | import act12 from "./assets/act/morph8.jpg" 109 | import act13 from "./assets/act/morph9.jpg" 110 | import act14 from "./assets/act/morph10.jpg" 111 | import act15 from "./assets/act/anneoliviadepth1.png" 112 | import act16 from "./assets/act/anneoliviadepth2.png" 113 | import act17 from "./assets/act/anneoliviadepth3.png" 114 | import act18 from "./assets/act/anneoliviadepth4.png" 115 | import act19 from "./assets/act/anneoliviadepth5.png" 116 | import act20 from "./assets/act/anneoliviadepth6.png" 117 | import act21 from "./assets/act/anneoliviadepth7.png" 118 | import act22 from "./assets/act/anneoliviadepth8.png" 119 | import act23 from "./assets/act/anneoliviadepth9.png" 120 | import act24 from "./assets/act/anneoliviadepth10.png" 121 | 122 | //koala 123 | import koala1 from "./assets/koala/koala1.jpeg" 124 | import koala2 from "./assets/koala/koala2.jpg" 125 | import koala3 from "./assets/koala/morph1.jpg" 126 | import koala4 from "./assets/koala/morph2.jpg" 127 | import koala5 from "./assets/koala/morph3.jpg" 128 | import koala6 from "./assets/koala/morph4.jpg" 129 | import koala7 from "./assets/koala/morph5.jpg" 130 | import koala8 from "./assets/koala/morph6.jpg" 131 | import koala9 from "./assets/koala/morph7.jpg" 132 | import koala10 from "./assets/koala/morph8.jpg" 133 | import koala11 from "./assets/koala/morph9.jpg" 134 | import koala12 from "./assets/koala/morph10.jpg" 135 | 136 | 137 | export enum ASSET_TYPE { 138 | FARM, AI, ACT, KOALA 139 | } 140 | 141 | export class Api { 142 | 143 | //Whatever server you might be running, not needed for usage of original site 144 | pathPrefix = 'http://127.0.0.1:8081/images/' 145 | localBundle = false 146 | aiAssetsList = [ai1, ai2, ai3, ai4, ai5, ai6, ai7, ai8, ai9, ai10, ai11, ai12, ai13, ai14, ai15, ai16] 147 | 148 | constructor() {} 149 | 150 | async getAssets(steps: number, type: ASSET_TYPE, useLocalBundle = false) : Promise { 151 | let folder: string = '' 152 | let imageUrls: any[] = [] 153 | 154 | switch(type) { 155 | case ASSET_TYPE.FARM: { 156 | if (!useLocalBundle) { 157 | folder = 'house/' 158 | imageUrls = ['houseapic.json', 'housemap.json'] 159 | } else { 160 | imageUrls = [housepic, housemapzip] 161 | } 162 | break 163 | } 164 | case ASSET_TYPE.AI: { 165 | if (!useLocalBundle) { 166 | folder = 'ai/' 167 | imageUrls = ['stable1.json', 'stable1depth.json', 'ryska/worm.json', 'ryska/wormdepth.json', 'cand1.json', 'canddepth1.json', 'ryska/resistancecity.json', 'ryska/resistancecitydepth.json', 'cand3.json', 'canddepth3.json', 'cand4.json', 'canddepth4.json', 'ryska/bus.json', 'ryska/busdepth.json', 'ryska/resistance.json', 'ryska/resistancedepth.json'] 168 | } else { 169 | imageUrls = this.aiAssetsList 170 | } 171 | break 172 | } 173 | case ASSET_TYPE.ACT: { 174 | // need debug! 175 | if (!useLocalBundle) { 176 | folder = 'act/' 177 | imageUrls = ['anneoriginal.json', 'annedepthmap.json', 'oliviaoriginal.json', 'oliviadepthmap.json'] 178 | 179 | for(let i = 1; i <= steps; i++) { 180 | imageUrls.push('anneolivia' + i + '.json') 181 | } 182 | 183 | for(let i = 1; i <= steps; i++) { 184 | imageUrls.push('anneoliviadepth' + i + '.json') 185 | } 186 | } else { 187 | imageUrls = [act1, act2, act3, act4, act5, act6, act7, act8, act9, act10, act11, act12, act13, act14, act15, act16, act17, act18, act19, act20, act21, act22, act23, act24] 188 | } 189 | break 190 | } 191 | case ASSET_TYPE.KOALA: { 192 | if (!useLocalBundle) { 193 | folder = 'koala/' 194 | imageUrls = ['koala1.json', 'koala2.json'] 195 | for(let i = 1; i <= steps; i++) { 196 | imageUrls.push('morphoutput' + i + '.json') 197 | } 198 | } else { 199 | imageUrls = [koala1, koala2, koala3, koala4, koala5, koala6, koala7, koala8, koala9, koala10, koala11, koala12 ] 200 | } 201 | break 202 | } 203 | } 204 | 205 | if (useLocalBundle) { 206 | const imageZips = await this.fetch(folder, imageUrls, true, false) 207 | return await this.decom(imageZips) 208 | } else { 209 | return await this.fetch(folder, imageUrls) 210 | } 211 | } 212 | 213 | async decom(imageZips: any[]) { 214 | let imageJsons: any[] = [] 215 | for (let i = 0; i < imageZips.length; i++) { 216 | let fileReader = await imageZips[i].arrayBuffer(); 217 | const binData = new Uint8Array(fileReader); 218 | var parsed = pako.ungzip(binData,{ 'to': 'string' }); 219 | imageJsons.push(JSON.parse(parsed)) 220 | } 221 | 222 | return imageJsons 223 | } 224 | 225 | async fetch(folder: string, imageUrls: string[], useAbsolute = false, asJson = true) : Promise{ 226 | let finalPrefix = '' 227 | if (!useAbsolute) { 228 | finalPrefix = this.pathPrefix + folder 229 | } 230 | const imageJsons = await Promise.all(imageUrls.map(async url => { 231 | const response = await fetch(finalPrefix + url, { headers: { 'Accept-Encoding': 'gzip', 'accept': 'application/json; charset=utf8;' } }) 232 | 233 | if (asJson) { 234 | return response.json() 235 | } else { 236 | return response 237 | } 238 | }) 239 | ) 240 | 241 | return imageJsons 242 | } 243 | 244 | async fetchImages(steps: number, type: ASSET_TYPE, useLocalBundle = false) : Promise { 245 | let folder: string = '' 246 | let imageUrls: any[] = [] 247 | 248 | switch(type) { 249 | case ASSET_TYPE.FARM: { 250 | if (!useLocalBundle) { 251 | folder = 'house/' 252 | imageUrls = ['housepic.jpg', 'housemap.png'] 253 | } else { 254 | imageUrls = [housepic, housemapzip] 255 | } 256 | break 257 | } 258 | case ASSET_TYPE.AI: { 259 | if (!useLocalBundle) { 260 | folder = 'ai/' 261 | imageUrls = ['stable1.png', 'stable1depth.png', 'ryska/worm.png', 'ryska/wormdepth.png', 'cand1.jpg', 'canddepth1.jpg', 'ryska/resistancecity.png', 'ryska/resistancecitydepth.png', 'cand3.jpg', 'canddepth3.jpg', 'cand4.jpg', 'canddepth4.png', 'ryska/bus.png', 'ryska/busdepth.png', 'ryska/resistance.png', 'ryska/resistancedepth.png'] 262 | } else { 263 | imageUrls = [ai1, ai2, ai3, ai4, ai5, ai6, ai7, ai8, ai9, ai10, ai11, ai12, ai13, ai14, ai15, ai16] 264 | } 265 | break 266 | } 267 | case ASSET_TYPE.ACT: { 268 | if (!useLocalBundle) { 269 | folder = 'act/' 270 | imageUrls = ['anneoriginal.jpg', 'annedepthmap.png', 'oliviaoriginal.jpeg', 'oliviadepthmap.png'] 271 | 272 | for(let i = 1; i <= steps; i++) { 273 | imageUrls.push('morph' + i + '.jpg') 274 | } 275 | 276 | for(let i = 1; i <= steps; i++) { 277 | imageUrls.push('anneoliviadepth' + i + '.png') 278 | } 279 | } else { 280 | imageUrls = [act1, act2, act3, act4, act5, act6, act7, act8, act9, act10, act11, act12, act13, act14, act15, act16, act17, act18, act19, act20, act21, act22, act23, act24] 281 | } 282 | break 283 | } 284 | case ASSET_TYPE.KOALA: { 285 | if (!useLocalBundle) { 286 | folder = 'koala/' 287 | imageUrls = ['koala1.jpeg', 'koala2.jpg'] 288 | for(let i = 1; i <= steps; i++) { 289 | imageUrls.push('morph' + i + '.jpg') 290 | } 291 | } else { 292 | imageUrls = [koala1, koala2, koala3, koala4, koala5, koala6, koala7, koala8, koala9, koala10, koala11, koala12 ] 293 | } 294 | break 295 | } 296 | } 297 | 298 | return await this.makePromise(folder, imageUrls, useLocalBundle) 299 | } 300 | 301 | async makePromise(folder: string, urls: string[], useAbsolute = false) { 302 | let finalPrefix = '' 303 | if (!useAbsolute) { 304 | finalPrefix = this.pathPrefix + folder 305 | } 306 | 307 | return new Promise((resolve, reject) => { 308 | let textures: any = []; 309 | let onLoad = function () { 310 | resolve(textures); 311 | }; 312 | let onProgress = function () { }; 313 | let onError = function (url: string) { 314 | resolve([]); 315 | }; 316 | 317 | let manager = new THREE.LoadingManager(onLoad, onProgress, onError); 318 | for (let i = 0; i < urls.length; i++) { 319 | let url = finalPrefix + urls[i]; 320 | let loader = new THREE.TextureLoader(manager); 321 | textures[i] = loader.load(url); 322 | } 323 | }) 324 | } 325 | } -------------------------------------------------------------------------------- /src/client/assets/act/annedepthmap.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/annedepthmap.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/annedepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/annedepthmap.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth1.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth1.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth10.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth10.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth10.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth2.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth2.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth2.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth3.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth3.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth3.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth4.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth4.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth4.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth5.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth5.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth5.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth6.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth6.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth6.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth7.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth7.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth7.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth8.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth8.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth8.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth9.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth9.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/anneoliviadepth9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoliviadepth9.png -------------------------------------------------------------------------------- /src/client/assets/act/anneoriginal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoriginal.jpg -------------------------------------------------------------------------------- /src/client/assets/act/anneoriginal.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/anneoriginal.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph1.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph1.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph10.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph10.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph10.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph2.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph2.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph2.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph3.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph3.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph3.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph4.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph4.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph4.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph5.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph5.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph5.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph6.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph6.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph6.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph7.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph7.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph7.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph8.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph8.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph8.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/morph9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph9.jpg -------------------------------------------------------------------------------- /src/client/assets/act/morph9.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/morph9.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/oliviadepthmap.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/oliviadepthmap.json.gz -------------------------------------------------------------------------------- /src/client/assets/act/oliviadepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/oliviadepthmap.png -------------------------------------------------------------------------------- /src/client/assets/act/oliviaoriginal.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/oliviaoriginal.jpeg -------------------------------------------------------------------------------- /src/client/assets/act/oliviaoriginal.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/act/oliviaoriginal.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/cand1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand1.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/cand1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand1.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/cand3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand3.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/cand3.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand3.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/cand4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand4.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/cand4.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/cand4.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth1.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth1.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth3.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth3.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth3.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth4.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/canddepth4.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/canddepth4.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/bus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/bus.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/bus.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/bus.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/busdepth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/busdepth.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/busdepth.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/busdepth.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistance.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistance.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistance.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistance.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancecity.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancecity.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancecity.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancecity.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancecitydepth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancecitydepth.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancecitydepth.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancecitydepth.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancedepth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancedepth.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/resistancedepth.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/resistancedepth.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/worm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/worm.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/worm.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/worm.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/wormdepth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/wormdepth.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/ryska/wormdepth.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/ryska/wormdepth.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/stable1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/stable1.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/stable1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/stable1.json.gz -------------------------------------------------------------------------------- /src/client/assets/ai/stable1depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/stable1depth.jpg -------------------------------------------------------------------------------- /src/client/assets/ai/stable1depth.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/ai/stable1depth.json.gz -------------------------------------------------------------------------------- /src/client/assets/house/houseapic.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/house/houseapic.json.gz -------------------------------------------------------------------------------- /src/client/assets/house/housemap.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/house/housemap.json.gz -------------------------------------------------------------------------------- /src/client/assets/house/housemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/house/housemap.png -------------------------------------------------------------------------------- /src/client/assets/house/housepic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/house/housepic.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/koala1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/koala1.jpeg -------------------------------------------------------------------------------- /src/client/assets/koala/koala1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/koala1.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/koala2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/koala2.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/koala2.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/koala2.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morph1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph1.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph10.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph2.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph3.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph4.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph5.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph6.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph7.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph8.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morph9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morph9.jpg -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput1.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput10.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput10.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput2.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput2.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput3.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput3.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput4.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput4.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput5.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput5.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput6.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput6.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput7.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput7.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput8.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput8.json.gz -------------------------------------------------------------------------------- /src/client/assets/koala/morphoutput9.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamblack/point-cloud-visualizer/27fe214632a44432b7f0fc616f687b5b50307dca/src/client/assets/koala/morphoutput9.json.gz -------------------------------------------------------------------------------- /src/client/client.ts: -------------------------------------------------------------------------------- 1 | import { CloudPointRenderer } from './cloudPointRenderer' 2 | import { WebGL2Renderer } from './webgl2/WebGL2Renderer' 3 | 4 | // Point cloud project 5 | // const cloudPointRenderer = new CloudPointRenderer() 6 | // cloudPointRenderer.start() 7 | 8 | // Vector field project 9 | const webGL2Renderer = new WebGL2Renderer() 10 | webGL2Renderer.init() -------------------------------------------------------------------------------- /src/client/cloudPointRenderer.ts: -------------------------------------------------------------------------------- 1 | import Stats from 'three/examples/jsm/libs/stats.module' 2 | import * as THREE from 'three' 3 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 4 | import { GuiWrapper } from './guiWrapper' 5 | import { GeometryHelper } from './geometryHelper' 6 | 7 | export class CloudPointRenderer { 8 | stats = Stats() 9 | clock = new THREE.Clock() 10 | scene = new THREE.Scene() 11 | camera = new THREE.PerspectiveCamera( 12 | 80, 13 | window.innerWidth / window.innerHeight, 14 | 0.1, 15 | 100 16 | ) 17 | guiWrapper!: GuiWrapper 18 | geometryHelper!: GeometryHelper 19 | currentSceneObject : THREE.Points | THREE.Mesh | undefined 20 | renderer = new THREE.WebGLRenderer({ antialias: true }) 21 | 22 | parallaxUniforms = { 23 | u_time: { type: "f", value: 1.0 }, 24 | u_texture: {type: 't', value: null}, 25 | u_use_texture: {type: 'b', value: null}, 26 | u_minZ: { type: "f", value: 1.0 }, 27 | u_maxZ: { type: "f", value: 1.0 }, 28 | u_camera_angle: { type: "vec3", value: new THREE.Vector3(0, 0, 0) }, 29 | u_scale: { type: "f", value: 1.0 }, 30 | // 0 - no parallax, 1 - sin/cos mapping, 2 - dynamic, follows camera 31 | u_parallax_type: { type: "i", value: 0 }, 32 | } 33 | 34 | morphValue = 0; 35 | morphDirection = 1; 36 | stepCounter = 0 37 | interpolationStep = 0.01 38 | lastCameraPosition = new THREE.Vector3(0, 0, 0) 39 | 40 | interpolationOptions = { 41 | auto: false, 42 | frame: 0, 43 | scale: 0.2, 44 | resetCurrent: () => { 45 | this.resetCurrentFrame() 46 | }, 47 | reset: () => { 48 | this.resetState() 49 | }, 50 | invert: () => { 51 | this.invertState() 52 | } 53 | } 54 | 55 | textureOptions = { 56 | textureUsage: false, 57 | } 58 | 59 | renderingOptions = { 60 | type: 'Farm', 61 | } 62 | 63 | parallaxOptions = { 64 | type: 'Auto' 65 | } 66 | 67 | // In general we would rather use events 68 | callbackOptions = { 69 | renderSelectedMesh: (meshType: string) => { 70 | this.renderSelectedMesh(meshType) 71 | }, 72 | scaleScene: (scale: number) => { 73 | if (this.currentSceneObject == undefined) { 74 | return 75 | } 76 | this.currentSceneObject.scale.z = scale 77 | }, 78 | handleParallaxSelection: (parallaxType: string) => { 79 | this.handleParallaxSelection(parallaxType) 80 | }, 81 | moveToFrame: (frame: number) => { 82 | this.moveToFrame(frame) 83 | } 84 | } 85 | 86 | 87 | constructor() { 88 | document.body.appendChild(this.stats.dom) 89 | this.camera.position.z = 3 90 | 91 | this.renderer.setSize(window.innerWidth, window.innerHeight) 92 | document.body.appendChild(this.renderer.domElement) 93 | 94 | const controls = new OrbitControls(this.camera, this.renderer.domElement) 95 | controls.screenSpacePanning = true 96 | 97 | const onWindowResize = () => { 98 | this.camera.aspect = window.innerWidth / window.innerHeight 99 | this.camera.updateProjectionMatrix() 100 | this.renderer.setSize(window.innerWidth, window.innerHeight) 101 | this.render() 102 | } 103 | window.addEventListener('resize', onWindowResize, false) 104 | } 105 | 106 | start() { 107 | this.guiWrapper = new GuiWrapper(this.interpolationOptions, this.textureOptions, this.renderingOptions, this.parallaxOptions, this.callbackOptions) 108 | this.geometryHelper = new GeometryHelper(this.guiWrapper, this.parallaxUniforms) 109 | this.guiWrapper.triggerInitialScene() 110 | this.animate() 111 | } 112 | 113 | async renderSelectedMesh(sceneType: any) { 114 | //switch gallery 115 | if (this.currentSceneObject != undefined) { 116 | this.scene.remove(this.currentSceneObject) 117 | this.currentSceneObject.geometry.dispose() 118 | this.currentSceneObject.clear() 119 | } 120 | 121 | this.guiWrapper.addOptions() 122 | 123 | // move to geometryHelper 124 | if (this.geometryHelper.useWorker) { 125 | const generatedMeshData = await this.geometryHelper.generatePointsByWorker() 126 | const renderedType = this.guiWrapper.renderingType!.getValue() 127 | 128 | if (generatedMeshData.centerPositions[renderedType] != undefined){ 129 | this.geometryHelper.imgCenterPoints[renderedType] = [] 130 | for (let i = 0; i < generatedMeshData.centerPositions[renderedType].length; i++) { 131 | this.geometryHelper.imgCenterPoints[renderedType].push(new THREE.Vector3( 132 | generatedMeshData.centerPositions[renderedType][i].x, 133 | generatedMeshData.centerPositions[renderedType][i].y, 134 | generatedMeshData.centerPositions[renderedType][i].z 135 | )) 136 | } 137 | } 138 | 139 | this.parallaxUniforms.u_minZ.value = generatedMeshData.minZ 140 | this.parallaxUniforms.u_maxZ.value = generatedMeshData.maxZ 141 | this.currentSceneObject = generatedMeshData.mesh 142 | } else { 143 | this.currentSceneObject = await this.geometryHelper.generatePoints() 144 | } 145 | 146 | this.currentSceneObject.scale.set( 5, 5, this.interpolationOptions.scale ); 147 | this.scene.add(this.currentSceneObject) 148 | 149 | this.clock.start() 150 | } 151 | 152 | handleParallaxSelection(value: string) { 153 | switch(value) { 154 | case 'None': { 155 | this.parallaxUniforms.u_parallax_type.value = 0 156 | break 157 | } 158 | case 'Auto': { 159 | this.parallaxUniforms.u_parallax_type.value = 2 160 | break 161 | } 162 | case 'Dynamic': { 163 | this.parallaxUniforms.u_parallax_type.value = 1 164 | break 165 | } 166 | } 167 | } 168 | 169 | animate() { 170 | 171 | requestAnimationFrame(this.animate.bind(this)) 172 | 173 | if (this.interpolationOptions.auto) { 174 | this.interpolate() 175 | } 176 | 177 | this.parallaxUniforms.u_time.value = this.clock.getElapsedTime() 178 | 179 | if (this.guiWrapper.renderingType != undefined && 180 | this.geometryHelper.imgCenterPoints[this.guiWrapper.renderingType.getValue()] != undefined && 181 | this.lastCameraPosition.angleTo(this.camera.position) != 0 && 182 | this.currentSceneObject != undefined 183 | ) { 184 | const imgCenterPoint = this.geometryHelper.imgCenterPoints[this.guiWrapper.renderingType.getValue()][this.interpolationOptions.frame] 185 | this.lastCameraPosition = this.camera.position.clone() 186 | 187 | let angleX = this.camera.position.clone().setY(0).angleTo(imgCenterPoint as THREE.Vector3) 188 | let angleY = this.camera.position.clone().setX(0).angleTo(imgCenterPoint) 189 | 190 | let normalX = new THREE.Plane().setFromCoplanarPoints(new THREE.Vector3(), this.camera.position.clone().setY(0), imgCenterPoint).normal 191 | let normalY = new THREE.Plane().setFromCoplanarPoints(new THREE.Vector3(), this.camera.position.clone().setX(0), imgCenterPoint).normal 192 | 193 | this.parallaxUniforms.u_scale.value = 1 + this.currentSceneObject.scale.z 194 | this.parallaxUniforms.u_camera_angle.value = new THREE.Vector3(-angleX*normalX.y, angleY*normalY.x, 0) 195 | } 196 | 197 | this.render() 198 | this.stats.update() 199 | } 200 | 201 | resetState(updateDirection: boolean = true) { 202 | this.morphValue = 0; 203 | if (updateDirection) { 204 | this.guiWrapper.updateGui(0) 205 | this.morphDirection = 1; 206 | } 207 | this.stepCounter = 0 208 | for(let i = 0; i < this.geometryHelper.steps+1; i++) { 209 | if (this.currentSceneObject != undefined) { 210 | this.currentSceneObject.morphTargetInfluences![i] = 0 211 | } 212 | } 213 | } 214 | 215 | invertState() { 216 | this.morphDirection *= -1 217 | } 218 | 219 | moveToFrame(frame: number) { 220 | this.resetState(false) 221 | this.stepCounter = frame 222 | if (frame != 0 && this.currentSceneObject != undefined) { 223 | this.currentSceneObject.morphTargetInfluences![frame - 1] = 1 224 | this.stepCounter = frame-1 225 | } 226 | } 227 | 228 | resetCurrentFrame() { 229 | this.moveToFrame(this.stepCounter+1) 230 | } 231 | 232 | interpolate() { 233 | if (!this.currentSceneObject) { 234 | return 235 | } 236 | this.currentSceneObject.morphTargetInfluences![this.stepCounter] = this.morphValue 237 | if (this.stepCounter > 0 && this.morphDirection == 1) { 238 | this.currentSceneObject.morphTargetInfluences![this.stepCounter - 1] = 1 - this.morphValue 239 | } else if (this.stepCounter < this.geometryHelper.steps && this.morphDirection == -1) { 240 | this.currentSceneObject.morphTargetInfluences![this.stepCounter + 1] = 1 - this.morphValue 241 | } 242 | this.morphValue += this.interpolationStep * Math.abs(this.morphDirection); 243 | if (this.morphValue > 1 || this.morphValue < 0) { 244 | if ((this.stepCounter == this.geometryHelper.steps && this.morphDirection == 1) || (this.stepCounter == 0 && this.morphDirection == -1)) { 245 | this.morphDirection *= -1; 246 | } 247 | 248 | this.stepCounter += 1*this.morphDirection 249 | this.guiWrapper.updateGui(this.stepCounter) 250 | this.morphValue = 0 251 | } 252 | } 253 | 254 | render() { 255 | this.renderer.render(this.scene, this.camera) 256 | } 257 | } -------------------------------------------------------------------------------- /src/client/geometryHelper.ts: -------------------------------------------------------------------------------- 1 | import { GuiWrapper } from "./guiWrapper" 2 | import * as THREE from 'three' 3 | import * as SHADERS from './shader' 4 | import { Api, ASSET_TYPE } from "./api" 5 | import * as geometryUtils from "./geometryUtil" 6 | 7 | export class GeometryHelper { 8 | 9 | steps = 10 10 | imgCenterPoints: {[sceneType: string] : Array}= {} 11 | imgMinZ: number = 100 12 | imgMaxZ: number = -100 13 | api = new Api() 14 | 15 | // true and false - prod 16 | // true | false and true - dev -> i.e. provide images from your server in api pathPrefix settings 17 | // do not forget to edit imports 18 | useLocalAssets = true 19 | useRawData = false 20 | 21 | // if true, mesh generation is offloaded to worker and useLocalAssets/useRawData is ignored 22 | // used in prod, behaves like useLocalAssets = true, useRawData = false 23 | useWorker = true 24 | 25 | constructor(public guiWrapper: GuiWrapper, public parallaxUniforms: any) { 26 | 27 | } 28 | 29 | // Method used for local setting - main thread 30 | async generatePoints() : Promise{ 31 | let geometry: THREE.BufferGeometry | undefined 32 | this.steps = 10 33 | 34 | const target = this.guiWrapper.renderingType?.getValue() 35 | switch(target) { 36 | case 'Waifus': { 37 | if (this.useRawData) { 38 | const imageJsons = await this.api.getAssets(this.steps, ASSET_TYPE.ACT, this.useLocalAssets) 39 | geometry = geometryUtils.getActBufferGeometry(new geometryUtils.ScenarioDataWrapper([], imageJsons), this.steps) 40 | } else { 41 | let textures : any[] = await this.api.fetchImages(0, ASSET_TYPE.ACT, true); 42 | let imageData = await this.textureToImageData(textures) 43 | geometry = geometryUtils.getActBufferGeometry(new geometryUtils.ScenarioDataWrapper(imageData), this.steps) 44 | } 45 | this.guiWrapper.updateFrameCounter(this.steps + 1) 46 | break 47 | } 48 | case 'Farm': { 49 | let generatedWrapper : geometryUtils.GeneratedMeshWrapper 50 | if (this.useRawData) { 51 | const imageJsons = await this.api.getAssets(0, ASSET_TYPE.FARM, this.useLocalAssets) 52 | generatedWrapper = geometryUtils.getFarmBufferGeometry(new geometryUtils.ScenarioDataWrapper([], imageJsons), this.imgCenterPoints, target) 53 | } else { 54 | let textures : any[] = await this.api.fetchImages(0, ASSET_TYPE.FARM, true); 55 | let imageData = await this.textureToImageData(textures) 56 | generatedWrapper = geometryUtils.getFarmBufferGeometry(new geometryUtils.ScenarioDataWrapper(imageData), this.imgCenterPoints, target) 57 | } 58 | geometry = generatedWrapper.geometry 59 | this.imgMinZ = generatedWrapper.imgMinZ 60 | this.imgMaxZ = generatedWrapper.imgMaxZ 61 | this.guiWrapper.updateFrameCounter(2) 62 | break 63 | } 64 | case 'Koala': { 65 | if (this.useRawData) { 66 | const imageJsons = await this.api.getAssets(this.steps, ASSET_TYPE.KOALA, this.useLocalAssets) 67 | geometry = geometryUtils.getKoalaBufferGeometry(new geometryUtils.ScenarioDataWrapper([], imageJsons), this.steps) 68 | } else { 69 | let textures : any[] = await this.api.fetchImages(0, ASSET_TYPE.KOALA, true); 70 | let imageData = await this.textureToImageData(textures) 71 | geometry = geometryUtils.getKoalaBufferGeometry(new geometryUtils.ScenarioDataWrapper(imageData), this.steps) 72 | } 73 | this.guiWrapper.updateFrameCounter(this.steps + 1) 74 | break 75 | } 76 | case 'ai_images': { 77 | if (this.useRawData) { 78 | const imageJsons = await this.api.getAssets(0, ASSET_TYPE.AI, this.useLocalAssets) 79 | geometry = geometryUtils.getAIImagesBufferGeometry(new geometryUtils.ScenarioDataWrapper([], imageJsons), this.imgCenterPoints, target) 80 | } else { 81 | let textures : any[] = await this.api.fetchImages(0, ASSET_TYPE.AI, true); 82 | let imageData = await this.textureToImageData(textures) 83 | geometry = geometryUtils.getAIImagesBufferGeometry(new geometryUtils.ScenarioDataWrapper(imageData), this.imgCenterPoints, target) 84 | } 85 | this.guiWrapper.updateFrameCounter(this.api.aiAssetsList.length/2 - 1) 86 | break 87 | } 88 | } 89 | 90 | const shaderMaterial = new THREE.ShaderMaterial({ 91 | uniforms: this.parallaxUniforms, 92 | vertexShader: SHADERS.VERTEX_SHADER, 93 | fragmentShader: SHADERS.FRAGMENT_SHADER 94 | }) 95 | 96 | //Cache in case of heavy/default usage 97 | if (this.guiWrapper.textureOptions.textureUsage) { 98 | const loadedTexture = new THREE.TextureLoader().load( 'http://127.0.0.1:8081/images/house/houseapic.jpg' ); 99 | shaderMaterial.uniforms.u_texture.value = loadedTexture 100 | } 101 | shaderMaterial.uniforms.u_use_texture.value = this.guiWrapper.textureOptions.textureUsage 102 | shaderMaterial.uniforms.u_minZ.value = this.imgMinZ 103 | shaderMaterial.uniforms.u_maxZ.value = this.imgMaxZ 104 | 105 | return new THREE.Points(geometry, shaderMaterial); 106 | } 107 | 108 | // Method used for prod setting - geometry creation in Webworker 109 | async generatePointsByWorker() : Promise<{mesh: THREE.Points | THREE.Mesh, centerPositions: {[sceneType: string] : Array}, minZ: number, maxZ: number}>{ 110 | let geometry: THREE.BufferGeometry | undefined 111 | this.steps = 10 112 | 113 | const shaderMaterial = new THREE.ShaderMaterial({ 114 | uniforms: this.parallaxUniforms, 115 | vertexShader: SHADERS.VERTEX_SHADER, 116 | fragmentShader: SHADERS.FRAGMENT_SHADER 117 | }) 118 | 119 | //Cache in case of heavy/default usage 120 | if (this.guiWrapper.textureOptions.textureUsage) { 121 | const loadedTexture = new THREE.TextureLoader().load( 'http://127.0.0.1:8081/images/house/houseapic.jpg' ); 122 | shaderMaterial.uniforms.u_texture.value = loadedTexture 123 | } 124 | shaderMaterial.uniforms.u_use_texture.value = this.guiWrapper.textureOptions.textureUsage 125 | 126 | let textures : any[] | undefined 127 | let targetGeometry = this.guiWrapper.renderingType?.getValue() 128 | let stepsNumber = this.steps 129 | let centerPointToUpdate = this.imgCenterPoints 130 | 131 | switch(targetGeometry) { 132 | case 'Waifus': { 133 | textures = await this.api.fetchImages(this.steps, ASSET_TYPE.ACT, true); 134 | this.guiWrapper.updateFrameCounter(this.steps + 1) 135 | break 136 | } 137 | case 'Farm': { 138 | textures = await this.api.fetchImages(0, ASSET_TYPE.FARM, true); 139 | this.guiWrapper.updateFrameCounter(2) 140 | break 141 | } 142 | case 'Koala': { 143 | textures = await this.api.fetchImages(this.steps, ASSET_TYPE.KOALA, true); 144 | this.guiWrapper.updateFrameCounter(this.steps + 1) 145 | break 146 | } 147 | case 'ai_images': { 148 | textures = await this.api.fetchImages(0, ASSET_TYPE.AI, true); 149 | this.guiWrapper.updateFrameCounter(7) 150 | break 151 | } 152 | } 153 | 154 | let imageData = await this.textureToImageData(textures!) 155 | 156 | let geometryPromise = new Promise<{mesh: THREE.Points | THREE.Mesh, centerPositions: {[sceneType: string] : Array}, minZ: number, maxZ: number}>(function(resolve, reject) { 157 | let worker = new Worker(new URL('worker.ts', import.meta.url)) 158 | worker.postMessage({imageData, targetGeometry, stepsNumber, centerPointToUpdate}); 159 | worker.onmessage = function(event){ 160 | const workerGeometry = event.data.geometry 161 | const geometry = new THREE.BufferGeometry() 162 | 163 | for ( let attributeName of Object.keys( workerGeometry.attributes ) ) { 164 | 165 | const workerAttribute = workerGeometry.attributes[ attributeName ] 166 | 167 | const attribute = new THREE.BufferAttribute( 168 | workerAttribute.array, 169 | workerAttribute.itemSize, 170 | false 171 | ); 172 | 173 | geometry.setAttribute( attributeName, attribute ) 174 | 175 | } 176 | 177 | geometry.morphAttributes.position = [] 178 | geometry.morphAttributes.color = [] 179 | 180 | for ( let attributeName of Object.keys( workerGeometry.morphAttributes ) ) { 181 | for ( let i = 0; i < workerGeometry.morphAttributes[ attributeName ].length; i++ ) { 182 | 183 | const workerAttribute = workerGeometry.morphAttributes[ attributeName ][i]; 184 | 185 | const attribute = new THREE.BufferAttribute( 186 | workerAttribute.array, 187 | workerAttribute.itemSize, 188 | false 189 | ) 190 | geometry.morphAttributes[attributeName][i] = attribute 191 | } 192 | } 193 | 194 | const points = new THREE.Points(geometry, shaderMaterial) 195 | resolve({mesh: points, centerPositions: event.data.centerPointToUpdate, minZ: event.data.imgMinZ, maxZ: event.data.imgMaxZ}); 196 | } 197 | }) 198 | 199 | return geometryPromise 200 | 201 | } 202 | 203 | async textureToImageData(textures: THREE.Texture[]) : Promise { 204 | let imageData: ImageData[] = [] 205 | const canvas = document.createElement( 'canvas' ) 206 | 207 | for (let i = 0; i < textures.length; i++) { 208 | 209 | let tex = textures[i] 210 | 211 | canvas.width = tex.image.width 212 | canvas.height = tex.image.height 213 | // @ts-ignore 214 | // do more testing with offscreen - safari seems still behind 215 | // let offscreencanvas = canvas.transferControlToOffscreen() 216 | const context = canvas.getContext( '2d' ); 217 | context?.drawImage( tex.image, 0, 0 ) 218 | 219 | const data = context?.getImageData( 0, 0, canvas.width, canvas.height ) 220 | imageData.push(data!) 221 | 222 | tex.dispose() 223 | } 224 | 225 | canvas.width = 0; 226 | canvas.height = 0; 227 | canvas.remove() 228 | 229 | return imageData 230 | } 231 | } -------------------------------------------------------------------------------- /src/client/geometryUtil.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | 3 | let grayscaleNormalizationSize = 10000 4 | let colorSpaceNormalizationSize = 16777215 / 5 5 | let aiImagesNormalizationSize = 30000 6 | 7 | let POS_NUM = 3 8 | let COL_NUM = 3 9 | let UV_NUM = 2 10 | 11 | export type GeneratedMeshWrapper = { 12 | geometry: THREE.BufferGeometry, 13 | imgMinZ: number, 14 | imgMaxZ: number 15 | } 16 | 17 | export function getFarmBufferGeometry(scenarioDataWrapper: ScenarioDataWrapper, imgCenterPoints: {[sceneType: string] : Array}, targetGeometry: string) : GeneratedMeshWrapper { 18 | 19 | const geometry = new THREE.BufferGeometry() 20 | const height = scenarioDataWrapper.getHeightOfFrame(0) 21 | const width = scenarioDataWrapper.getWidthOfFrame(0) 22 | const numOfPoints = width*height 23 | 24 | const pointUv = new Float32Array(numOfPoints*2); 25 | const pointsToRender = new Float32Array(numOfPoints*3); 26 | const pointsToRenderMapped = new Float32Array(numOfPoints*3); 27 | const pointsToRenderFlat = new Float32Array(numOfPoints*3); 28 | const pointsColors = new Float32Array(numOfPoints*3); 29 | 30 | let pointPointer = 0 31 | let imgMinZ: number = 100 32 | let imgMaxZ: number = -100 33 | 34 | let centerDepth = scenarioDataWrapper.getCenterValueAt(1) 35 | 36 | // Do not forget that we are adding 3 versions of the image 37 | imgCenterPoints[targetGeometry] = [ 38 | new THREE.Vector3(0,0,centerDepth / grayscaleNormalizationSize), 39 | new THREE.Vector3(0,0,centerDepth / grayscaleNormalizationSize), 40 | new THREE.Vector3(0,0,centerDepth / grayscaleNormalizationSize) 41 | ] 42 | 43 | for(let i = 0; i < height; i++) { 44 | for(let j = 0; j < width; j++) { 45 | 46 | const u = i/width 47 | const v = j/width 48 | 49 | // Default view - depth map 50 | pointsToRender[pointPointer*3 + 1] = -u + (height/width)/2 51 | pointsToRender[pointPointer*3] = v - 0.5 52 | pointsToRender[pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(1, i, j, pointPointer*4) / 30 53 | 54 | 55 | // values passed to vertex shader for parallax 56 | if (pointsToRender[pointPointer*3 + 2] < imgMinZ) { 57 | imgMinZ = pointsToRender[pointPointer*3 + 2] 58 | } 59 | if (pointsToRender[pointPointer*3 + 2] > imgMaxZ) { 60 | imgMaxZ = pointsToRender[pointPointer*3 + 2] 61 | } 62 | 63 | // Custom RGB mapping 64 | pointsToRenderMapped[pointPointer*3 + 1] = -u + (height/width)/2 65 | pointsToRenderMapped[pointPointer*3] = v - 0.5 66 | pointsToRenderMapped[pointPointer*3 + 2] = rgbToInt([scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4, true, 0), scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 1, true, 1), scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 2, true, 2)]) / colorSpaceNormalizationSize 67 | 68 | // Standard image 69 | pointsToRenderFlat[pointPointer*3 + 1] = -u + (height/width)/2 70 | pointsToRenderFlat[pointPointer*3] = v - 0.5 71 | pointsToRenderFlat[pointPointer*3 + 2] = 0 72 | 73 | pointsColors[pointPointer*3] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4, true, 0) / 255 74 | pointsColors[pointPointer*3 + 1] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 1, true, 1) / 255 75 | pointsColors[pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 2, true, 2) / 255 76 | 77 | pointPointer++ 78 | } 79 | } 80 | 81 | // if (this.guiWrapper.textureOptions.textureUsage) { 82 | // geometry.setAttribute('uv', new THREE.BufferAttribute(pointUv, this.UV_NUM)) 83 | // } else { 84 | // geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors, this.COL_NUM)) 85 | // } 86 | geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors, COL_NUM)) 87 | geometry.setAttribute('position', new THREE.BufferAttribute(pointsToRender, POS_NUM)) 88 | 89 | geometry.morphAttributes.position = [] 90 | geometry.morphAttributes.color = [] 91 | 92 | // // Add final destination image 93 | geometry.morphAttributes.position[0] = new THREE.BufferAttribute(pointsToRenderFlat, POS_NUM) 94 | geometry.morphAttributes.color[0] = new THREE.BufferAttribute(pointsColors, COL_NUM) 95 | 96 | geometry.morphAttributes.position[1] = new THREE.BufferAttribute(pointsToRenderMapped, POS_NUM) 97 | geometry.morphAttributes.color[1] = new THREE.BufferAttribute(pointsColors, COL_NUM) 98 | 99 | return {geometry, imgMinZ, imgMaxZ} 100 | } 101 | 102 | 103 | export function getKoalaBufferGeometry(scenarioDataWrapper: ScenarioDataWrapper, steps: number) : THREE.BufferGeometry { 104 | 105 | const arrayMorphOffset = 2 106 | 107 | const geometry = new THREE.BufferGeometry() 108 | 109 | const height = scenarioDataWrapper.getHeightOfFrame(0) 110 | const width = scenarioDataWrapper.getWidthOfFrame(0) 111 | const numOfPoints = width*height 112 | 113 | const pointsToRender = new Float32Array(numOfPoints*3); 114 | const pointsColors = new Float32Array(numOfPoints*3); 115 | const pointsToRenderMorph = new Float32Array(numOfPoints*3); 116 | const pointsColorsMorph = new Float32Array(numOfPoints*3); 117 | 118 | const pointsToRenderSteps : Array = [] 119 | const pointsColorsSteps : Array = [] 120 | let pointPointer = 0 121 | 122 | for(let i = 0; i < steps; i++) { 123 | pointsToRenderSteps[i] = new Float32Array(numOfPoints*3) 124 | pointsColorsSteps[i] = new Float32Array(numOfPoints*3) 125 | } 126 | 127 | for(let i = 0; i < height; i++) { 128 | for(let j = 0; j < width; j++) { 129 | 130 | const u = i/width 131 | const v = j/width 132 | const rOriginal = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 0, true, 0) 133 | const gOriginal = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 1, true, 1) 134 | const bOriginal = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 2, true, 2) 135 | 136 | pointsToRender[pointPointer*3 + 1] = -u + (height/width)/2 137 | pointsToRender[pointPointer*3] = v - 0.5 138 | pointsToRender[pointPointer*3 + 2] = rgbToInt([rOriginal, gOriginal, bOriginal]) / colorSpaceNormalizationSize 139 | 140 | pointsColors[pointPointer*3] = rOriginal / 255 141 | pointsColors[pointPointer*3 + 1] = gOriginal / 255 142 | pointsColors[pointPointer*3 + 2] = bOriginal / 255 143 | 144 | 145 | for(let k = 0; k < steps; k++) { 146 | const rMorph = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4 + 0, true, 0) 147 | const gMorph = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4 + 1, true, 1) 148 | const bMorph = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4 + 2, true, 2) 149 | 150 | pointsToRenderSteps[k][pointPointer*3 + 1] = -u + (height/width)/2 151 | pointsToRenderSteps[k][pointPointer*3] = v - 0.5 152 | pointsToRenderSteps[k][pointPointer*3 + 2] = rgbToInt([rMorph, gMorph, bMorph]) / colorSpaceNormalizationSize 153 | 154 | pointsColorsSteps[k][pointPointer*3] = rMorph / 255 155 | pointsColorsSteps[k][pointPointer*3 + 1] = gMorph / 255 156 | pointsColorsSteps[k][pointPointer*3 + 2] = bMorph / 255 157 | } 158 | 159 | const rTarget = scenarioDataWrapper.getValueAt(1, i, j, pointPointer*4 + 0, true, 0) 160 | const gTarget = scenarioDataWrapper.getValueAt(1, i, j, pointPointer*4 + 1, true, 1) 161 | const bTarget = scenarioDataWrapper.getValueAt(1, i, j, pointPointer*4 + 2, true, 2) 162 | 163 | pointsToRenderMorph[pointPointer*3 + 1] = -u + (height/width)/2 164 | pointsToRenderMorph[pointPointer*3] = v - 0.5 165 | pointsToRenderMorph[pointPointer*3 + 2] = rgbToInt([rTarget, gTarget, bTarget]) / colorSpaceNormalizationSize 166 | 167 | pointsColorsMorph[pointPointer*3] = rTarget / 255 168 | pointsColorsMorph[pointPointer*3 + 1] = gTarget / 255 169 | pointsColorsMorph[pointPointer*3 + 2] = bTarget / 255 170 | 171 | pointPointer++ 172 | } 173 | } 174 | 175 | geometry.setAttribute('position', new THREE.BufferAttribute(pointsToRender, POS_NUM)) 176 | geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors, COL_NUM)) 177 | geometry.morphAttributes.position = [] 178 | geometry.morphAttributes.color = [] 179 | 180 | for(let i = 0; i < steps; i++) { 181 | geometry.morphAttributes.position[i] = new THREE.BufferAttribute( pointsToRenderSteps[i], POS_NUM) 182 | geometry.morphAttributes.color[i] = new THREE.BufferAttribute( pointsColorsSteps[i], COL_NUM) 183 | } 184 | 185 | // Add final destination image 186 | geometry.morphAttributes.position[steps] = new THREE.BufferAttribute(pointsToRenderMorph, POS_NUM) 187 | geometry.morphAttributes.color[steps] = new THREE.BufferAttribute(pointsColorsMorph, COL_NUM) 188 | 189 | return geometry 190 | } 191 | 192 | export function getAIImagesBufferGeometry(scenarioDataWrapper: ScenarioDataWrapper, imgCenterPoints: {[sceneType: string] : Array}, targetGeometry: string) : THREE.BufferGeometry { 193 | 194 | if (imgCenterPoints[targetGeometry] == undefined) { 195 | imgCenterPoints[targetGeometry] = [] 196 | } 197 | 198 | const geometry = new THREE.BufferGeometry() 199 | 200 | const pointsToRender : Array = [] 201 | const pointsColors : Array = [] 202 | let pointPointer = 0 203 | 204 | let maxHeight = 0 205 | let maxWidth = 0 206 | const gallerySize = scenarioDataWrapper.getFramesCount()/2 207 | for(let i = 0; i < gallerySize; i++) { 208 | maxHeight = Math.max(maxHeight, scenarioDataWrapper.getHeightOfFrame(2*i)) 209 | maxWidth = Math.max(maxHeight, scenarioDataWrapper.getWidthOfFrame(2*i)) 210 | } 211 | 212 | for(let k = 0; k < gallerySize; k++) { 213 | 214 | const height = scenarioDataWrapper.getHeightOfFrame(2*k) 215 | const width = scenarioDataWrapper.getWidthOfFrame(2*k) 216 | const numOfPoints = maxWidth*maxHeight 217 | 218 | imgCenterPoints[targetGeometry][k] = new THREE.Vector3(0, 0, scenarioDataWrapper.getCenterValueAt(2*k + 1) / aiImagesNormalizationSize) 219 | 220 | const heightDifference = maxHeight - height 221 | const widthDifference = maxWidth - width 222 | const heightOffset = Math.floor(heightDifference/2) 223 | const widthOffset = Math.floor(widthDifference/2) 224 | 225 | pointsToRender[k] = new Float32Array(numOfPoints*3) 226 | pointsColors[k] = new Float32Array(numOfPoints*3) 227 | 228 | for(let i = 0; i < maxHeight; i++) { 229 | for(let j = 0; j < maxWidth; j++) { 230 | 231 | const u = i/maxWidth 232 | const v = j/maxWidth 233 | 234 | if (j < widthOffset || j >= maxWidth - widthOffset - 1 || i < heightOffset || i >= maxHeight - heightOffset - 1) { 235 | pointsToRender[k][pointPointer*3 + 1] = -u + (height/width)/2 236 | pointsToRender[k][pointPointer*3] = v - 0.5 237 | pointsToRender[k][pointPointer*3 + 2] = 0 238 | 239 | pointsColors[k][pointPointer*3] = 0 240 | pointsColors[k][pointPointer*3 + 1] = 0 241 | pointsColors[k][pointPointer*3 + 2] = 0 242 | } else { 243 | const iShiftedIndex = i - heightOffset 244 | const jShiftedIndex = j - widthOffset 245 | 246 | pointsToRender[k][pointPointer*3 + 1] = -u + (height/width)/2 247 | pointsToRender[k][pointPointer*3] = v - 0.5 248 | pointsToRender[k][pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(2*k + 1, iShiftedIndex, jShiftedIndex, 4*(iShiftedIndex*width + jShiftedIndex)) / 40 249 | 250 | pointsColors[k][pointPointer*3] = scenarioDataWrapper.getValueAt(2*k, iShiftedIndex, jShiftedIndex, 4*(iShiftedIndex*width + jShiftedIndex), true, 0) / 255 251 | pointsColors[k][pointPointer*3 + 1] = scenarioDataWrapper.getValueAt(2*k, iShiftedIndex, jShiftedIndex, 4*(iShiftedIndex*width + jShiftedIndex) + 1, true, 1) / 255 252 | pointsColors[k][pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(2*k, iShiftedIndex, jShiftedIndex, 4*(iShiftedIndex*width + jShiftedIndex) + 2, true, 2) / 255 253 | } 254 | pointPointer++ 255 | } 256 | } 257 | 258 | pointPointer = 0 259 | } 260 | 261 | geometry.setAttribute('position', new THREE.BufferAttribute(pointsToRender[0], POS_NUM)) 262 | geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors[0], COL_NUM)) 263 | 264 | if (pointsToRender.length > 1) { 265 | geometry.morphAttributes.position = [] 266 | geometry.morphAttributes.color = [] 267 | 268 | for(let i = 1; i < gallerySize; i++) { 269 | geometry.morphAttributes.position[i - 1] = new THREE.BufferAttribute( pointsToRender[i], POS_NUM) 270 | geometry.morphAttributes.color[i - 1] = new THREE.BufferAttribute( pointsColors[i], COL_NUM) 271 | } 272 | } 273 | 274 | return geometry 275 | } 276 | 277 | 278 | export function getActBufferGeometry(scenarioDataWrapper: ScenarioDataWrapper, steps: number) : THREE.BufferGeometry { 279 | 280 | const arrayMorphOffset = 4 281 | const geometry = new THREE.BufferGeometry() 282 | 283 | const height = scenarioDataWrapper.getHeightOfFrame(0) 284 | const width = scenarioDataWrapper.getWidthOfFrame(0) 285 | const numOfPoints = width*height 286 | 287 | const pointsToRender = new Float32Array(numOfPoints*3); 288 | const pointsColors = new Float32Array(numOfPoints*3); 289 | const pointsToRenderMorph = new Float32Array(numOfPoints*3); 290 | const pointsColorsMorph = new Float32Array(numOfPoints*3); 291 | 292 | const pointsToRenderSteps : Array = [] 293 | const pointsColorsSteps : Array = [] 294 | let pointPointer = 0 295 | 296 | for(let i = 0; i < steps; i++) { 297 | pointsToRenderSteps[i] = new Float32Array(numOfPoints*3) 298 | pointsColorsSteps[i] = new Float32Array(numOfPoints*3) 299 | } 300 | 301 | for(let i = 0; i < height; i++) { 302 | for(let j = 0; j < width; j++) { 303 | 304 | const u = i/width 305 | const v = j/width 306 | 307 | pointsToRender[pointPointer*3 + 1] = -u + (height/width)/2 308 | pointsToRender[pointPointer*3] = v - 0.5 309 | removeNoise(scenarioDataWrapper.getValueAt(1, i, j, pointPointer*4, false, 0, true), scenarioDataWrapper.getFrameRawDataAt(1), i, j, width, height, pointPointer, pointsToRender) 310 | 311 | pointsColors[pointPointer*3] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4, true, 0) / 255 312 | pointsColors[pointPointer*3 + 1] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 1, true, 1) / 255 313 | pointsColors[pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(0, i, j, pointPointer*4 + 2, true, 2) / 255 314 | 315 | 316 | for(let k = 0; k < steps; k++) { 317 | 318 | pointsToRenderSteps[k][pointPointer*3 + 1] = -u + (height/width)/2 319 | pointsToRenderSteps[k][pointPointer*3] = v - 0.5 320 | removeNoise(scenarioDataWrapper.getValueAt(arrayMorphOffset + steps + k, i, j, pointPointer*4, false, 0, true), scenarioDataWrapper.getFrameRawDataAt(arrayMorphOffset + steps + k), i, j, width, height, pointPointer, pointsToRenderSteps[k]) 321 | 322 | pointsColorsSteps[k][pointPointer*3] = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4, true, 0) / 255 323 | pointsColorsSteps[k][pointPointer*3 + 1] = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4 + 1, true, 1) / 255 324 | pointsColorsSteps[k][pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(k + arrayMorphOffset, i, j, pointPointer*4 + 2, true, 2) / 255 325 | } 326 | 327 | 328 | 329 | pointsToRenderMorph[pointPointer*3 + 1] = -u + (height/width)/2 330 | pointsToRenderMorph[pointPointer*3] = v - 0.5 331 | removeNoise(scenarioDataWrapper.getValueAt(3, i, j, pointPointer*4, false, 0, true), scenarioDataWrapper.getFrameRawDataAt(3), i, j, width, height, pointPointer, pointsToRenderMorph) 332 | 333 | pointsColorsMorph[pointPointer*3] = scenarioDataWrapper.getValueAt(2, i, j, pointPointer*4, true, 0) / 255 334 | pointsColorsMorph[pointPointer*3 + 1] = scenarioDataWrapper.getValueAt(2, i, j, pointPointer*4 + 1, true, 1) / 255 335 | pointsColorsMorph[pointPointer*3 + 2] = scenarioDataWrapper.getValueAt(2, i, j, pointPointer*4 + 2, true, 2) / 255 336 | 337 | pointPointer++ 338 | } 339 | } 340 | 341 | geometry.setAttribute('position', new THREE.BufferAttribute(pointsToRender, POS_NUM)) 342 | geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors, COL_NUM)) 343 | geometry.morphAttributes.position = [] 344 | geometry.morphAttributes.color = [] 345 | 346 | for(let i = 0; i < steps; i++) { 347 | geometry.morphAttributes.position[i] = new THREE.BufferAttribute( pointsToRenderSteps[i], POS_NUM) 348 | geometry.morphAttributes.color[i] = new THREE.BufferAttribute( pointsColorsSteps[i], COL_NUM) 349 | } 350 | 351 | // Add final destination image 352 | geometry.morphAttributes.position[steps] = new THREE.BufferAttribute(pointsToRenderMorph, POS_NUM) 353 | geometry.morphAttributes.color[steps] = new THREE.BufferAttribute(pointsColorsMorph, COL_NUM) 354 | 355 | return geometry 356 | } 357 | 358 | // util functions 359 | 360 | function removeNoise(depthMapColor: number, depthmap: Array> | ImageData, i: number, j: number, width: number, height: number, pointPointer: number, pointsToRender: Float32Array) { 361 | if (depthMapColor != 0) { 362 | 363 | const percentage = depthMapColor/100 364 | let left = depthMapColor 365 | let right = depthMapColor 366 | let top = depthMapColor 367 | let down = depthMapColor 368 | const dropThreshold = 5*percentage 369 | 370 | if (!Array.isArray(depthmap)) { 371 | if (j > 0) left = (depthmap as ImageData).data[pointPointer*4-4] 372 | if (j < width - 1) right = (depthmap as ImageData).data[pointPointer*4+4] 373 | if (pointPointer - width > 0) top = (depthmap as ImageData).data[4*(pointPointer - width)] 374 | if (pointPointer + width < height*width-1) down = (depthmap as ImageData).data[4*(pointPointer + width)] 375 | } else { 376 | if (j > 0) left = depthmap[i][j-1] 377 | if (j < width-1) right = depthmap[i][j+1] 378 | if (i > 0) top = depthmap[i-1][j] 379 | if (i < height-1) down = depthmap[i+1][j] 380 | } 381 | 382 | if(Math.abs(left - depthMapColor) > dropThreshold || Math.abs(right - depthMapColor) > dropThreshold) { 383 | pointsToRender[pointPointer*3 + 2] = 0 384 | } 385 | else if(Math.abs(top - depthMapColor) > dropThreshold || Math.abs(down - depthMapColor) > dropThreshold) { 386 | pointsToRender[pointPointer*3 + 2] = 0 387 | } else { 388 | // extra scale 389 | if (!Array.isArray(depthmap)) { 390 | pointsToRender[pointPointer*3 + 2] = 10 + -1*depthMapColor / 20 391 | } else { 392 | pointsToRender[pointPointer*3 + 2] = 3*(1 - depthMapColor) 393 | } 394 | } 395 | } 396 | } 397 | 398 | function rgbToInt(color: Array): number { 399 | let rbgInt = 0 400 | for(let c of color) { 401 | rbgInt = (rbgInt<<8) + c 402 | } 403 | 404 | return rbgInt 405 | } 406 | 407 | // Wrapper of frame data so we can merge geometry generation methods 408 | export class ScenarioDataWrapper { 409 | 410 | parsedData: any[] = [] 411 | 412 | constructor(public imageData: ImageData[] = [], public jsonData: any[] = [] ) { 413 | if (jsonData.length > 0) { 414 | for (let i = 0; i < jsonData.length; i++) { 415 | this.parsedData.push(JSON.parse(jsonData[i])) 416 | } 417 | } 418 | } 419 | 420 | getHeightOfFrame(frame: number) : number { 421 | if (this.imageData.length != 0) { 422 | return this.imageData[frame].height 423 | } else { 424 | return this.parsedData[frame].length 425 | } 426 | } 427 | 428 | getWidthOfFrame(frame: number) : number { 429 | if (this.imageData.length != 0) { 430 | return this.imageData[frame].width 431 | } else { 432 | return this.parsedData[frame][0].length 433 | } 434 | } 435 | 436 | getCenterValueAt(frame: number) : number { 437 | if (this.imageData.length != 0) { 438 | return this.imageData[frame].data[this.imageData[frame].width*this.imageData[frame].height*2] 439 | } else { 440 | return this.parsedData[frame][Math.floor(this.parsedData[frame].length/2)][Math.floor(this.parsedData[frame][0].length/2)] 441 | } 442 | } 443 | 444 | getValueAt(frame: number, height: number, width: number, index: number, isRgb = false, offset = 0, ignoreScale = false) : number { 445 | if (this.imageData.length != 0) { 446 | return this.imageData[frame].data[index] 447 | } else { 448 | if (isRgb) { 449 | return this.parsedData[frame][height][width][offset] 450 | } else { 451 | let scale = 330 452 | if (ignoreScale) { 453 | scale = 1 454 | } 455 | return this.parsedData[frame][height][width] / scale 456 | } 457 | } 458 | } 459 | 460 | getFramesCount() : number { 461 | if (this.imageData.length != 0) { 462 | return this.imageData.length 463 | } else { 464 | return this.parsedData.length 465 | } 466 | } 467 | 468 | getFrameRawDataAt(frame: number) : ImageData | number[][] { 469 | if (this.imageData.length != 0) { 470 | return this.imageData[frame] 471 | } else { 472 | return this.parsedData[frame] 473 | } 474 | } 475 | } -------------------------------------------------------------------------------- /src/client/guiWrapper.ts: -------------------------------------------------------------------------------- 1 | import { GUI, GUIController } from "dat.gui"; 2 | 3 | export class GuiWrapper { 4 | 5 | gui: GUI 6 | sceneSettingsFolder: GUI | undefined 7 | renderingType: GUIController | undefined 8 | 9 | optionsFolder: GUI | undefined 10 | frameOptionGui: GUIController | undefined 11 | 12 | textureRenderFolder: GUI | undefined 13 | 14 | parallaxFolder: GUI | undefined 15 | parallaxGui: GUIController | undefined 16 | 17 | constructor(public interpolationOptions: any, public textureOptions: any, public renderingOptions: any, public parallaxOptions: any, public callbackOptions: any) { 18 | this.gui = new GUI() 19 | this.initDefaultGui() 20 | } 21 | 22 | initDefaultGui() { 23 | //General scene settings 24 | this.sceneSettingsFolder = this.gui.addFolder('Scene settings') 25 | this.renderingType = this.sceneSettingsFolder.add(this.renderingOptions, 'type', ['Waifus', 'Farm', 'Koala', 'ai_images']).name("Rendering type").onChange(value => { 26 | this.callbackOptions.renderSelectedMesh(value) 27 | }) 28 | this.sceneSettingsFolder.add(this.interpolationOptions, 'scale', -2, 2, 0.001).onChange(value => { 29 | this.callbackOptions.scaleScene(value) 30 | }) 31 | this.sceneSettingsFolder.open() 32 | 33 | //Add texture options 34 | this.createTextureOptions() 35 | 36 | //Add parallax options 37 | this.createParallaxOptions() 38 | } 39 | 40 | triggerInitialScene() { 41 | this.renderingType?.setValue('Farm') 42 | } 43 | 44 | createTextureOptions() { 45 | if (this.textureRenderFolder != undefined) { 46 | return 47 | } 48 | this.textureRenderFolder = this.gui.addFolder('Texture settings') 49 | this.textureRenderFolder.add(this.textureOptions, 'textureUsage').name("Texture as color source").onChange(value => { 50 | this.callbackOptions.renderSelectedMesh('Farm') 51 | }); 52 | this.textureRenderFolder.open() 53 | } 54 | 55 | createParallaxOptions() { 56 | if (this.parallaxFolder != undefined) { 57 | return 58 | } 59 | this.parallaxFolder = this.gui.addFolder('Parallax settings') 60 | this.parallaxGui = this.parallaxFolder.add(this.parallaxOptions, 'type', ['None', 'Auto', 'Dynamic']).name("Parallax type").onChange(value => { 61 | this.callbackOptions.handleParallaxSelection(value) 62 | }) 63 | this.parallaxGui.setValue('Auto') 64 | this.parallaxFolder.open() 65 | } 66 | 67 | createDetailedOptions(liteVersion = false) { 68 | this.optionsFolder = this.gui.addFolder('Options') 69 | // caution on max value - steps in original 70 | this.frameOptionGui = this.optionsFolder.add( this.interpolationOptions, 'frame', 0, 11, 1).onChange(value => { 71 | this.callbackOptions.moveToFrame(value) 72 | }) 73 | 74 | if (!liteVersion) { 75 | this.optionsFolder.add(this.interpolationOptions, 'auto') 76 | this.optionsFolder.add(this.interpolationOptions, "resetCurrent").name("Reset current frame") 77 | this.optionsFolder.add(this.interpolationOptions, "reset").name("Reset") 78 | this.optionsFolder.add(this.interpolationOptions, "invert").name("Invert") 79 | } 80 | this.optionsFolder.open() 81 | } 82 | 83 | addOptions() { 84 | if (this.optionsFolder != undefined) { 85 | this.gui.removeFolder(this.optionsFolder) 86 | this.optionsFolder = undefined 87 | } 88 | switch(this.renderingType?.getValue()) { 89 | case 'Waifus': { 90 | if (this.optionsFolder == undefined) { 91 | this.createDetailedOptions() 92 | this.removeTextureOptions() 93 | } 94 | this.sceneSettingsFolder?.updateDisplay() 95 | this.parallaxGui?.setValue('None') 96 | this.removeParallaxOptions() 97 | break 98 | } 99 | case 'Farm': { 100 | if (this.optionsFolder != undefined) { 101 | this.gui.removeFolder(this.optionsFolder) 102 | this.optionsFolder = undefined 103 | } 104 | this.createDetailedOptions(true) 105 | this.createTextureOptions() 106 | this.createParallaxOptions() 107 | break 108 | } 109 | case 'Koala': { 110 | if (this.optionsFolder == undefined) { 111 | this.createDetailedOptions() 112 | this.removeTextureOptions() 113 | } 114 | this.parallaxGui?.setValue('None') 115 | this.removeParallaxOptions() 116 | break 117 | } 118 | case 'ai_images': { 119 | if (this.optionsFolder == undefined) { 120 | this.removeTextureOptions() 121 | this.createDetailedOptions(true) 122 | } 123 | this.createParallaxOptions() 124 | break 125 | } 126 | } 127 | 128 | this.updateGui(0) 129 | } 130 | 131 | updateGui(frame: number) { 132 | this.interpolationOptions.frame = frame 133 | this.optionsFolder?.updateDisplay() 134 | } 135 | 136 | updateFrameCounter(max: number) { 137 | this.frameOptionGui?.max(max) 138 | } 139 | 140 | removeTextureOptions() { 141 | if (this.textureRenderFolder != undefined) { 142 | this.gui.removeFolder(this.textureRenderFolder) 143 | this.textureRenderFolder = undefined 144 | } 145 | } 146 | 147 | removeParallaxOptions() { 148 | if (this.parallaxFolder != undefined) { 149 | this.parallaxGui?.remove() 150 | this.gui.removeFolder(this.parallaxFolder) 151 | this.parallaxFolder = undefined 152 | } 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /src/client/shader.ts: -------------------------------------------------------------------------------- 1 | export const VERTEX_SHADER = ` 2 | #ifdef GL_ES 3 | precision highp float; 4 | #endif 5 | 6 | uniform float size; 7 | uniform float scale; 8 | #define USE_COLOR 9 | #define USE_MORPHCOLORS 10 | #include 11 | #include 12 | #include 13 | 14 | attribute vec3 color; 15 | varying vec2 vUv; 16 | uniform float u_time; 17 | uniform float u_minZ; 18 | uniform float u_maxZ; 19 | uniform float u_scale; 20 | uniform vec3 u_camera_angle; 21 | uniform int u_parallax_type; 22 | 23 | void main() 24 | { 25 | #include 26 | #include 27 | #include 28 | vColor = color; 29 | vUv = uv; 30 | gl_PointSize = 2.0; 31 | 32 | #if defined( MORPHTARGETS_COUNT ) 33 | vColor *= morphTargetBaseInfluence; 34 | for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { 35 | if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; 36 | } 37 | #endif 38 | 39 | if (u_parallax_type == 1) { 40 | transformed.x += u_scale*u_camera_angle.x*transformed.z*0.05; 41 | transformed.y += u_scale*u_camera_angle.y*transformed.z*0.02; 42 | } else if (u_parallax_type == 2) { 43 | transformed.x += transformed.z*cos(0.5*u_time)*0.01; 44 | transformed.y += transformed.z*sin(0.5*u_time)*0.005; 45 | } 46 | 47 | vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); 48 | gl_Position = projectionMatrix * mvPosition; 49 | } 50 | `; 51 | 52 | export const FRAGMENT_SHADER = ` 53 | #ifdef GL_ES 54 | precision highp float; 55 | #endif 56 | 57 | varying vec3 vColor; 58 | varying vec2 vUv; 59 | uniform sampler2D u_texture; 60 | uniform bool u_use_texture; 61 | 62 | void main() 63 | { 64 | if (u_use_texture) { 65 | gl_FragColor = texture2D(u_texture, vUv); 66 | } else { 67 | gl_FragColor = vec4( vColor, 1.0 ); 68 | } 69 | } 70 | 71 | `; 72 | -------------------------------------------------------------------------------- /src/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "target": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "typeRoots" : ["./node_modules/@webgpu/types/dist", "node_modules/@types", "src/types"], 8 | "lib": [ 9 | "dom", 10 | "dom.iterable", 11 | "esnext" 12 | ], 13 | "types": [ 14 | // ... other types 15 | "node" 16 | ], 17 | }, 18 | "include": [ 19 | "**/*.ts", 20 | "**/*.tsx", 21 | "**/*.d.ts" 22 | ], 23 | } 24 | -------------------------------------------------------------------------------- /src/client/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.jpg'; 2 | declare module '*.jpeg'; 3 | declare module '*.png'; 4 | declare module '*.json'; 5 | declare module '*.gz'; -------------------------------------------------------------------------------- /src/client/webgl2/WebGL2Renderer.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "./glutils" 2 | import { Logger } from "./logger" 3 | import { Scene } from "./scene" 4 | 5 | export class WebGL2Renderer { 6 | scene!: Scene 7 | 8 | init() { 9 | let canvas: HTMLCanvasElement = document.querySelector("#webgl2canvas")! 10 | let context = canvas.getContext("webgl2") 11 | 12 | if (context) { 13 | Logger.log("WebGL2 context ok") 14 | GLUtils.init(context, canvas) 15 | this.scene = new Scene() 16 | this.scene.setupPrograms() 17 | this.scene.draw() 18 | } else { 19 | Logger.log("WebGL2 context not available") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/client/webgl2/cameraControls.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix" 2 | import { GLUtils } from "./glutils" 3 | import { WebGLGUI } from "./webglgui" 4 | 5 | export class CameraControls { 6 | 7 | cameraFeedback: WebGLGUI 8 | inDrag = false 9 | shiftActive = false 10 | initDown = { 11 | x: 0, y: 0 12 | } 13 | 14 | projMatrix: mat4 15 | orthoMatrix: mat4 16 | viewMatrix: mat4 17 | particlesViewMatrix!: mat4 18 | identMatrix = mat4.create() 19 | 20 | constructor(cameraFeedback: WebGLGUI) { 21 | this.cameraFeedback = cameraFeedback 22 | 23 | GLUtils.canvas.addEventListener("mousedown", (e) => this.mouseDown(e)) 24 | GLUtils.canvas.addEventListener("mouseup", (e) => this.mouseUp(e)) 25 | GLUtils.canvas.addEventListener("mouseout", (e) => this.mouseUp(e)) 26 | GLUtils.canvas.addEventListener("mousemove", (e) => this.mouseMove(e)) 27 | GLUtils.canvas.addEventListener("wheel", (e) => this.wheel(e)) 28 | 29 | this.projMatrix = mat4.create() 30 | mat4.perspectiveNO(this.projMatrix, GLUtils.degToRad(70), GLUtils.getAspectRatio(), 0.001, 10) 31 | this.orthoMatrix = mat4.create() 32 | mat4.orthoNO(this.orthoMatrix, -1, 1, -1, 1, 0.1, 2) 33 | this.viewMatrix = mat4.create() 34 | this.calculateViewMatrix() 35 | } 36 | 37 | calculateViewMatrix() { 38 | let clean = mat4.create() 39 | let trans = vec3.create() 40 | trans[0] = this.cameraFeedback.guiOptions.moveX 41 | trans[1] = this.cameraFeedback.guiOptions.moveY 42 | trans[2] = this.cameraFeedback.guiOptions.moveZ 43 | mat4.rotateX(clean, clean, GLUtils.degToRad(this.cameraFeedback.guiOptions.rotateX)) 44 | mat4.rotateY(clean, clean, GLUtils.degToRad(this.cameraFeedback.guiOptions.rotateY)) 45 | mat4.rotateZ(clean, clean, GLUtils.degToRad(this.cameraFeedback.guiOptions.rotateZ)) 46 | mat4.translate(clean, clean, trans) 47 | 48 | mat4.invert(clean, clean) 49 | this.viewMatrix = clean 50 | 51 | if (!this.particlesViewMatrix) { 52 | this.particlesViewMatrix = mat4.clone(this.viewMatrix) 53 | } 54 | } 55 | 56 | mouseDown(mouseEvent: any) { 57 | this.inDrag = true 58 | this.initDown = { 59 | x: mouseEvent.clientX, 60 | y: mouseEvent.clientY 61 | } 62 | 63 | this.shiftActive = mouseEvent.shiftKey 64 | mouseEvent.preventDefault() 65 | return false 66 | } 67 | 68 | private mouseUp(mouseEvent: any) { 69 | this.inDrag = false 70 | this.shiftActive = false 71 | } 72 | 73 | mouseMove(mouseEvent: any) { 74 | if (!this.inDrag) { 75 | return false 76 | } 77 | 78 | let deltaX = (mouseEvent.clientX - this.initDown.x) / 5 79 | let deltaY = (mouseEvent.clientY - this.initDown.y) / 5 80 | this.initDown = { 81 | x: mouseEvent.clientX, 82 | y: mouseEvent.clientY 83 | } 84 | 85 | if (this.shiftActive) { 86 | this.cameraFeedback.shiftedBy(-deltaX * 0.01, deltaY * 0.01) 87 | } else { 88 | this.cameraFeedback.rotatedBy(deltaX, deltaY) 89 | } 90 | 91 | mouseEvent.preventDefault() 92 | } 93 | 94 | wheel(mouseEvent: any) { 95 | this.cameraFeedback.zoomedBy(Math.sign(mouseEvent.deltaY) * 0.1) 96 | mouseEvent.preventDefault() 97 | } 98 | 99 | } 100 | 101 | export interface CameraFeedback { 102 | rotatedBy(xRotation: number, yRotation: number) : void 103 | zoomedBy(zoom: number) : void 104 | shiftedBy(xShift: number, yShift: number) : void 105 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/drawObject.ts: -------------------------------------------------------------------------------- 1 | import { CameraControls } from "../cameraControls"; 2 | import { SimplexDrawObject } from "./simplexDrawObject"; 3 | import { WebGLGUI } from "../webglgui"; 4 | import { VectorFieldDrawObject } from "./vectorFieldDrawObject"; 5 | import { FlowDrawObject } from "./flowDrawObject"; 6 | import { FlightHelper } from "../flightHelper"; 7 | import { FlightDrawObject } from "./flightDrawObject"; 8 | import { InterpolationHelper } from "../interpolationHelper"; 9 | import { Program } from "../program/program"; 10 | import { FlightVectorFieldDrawObject } from "./flightVectorFieldDrawObject"; 11 | 12 | // wrapper for anything that should be updated per frame 13 | export class DrawObjectsWrapper { 14 | 15 | simplexDrawObject: SimplexDrawObject 16 | vectorFieldDrawObject: VectorFieldDrawObject 17 | flowDrawObject: FlowDrawObject 18 | flightDrawObject: FlightDrawObject 19 | flightVectorFieldDrawObject: FlightVectorFieldDrawObject 20 | 21 | constructor(public cameraControls: CameraControls, public webglgui: WebGLGUI, public drawParams: DrawParams, public interpolationHelper: InterpolationHelper) { 22 | this.simplexDrawObject = new SimplexDrawObject() 23 | this.vectorFieldDrawObject = new VectorFieldDrawObject() 24 | this.flowDrawObject = new FlowDrawObject(drawParams.particlesTextureSize) 25 | this.flightDrawObject = new FlightDrawObject(interpolationHelper) 26 | this.flightVectorFieldDrawObject = new FlightVectorFieldDrawObject(interpolationHelper) 27 | } 28 | 29 | 30 | draw() { 31 | this.drawParams.hideNoise = this.webglgui.guiOptions.hideNoise 32 | this.drawParams.simplexViewState = this.webglgui.simplexViewState 33 | for (let i = 1; i <= this.webglgui.guiOptions.layers; i++) { 34 | this.drawParams.layer = i 35 | this.simplexDrawObject.draw(this.drawParams, this.cameraControls) 36 | if (!this.webglgui.guiOptions.hideVectors) { 37 | this.vectorFieldDrawObject.draw(this.drawParams, this.cameraControls) 38 | } 39 | } 40 | 41 | if (this.webglgui.guiOptions.hideFlow == false) { 42 | this.flowDrawObject.draw(this.drawParams, this.cameraControls) 43 | } 44 | 45 | for (let i = 0; i < FlightHelper.flightPaths.length; i++) { 46 | this.drawParams.flightIndex = i 47 | this.flightDrawObject.draw(this.drawParams, this.cameraControls) 48 | if (!this.webglgui.guiOptions.hideVectors) { 49 | this.flightVectorFieldDrawObject.draw(this.drawParams, this.cameraControls) 50 | } 51 | } 52 | } 53 | } 54 | 55 | export interface DrawObject { 56 | setupPrograms() : void 57 | draw(drawParams: DrawParams, cameraControls: CameraControls) : void 58 | } 59 | 60 | // Simple shared data object in renderer 61 | export class DrawParams { 62 | 63 | time = 0 64 | layer = 1 65 | flightIndex = 0 66 | //refactor to enum? 67 | simplexViewState = 0.0 68 | simplexTextureSize = { 69 | x: 100, 70 | y: 100 71 | } 72 | particlesTextureSize = { 73 | x: 0, 74 | y: 0 75 | } 76 | defaultSimplexTexture!: WebGLTexture 77 | currentSimplexTexture!: WebGLTexture 78 | hideNoise = true 79 | //reused program in pipeline 80 | fieldVectorProgram: Program | undefined 81 | 82 | constructor(public fieldVisualizationDensity: number) { 83 | 84 | } 85 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/flightDrawObject.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix"; 2 | import { CameraControls } from "../cameraControls"; 3 | import { FLIGHT_VERTEX_SHADER, FLIGHT_FRAGMENT_SHADER } from "../flowShaders"; 4 | import { GLUtils } from "../glutils"; 5 | import { InterpolationHelper } from "../interpolationHelper"; 6 | import { FlightProgram } from "../program/flightProgram"; 7 | import { UNIFORM_TYPE } from "../program/program"; 8 | import { DrawObject, DrawParams } from "./drawObject"; 9 | 10 | export class FlightDrawObject implements DrawObject { 11 | 12 | flightProgram!: FlightProgram 13 | 14 | constructor(public interpolationHelper: InterpolationHelper) { 15 | this.setupPrograms() 16 | } 17 | 18 | setupPrograms(): void { 19 | this.flightProgram = new FlightProgram(GLUtils.gl, FLIGHT_VERTEX_SHADER, FLIGHT_FRAGMENT_SHADER, null, GLUtils.gl.TRIANGLE_STRIP) 20 | this.flightProgram.generateData(this.interpolationHelper.sampleSize * 2, null) 21 | } 22 | draw(drawParams: DrawParams, cameraControls: CameraControls): void { 23 | this.flightProgram.useProgram() 24 | this.flightProgram.setFlightIndexAndRegenerate(drawParams.flightIndex)//, this.interpolationHelper.sampleSize*2) 25 | 26 | let flightRedColor = 0 27 | if (drawParams.flightIndex == this.interpolationHelper.maxIndex) { 28 | flightRedColor = 1.0 29 | } 30 | this.flightProgram.setUniform("u_color_r", flightRedColor, UNIFORM_TYPE.f) 31 | let modelMatrix = mat4.create() 32 | let trans = vec3.fromValues(0, 0, 0.2) 33 | mat4.translate(modelMatrix, modelMatrix, trans) 34 | 35 | this.flightProgram.setUniform("u_matrix_model", modelMatrix, UNIFORM_TYPE.mat) 36 | this.flightProgram.setUniform("u_matrix_view", cameraControls.viewMatrix, UNIFORM_TYPE.mat) 37 | this.flightProgram.setUniform("u_matrix_projection", cameraControls.projMatrix, UNIFORM_TYPE.mat) 38 | this.flightProgram.draw() 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/flightVectorFieldDrawObject.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix"; 2 | import { CameraControls } from "../cameraControls"; 3 | import { SAMPLE_NOISE_VERTEX_SHADER, SAMPLE_NOISE_FRAGMENT_SHADER } from "../flowShaders"; 4 | import { GLUtils } from "../glutils"; 5 | import { InterpolationHelper } from "../interpolationHelper"; 6 | import { UNIFORM_TYPE } from "../program/program"; 7 | import { SampleSimplexProgram } from "../program/sampleSimplexProgram"; 8 | import { DrawObject, DrawParams } from "./drawObject"; 9 | 10 | export class FlightVectorFieldDrawObject implements DrawObject { 11 | 12 | sampleSimplexProgram!: SampleSimplexProgram 13 | 14 | constructor(public interpolationHelper: InterpolationHelper) { 15 | this.setupPrograms() 16 | } 17 | 18 | setupPrograms(): void { 19 | this.sampleSimplexProgram = new SampleSimplexProgram(GLUtils.gl, SAMPLE_NOISE_VERTEX_SHADER, SAMPLE_NOISE_FRAGMENT_SHADER, ["sampledValue"]) 20 | this.sampleSimplexProgram.generateData(this.interpolationHelper.sampleSize, null) 21 | } 22 | draw(drawParams: DrawParams, cameraControls: CameraControls): void { 23 | // sample 24 | this.sampleSimplexProgram.useProgram() 25 | this.sampleSimplexProgram.setFlightIndexAndRegenerate(drawParams.flightIndex) 26 | this.sampleSimplexProgram.setUniform("u_time", drawParams.time, UNIFORM_TYPE.f) 27 | GLUtils.gl.enable(GLUtils.gl.RASTERIZER_DISCARD) 28 | GLUtils.gl.beginTransformFeedback(GLUtils.gl.POINTS); 29 | GLUtils.gl.drawArrays(GLUtils.gl.POINTS, 0, this.interpolationHelper.sampleSize + 1); 30 | GLUtils.gl.endTransformFeedback(); 31 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, null); 32 | 33 | // // turn on using fragment shaders again 34 | GLUtils.gl.disable(GLUtils.gl.RASTERIZER_DISCARD); 35 | 36 | // two angles in 2 planes + size of vector 37 | const results = new Float32Array(3 * (this.interpolationHelper.sampleSize + 1)) 38 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, this.sampleSimplexProgram.buffers[0]); 39 | 40 | GLUtils.gl.getBufferSubData( 41 | GLUtils.gl.ARRAY_BUFFER, 42 | 0, 43 | results, 44 | ); 45 | 46 | this.interpolationHelper.createNoiseInterpolation( 47 | results.filter((element, index) => index % 3 == 0), 48 | results.filter((element, index) => index % 3 == 1), 49 | results.filter((element, index) => index % 3 == 2), 50 | drawParams.flightIndex 51 | ) 52 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, null) 53 | 54 | 55 | // draw vectors 56 | drawParams.fieldVectorProgram!.useProgram() 57 | 58 | drawParams.fieldVectorProgram!.setUniform("u_fromTexel", 0.0, UNIFORM_TYPE.f) 59 | 60 | for (let i = 0; i < this.interpolationHelper.pathOriginalPoints[drawParams.flightIndex][0].length; i++) { 61 | 62 | drawParams.fieldVectorProgram!.setUniform("u_arrowXYAngleValue", this.interpolationHelper.fieldOriginalXYPoints[i], UNIFORM_TYPE.f) 63 | drawParams.fieldVectorProgram!.setUniform("u_arrowXZAngleValue", this.interpolationHelper.fieldOriginalXZPoints[i], UNIFORM_TYPE.f) 64 | drawParams.fieldVectorProgram!.setUniform("u_arrowSizeValue", this.interpolationHelper.fieldOriginalAbsSizePoints[i], UNIFORM_TYPE.f) 65 | 66 | let modelMatrix = mat4.create() 67 | mat4.translate(modelMatrix, modelMatrix, vec3.fromValues(this.interpolationHelper.pathOriginalPoints[drawParams.flightIndex][0][i], this.interpolationHelper.pathOriginalPoints[drawParams.flightIndex][2][i], 0.2 + this.interpolationHelper.pathOriginalPoints[drawParams.flightIndex][1][i])) 68 | 69 | drawParams.fieldVectorProgram!.setUniform("u_matrix_model", modelMatrix, UNIFORM_TYPE.mat) 70 | 71 | drawParams.fieldVectorProgram!.draw() 72 | } 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/flowDrawObject.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix"; 2 | import { CameraControls } from "../cameraControls"; 3 | import { FLOW_STEP_VERTEX_SHADER, FLOW_STEP_FRAGMENT_SHADER, FLOW_DRAW_VERTEX_SHADER, FLOW_DRAW_FRAGMENT_SHADER, FLOW_HISTORY_VERTEX_SHADER, FLOW_HISTORY_FRAGMENT_SHADER } from "../flowShaders"; 4 | import { GLUtils, OffTextureData } from "../glutils"; 5 | import { FlowCalculationProgram } from "../program/flowCalculationProgram"; 6 | import { FlowHistoryProgram } from "../program/flowHistoryProgram"; 7 | import { UpdateFlowProgram } from "../program/flowUpdateProgram"; 8 | import { Program, UNIFORM_TYPE } from "../program/program"; 9 | import { DrawObject, DrawParams } from "./drawObject"; 10 | 11 | export class FlowDrawObject implements DrawObject { 12 | 13 | flowUpdateProgram!: Program 14 | flowCurrentProgram!: Program 15 | flowHistoryProgram!: Program 16 | 17 | particlesOffTextureData1!: OffTextureData 18 | particlesOffTextureData2!: OffTextureData 19 | particleCount: number = 20000 20 | particleSpeed: number = 0.001 21 | 22 | constructor(public particlesTextureSize: any) { 23 | this.setupPrograms() 24 | } 25 | 26 | setupPrograms(): void { 27 | // Flow vizualization - calculations 28 | this.flowUpdateProgram = new UpdateFlowProgram(GLUtils.gl, FLOW_STEP_VERTEX_SHADER, FLOW_STEP_FRAGMENT_SHADER, ["newPosition"]) 29 | this.flowUpdateProgram.generateData(this.particleCount, null) 30 | 31 | // Flow vizualization - output particles 32 | this.flowCurrentProgram = new FlowCalculationProgram(GLUtils.gl, FLOW_DRAW_VERTEX_SHADER, FLOW_DRAW_FRAGMENT_SHADER) 33 | this.flowCurrentProgram.generateData(0, this.flowUpdateProgram.buffers) 34 | 35 | // Flow vizualization - output history texture 36 | this.particlesOffTextureData1 = GLUtils.createOffscreenTexture(this.particlesTextureSize.x, this.particlesTextureSize.y) 37 | GLUtils.resetDrawingContext() 38 | 39 | this.particlesOffTextureData2 = GLUtils.createOffscreenTexture(this.particlesTextureSize.x, this.particlesTextureSize.y) 40 | GLUtils.resetDrawingContext() 41 | 42 | // FlowHistoryProgram 43 | this.flowHistoryProgram = new FlowHistoryProgram(GLUtils.gl, FLOW_HISTORY_VERTEX_SHADER, FLOW_HISTORY_FRAGMENT_SHADER) 44 | this.flowHistoryProgram.generateData(0, null) 45 | } 46 | 47 | draw(drawParams: DrawParams, cameraControls: CameraControls): void { 48 | //drawFlowParticles 49 | this.flowUpdateProgram.useProgram() 50 | GLUtils.bindTexture(drawParams.defaultSimplexTexture) 51 | 52 | this.flowUpdateProgram.setUniform("u_time_delta", this.particleSpeed, UNIFORM_TYPE.f) 53 | this.flowUpdateProgram.setUniform("u_noise_size", [drawParams.simplexTextureSize.x, drawParams.simplexTextureSize.y], UNIFORM_TYPE.iv) 54 | 55 | GLUtils.gl.enable(GLUtils.gl.RASTERIZER_DISCARD) 56 | GLUtils.gl.beginTransformFeedback(GLUtils.gl.POINTS); 57 | GLUtils.gl.drawArrays(GLUtils.gl.POINTS, 0, this.particleCount); 58 | GLUtils.gl.endTransformFeedback(); 59 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, null); 60 | GLUtils.gl.disable(GLUtils.gl.RASTERIZER_DISCARD); 61 | 62 | //drawFlowToTexture 63 | GLUtils.setCustomViewport(this.particlesTextureSize.x, this.particlesTextureSize.y) 64 | this.flowHistoryProgram.useProgram() 65 | 66 | this.flowHistoryProgram.setUniform("u_matrix_model", cameraControls.identMatrix, UNIFORM_TYPE.mat) 67 | this.flowHistoryProgram.setUniform("u_matrix_view", cameraControls.particlesViewMatrix, UNIFORM_TYPE.mat) 68 | this.flowHistoryProgram.setUniform("u_matrix_projection", cameraControls.orthoMatrix, UNIFORM_TYPE.mat) 69 | this.flowHistoryProgram.setUniform("u_dimming_ratio", 0.99, UNIFORM_TYPE.f) 70 | GLUtils.bindTexture(this.particlesOffTextureData2.targetTexture) 71 | 72 | GLUtils.gl.bindFramebuffer(GLUtils.gl.FRAMEBUFFER, this.particlesOffTextureData1.frameBuffer) 73 | GLUtils.gl.framebufferTexture2D(GLUtils.gl.FRAMEBUFFER, GLUtils.gl.COLOR_ATTACHMENT0, GLUtils.gl.TEXTURE_2D, this.particlesOffTextureData1.targetTexture, 0) 74 | this.flowHistoryProgram.draw() 75 | 76 | //draw new points with updated position with non-dimmed alpha 77 | this.flowCurrentProgram.useProgram() 78 | 79 | this.flowCurrentProgram.setUniform("u_matrix_model", cameraControls.identMatrix, UNIFORM_TYPE.mat) 80 | this.flowCurrentProgram.setUniform("u_matrix_view", cameraControls.particlesViewMatrix, UNIFORM_TYPE.mat) 81 | this.flowCurrentProgram.setUniform("u_matrix_projection", cameraControls.orthoMatrix, UNIFORM_TYPE.mat) 82 | 83 | GLUtils.gl.bindFramebuffer(GLUtils.gl.FRAMEBUFFER, this.particlesOffTextureData1.frameBuffer) 84 | GLUtils.gl.framebufferTexture2D(GLUtils.gl.FRAMEBUFFER, GLUtils.gl.COLOR_ATTACHMENT0, GLUtils.gl.TEXTURE_2D, this.particlesOffTextureData1.targetTexture, 0) 85 | GLUtils.gl.drawArrays(GLUtils.gl.POINTS, 0, this.particleCount); 86 | 87 | GLUtils.resetDrawingContext() 88 | GLUtils.setDefaultViewport() 89 | 90 | this.flowHistoryProgram.useProgram() 91 | let modelMatrix = mat4.create() 92 | let trans = vec3.fromValues(0, 0, 0.05) 93 | mat4.translate(modelMatrix, modelMatrix, trans) 94 | 95 | this.flowHistoryProgram.setUniform("u_matrix_model", modelMatrix, UNIFORM_TYPE.mat) 96 | this.flowHistoryProgram.setUniform("u_matrix_view", cameraControls.viewMatrix, UNIFORM_TYPE.mat) 97 | this.flowHistoryProgram.setUniform("u_matrix_projection", cameraControls.projMatrix, UNIFORM_TYPE.mat) 98 | this.flowHistoryProgram.setUniform("u_dimming_ratio", 1.0, UNIFORM_TYPE.f) 99 | GLUtils.bindTexture(this.particlesOffTextureData1.targetTexture) 100 | 101 | this.flowHistoryProgram.draw() 102 | 103 | let tmpOffInfo = this.particlesOffTextureData1 104 | this.particlesOffTextureData1 = this.particlesOffTextureData2 105 | this.particlesOffTextureData2 = tmpOffInfo 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/simplexDrawObject.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix"; 2 | import { SIMPLEXA_VERTEX_SHADER, SIMPLEXB_FRAGMENT_SHADER } from "../flowShaders"; 3 | import { GLUtils, OffTextureData } from "../glutils"; 4 | import { Program, UNIFORM_TYPE } from "../program/program"; 5 | import { SimplexProgram } from "../program/simplexProgram"; 6 | import { DrawObject, DrawParams } from "./drawObject"; 7 | import { CameraControls } from "../cameraControls"; 8 | 9 | export class SimplexDrawObject implements DrawObject { 10 | 11 | simplexProgram!: Program 12 | tempSimplexTexWrapper!: OffTextureData 13 | 14 | constructor() { 15 | this.setupPrograms() 16 | } 17 | 18 | setupPrograms(): void { 19 | this.simplexProgram = new SimplexProgram(GLUtils.gl, SIMPLEXA_VERTEX_SHADER, SIMPLEXB_FRAGMENT_SHADER) 20 | this.simplexProgram.generateData(0, null) 21 | } 22 | 23 | draw(drawParams: DrawParams, cameraControls: CameraControls): void { 24 | this.simplexProgram.useProgram() 25 | this.simplexProgram.setUniform("u_time", drawParams.time, UNIFORM_TYPE.f) 26 | 27 | let modelMatrix = mat4.create() 28 | let trans = vec3.fromValues(0, 0, -0.05) 29 | mat4.translate(modelMatrix, modelMatrix, trans) 30 | this.simplexProgram.setUniform("u_matrix_model", modelMatrix, UNIFORM_TYPE.mat) 31 | 32 | if (!drawParams.hideNoise) { 33 | this.simplexProgram.useProgram() 34 | this.simplexProgram.setUniform("u_time", drawParams.time, UNIFORM_TYPE.f) 35 | this.simplexProgram.setUniform("u_z_layer", 0.1, UNIFORM_TYPE.f) 36 | this.simplexProgram.setUniform("u_screen_size", [GLUtils.resolution.x, GLUtils.resolution.y], UNIFORM_TYPE.fv) 37 | this.simplexProgram.setUniform("u_filter_visualization", drawParams.simplexViewState, UNIFORM_TYPE.f) 38 | 39 | this.simplexProgram.setUniform("u_matrix_view", cameraControls.viewMatrix, UNIFORM_TYPE.mat) 40 | this.simplexProgram.setUniform("u_matrix_projection", cameraControls.projMatrix, UNIFORM_TYPE.mat) 41 | 42 | this.simplexProgram.draw() 43 | } 44 | 45 | // reset to draw all 46 | this.simplexProgram.setUniform("u_filter_visualization", 0.0, UNIFORM_TYPE.f) 47 | 48 | //offscreen - do not forget to draw without matrix transformations 49 | GLUtils.setCustomViewport(drawParams.simplexTextureSize.x, drawParams.simplexTextureSize.y) 50 | 51 | this.simplexProgram.setUniform("u_matrix_view", cameraControls.particlesViewMatrix, UNIFORM_TYPE.mat) 52 | this.simplexProgram.setUniform("u_matrix_projection", cameraControls.orthoMatrix, UNIFORM_TYPE.mat) 53 | this.simplexProgram.setUniform("u_z_layer", drawParams.layer * 0.1, UNIFORM_TYPE.f) 54 | this.simplexProgram.setUniform("u_screen_size", [drawParams.simplexTextureSize.x, drawParams.simplexTextureSize.y], UNIFORM_TYPE.fv) 55 | 56 | if (!this.tempSimplexTexWrapper) { 57 | this.tempSimplexTexWrapper = GLUtils.createOffscreenTexture(drawParams.simplexTextureSize.x, drawParams.simplexTextureSize.y) 58 | } else { 59 | GLUtils.gl.bindTexture(GLUtils.gl.TEXTURE_2D, this.tempSimplexTexWrapper.targetTexture) 60 | GLUtils.gl.bindFramebuffer(GLUtils.gl.FRAMEBUFFER, this.tempSimplexTexWrapper.frameBuffer) 61 | } 62 | this.simplexProgram.draw() 63 | 64 | //save bottom texture for flow animation 65 | if (drawParams.layer == 1) { 66 | drawParams.defaultSimplexTexture = this.tempSimplexTexWrapper.targetTexture 67 | } 68 | drawParams.currentSimplexTexture = this.tempSimplexTexWrapper.targetTexture 69 | 70 | GLUtils.resetDrawingContext() 71 | GLUtils.setDefaultViewport() 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/client/webgl2/drawObject/vectorFieldDrawObject.ts: -------------------------------------------------------------------------------- 1 | import { mat4, vec3 } from "gl-matrix"; 2 | import { CameraControls } from "../cameraControls"; 3 | import { ARROW_VERTEX_SHADER, ARROW_FRAGMENT_SHADER } from "../flowShaders"; 4 | import { GLUtils } from "../glutils"; 5 | import { ArrowProgram } from "../program/arrowProgram"; 6 | import { Program, UNIFORM_TYPE } from "../program/program"; 7 | import { DrawObject, DrawParams } from "./drawObject"; 8 | 9 | export class VectorFieldDrawObject implements DrawObject { 10 | 11 | fieldVectorProgram!: Program 12 | 13 | constructor() { 14 | this.setupPrograms() 15 | } 16 | 17 | setupPrograms(): void { 18 | this.fieldVectorProgram = new ArrowProgram(GLUtils.gl, ARROW_VERTEX_SHADER, ARROW_FRAGMENT_SHADER) 19 | this.fieldVectorProgram.generateData(0, null) 20 | } 21 | 22 | draw(drawParams: DrawParams, cameraControls: CameraControls): void { 23 | 24 | if (!drawParams.fieldVectorProgram) { 25 | drawParams.fieldVectorProgram = this.fieldVectorProgram 26 | } 27 | 28 | this.fieldVectorProgram.useProgram() 29 | 30 | GLUtils.bindTexture(drawParams.currentSimplexTexture) 31 | 32 | 33 | this.fieldVectorProgram.setUniform("u_matrix_view", cameraControls.viewMatrix, UNIFORM_TYPE.mat) 34 | this.fieldVectorProgram.setUniform("u_matrix_projection", cameraControls.projMatrix, UNIFORM_TYPE.mat) 35 | 36 | if (cameraControls.cameraFeedback.guiOptions.zprojection) { 37 | this.fieldVectorProgram.setUniform("u_z_projection", 1.0, UNIFORM_TYPE.f) 38 | } else { 39 | this.fieldVectorProgram.setUniform("u_z_projection", 0.0, UNIFORM_TYPE.f) 40 | } 41 | 42 | this.fieldVectorProgram.setUniform("u_fromTexel", 1.0, UNIFORM_TYPE.f) 43 | this.fieldVectorProgram.setUniform("u_noise_size", [drawParams.simplexTextureSize.x, drawParams.simplexTextureSize.y], UNIFORM_TYPE.iv) 44 | 45 | for (let i = 0; i <= drawParams.fieldVisualizationDensity; i++) { 46 | 47 | let u = i * (2.0 / drawParams.fieldVisualizationDensity) - 1 48 | 49 | for (let j = 0; j <= drawParams.fieldVisualizationDensity; j++) { 50 | 51 | let v = j * (2.0 / drawParams.fieldVisualizationDensity) - 1 52 | // move to atrubute 53 | this.fieldVectorProgram.setUniform("u_vector_index", [i / drawParams.fieldVisualizationDensity, j / drawParams.fieldVisualizationDensity], UNIFORM_TYPE.fv) 54 | 55 | let modelMatrix = mat4.create() 56 | mat4.translate(modelMatrix, modelMatrix, vec3.fromValues(u, v, drawParams.layer * 0.1)) 57 | this.fieldVectorProgram.setUniform("u_matrix_model", modelMatrix, UNIFORM_TYPE.mat) 58 | 59 | this.fieldVectorProgram.draw() 60 | } 61 | } 62 | this.fieldVectorProgram.setUniform("u_fromTexel", 1.0, UNIFORM_TYPE.f) 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/client/webgl2/flightHelper.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "nerdamer" 2 | import { clamp } from "./glutils" 3 | 4 | let nerdamer = require("nerdamer/all.min") 5 | 6 | export class FlightHelper { 7 | 8 | static flightPaths: Array 9 | 10 | static init() { 11 | this.flightPaths = new Array() 12 | this.flightPaths.push( 13 | new FlightPath("sin(3*x)", "e^(-1/(1-x^2))") 14 | ) 15 | 16 | for( let i = 0; i < 5; i++) { 17 | let offset = "" + (1 + 0.1*i) + "*" 18 | this.flightPaths.push( 19 | new FlightPath("0", offset + "e^(-1/(1-x^2))") 20 | ) 21 | } 22 | } 23 | 24 | static getFlightPathAt(index: number) : FlightPath { 25 | return this.flightPaths[index] 26 | } 27 | } 28 | 29 | class FlightPath { 30 | 31 | expressionZ: Expression 32 | expressionY: Expression 33 | 34 | evaulateZ: any 35 | evaulateY: any 36 | 37 | constructor(valuesYGenerator: string, valuesZGenerator: string) { 38 | this.expressionZ = nerdamer(valuesZGenerator) 39 | this.expressionY = nerdamer(valuesYGenerator) 40 | 41 | this.evaulateZ = this.expressionZ.buildFunction() 42 | this.evaulateY = this.expressionY.buildFunction() 43 | } 44 | 45 | //into the space towards camera 46 | flightZPathAt(x: number) : number { 47 | x = clamp(x, -0.98, 0.98) 48 | return this.evaulateZ(x) 49 | } 50 | 51 | //top to bottom of screen 52 | flightYPathAt(x: number) : number { 53 | x = clamp(x, -0.98, 0.98) 54 | return this.evaulateY(x) 55 | } 56 | 57 | sampleFlightPath(sampleSize: number) : Array> { 58 | let samplesZ = new Array(sampleSize + 1) 59 | let samplesY = new Array(sampleSize + 1) 60 | let domain = new Array(sampleSize + 1) 61 | const step = 2.0 / sampleSize 62 | 63 | for (let i = 0; i <= sampleSize; i++ ) { 64 | //bump is defined at <-1;1> 65 | let position = step * i - 1 66 | domain[i] = position 67 | samplesZ[i] = this.flightZPathAt(position) 68 | samplesY[i] = this.flightYPathAt(position) 69 | } 70 | 71 | return new Array(domain, samplesZ, samplesY) 72 | } 73 | } -------------------------------------------------------------------------------- /src/client/webgl2/flowShaders.ts: -------------------------------------------------------------------------------- 1 | export const ARROW_VERTEX_SHADER = `#version 300 es 2 | 3 | in vec4 a_position; 4 | uniform vec2 u_vector_index; 5 | uniform ivec2 u_noise_size; 6 | uniform mat4 u_matrix_model; 7 | uniform mat4 u_matrix_view; 8 | uniform mat4 u_matrix_projection; 9 | uniform sampler2D noiseTex; 10 | 11 | // we can draw arrow with certain angle via texel or value passed to shader 12 | uniform float u_fromTexel; 13 | uniform float u_arrowXYAngleValue; 14 | uniform float u_arrowXZAngleValue; 15 | uniform float u_arrowSizeValue; 16 | uniform float u_z_projection; 17 | 18 | const float PI = 3.141; 19 | 20 | float map(float value, float min1, float max1, float min2, float max2) { 21 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 22 | } 23 | 24 | void main() { 25 | vec4 noiseValue = vec4(0.0); 26 | 27 | if (u_fromTexel == 1.0) { 28 | ivec2 texelCoords = ivec2(round(vec2(u_vector_index.x*float(u_noise_size.x), u_vector_index.y*float(u_noise_size.y)))); 29 | // edge is non-existent 30 | texelCoords.x = min( texelCoords.x, u_noise_size.x - 1); 31 | texelCoords.y = min( texelCoords.y, u_noise_size.y - 1); 32 | 33 | noiseValue = texelFetch(noiseTex, texelCoords, 0); 34 | } else { 35 | noiseValue = vec4(u_arrowXYAngleValue, u_arrowXZAngleValue, u_arrowSizeValue, 0.0); 36 | } 37 | 38 | float angle = map(noiseValue.r, 0.0, 1.0, 0.0, PI * 2.0); 39 | float zAngle = map(noiseValue.g, 0.0, 1.0, 0.0, PI * 2.0); 40 | 41 | if (u_z_projection == 1.0) { 42 | zAngle = 0.0; 43 | } 44 | 45 | float noiseScaleFactor = 0.05 + noiseValue.b / 10.0; 46 | mat4 noiseScale = mat4(noiseScaleFactor, 0.0, 0.0, 0.0, // 1. column 47 | 0.0, noiseScaleFactor, 0.0, 0.0, // 2. column 48 | 0.0, 0.0, noiseScaleFactor, 0.0, // 3. column 49 | 0.0, 0.0, 0.0, 1.0); // 4. column 50 | 51 | // scale 52 | vec4 rotated_position = noiseScale*a_position;//*(1.0 + noiseValue.b*1000.0); 53 | // Basic xy rotation 54 | rotated_position = vec4(rotated_position.x*cos(angle) - rotated_position.y*sin(angle), rotated_position.x*sin(angle) + rotated_position.y*cos(angle), rotated_position.zw); 55 | // Basic xz rotation 56 | rotated_position = vec4(rotated_position.x*cos(zAngle), rotated_position.y, -rotated_position.x*sin(zAngle) , rotated_position.w); 57 | gl_Position = u_matrix_projection * u_matrix_view * u_matrix_model * rotated_position; 58 | } 59 | ` 60 | 61 | export const ARROW_FRAGMENT_SHADER = `#version 300 es 62 | 63 | precision highp float; 64 | out vec4 outColor; 65 | 66 | void main() { 67 | outColor = vec4(0, 0, 0.5, 1); 68 | } 69 | ` 70 | 71 | export const FLOW_HISTORY_VERTEX_SHADER = `#version 300 es 72 | 73 | in vec4 a_position; 74 | in vec2 a_texcoord; 75 | uniform mat4 u_matrix_model; 76 | uniform mat4 u_matrix_view; 77 | uniform mat4 u_matrix_projection; 78 | 79 | out vec2 v_texcoord; 80 | 81 | void main() { 82 | gl_Position = u_matrix_projection * u_matrix_view * u_matrix_model * a_position; 83 | v_texcoord = a_texcoord; 84 | } 85 | ` 86 | 87 | export const FLOW_HISTORY_FRAGMENT_SHADER = `#version 300 es 88 | 89 | precision highp float; 90 | out vec4 outColor; 91 | uniform sampler2D u_flowHistoryTex; 92 | uniform float u_dimming_ratio; 93 | in vec2 v_texcoord; 94 | 95 | void main() { 96 | vec4 dimmedColor = texture(u_flowHistoryTex, v_texcoord)*u_dimming_ratio; 97 | if (dimmedColor.a < 0.2) { 98 | dimmedColor.a = 0.0; 99 | } 100 | outColor = dimmedColor; 101 | } 102 | ` 103 | 104 | export const FLOW_STEP_VERTEX_SHADER = `#version 300 es 105 | in vec3 oldPosition; 106 | 107 | uniform float u_time_delta; 108 | uniform ivec2 u_noise_size; 109 | 110 | out vec3 newPosition; 111 | 112 | uniform sampler2D noiseTex; 113 | 114 | const float PI = 3.141; 115 | 116 | float map(float value, float min1, float max1, float min2, float max2) { 117 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 118 | } 119 | 120 | float rand(vec2 n, float simplexValue) 121 | { 122 | return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))*41753.5453); 123 | } 124 | 125 | void main() { 126 | 127 | float xPosition = (oldPosition.x + 1.0)/2.0; 128 | float yPosition = (oldPosition.y + 1.0)/2.0; 129 | 130 | ivec2 texelCoords = ivec2(round(vec2(xPosition*float(u_noise_size.x),yPosition*float(u_noise_size.y)))); 131 | // edge is non-existent 132 | texelCoords.x = min( texelCoords.x, u_noise_size.x - 1); 133 | texelCoords.y = min( texelCoords.y, u_noise_size.y - 1); 134 | 135 | vec4 noiseValue = texelFetch(noiseTex, texelCoords, 0); 136 | 137 | float angle = map(noiseValue.r, 0.0, 1.0, 0.0, PI * 2.0); 138 | vec2 angleVelocity = vec2(cos(angle), sin(angle)); 139 | 140 | vec2 absolutePosition = oldPosition.xy + angleVelocity * u_time_delta*0.5; 141 | 142 | vec2 particleSeed = vec2(xPosition, yPosition); 143 | if (rand(particleSeed, noiseValue.r) > 0.98) { 144 | float randX = (rand(particleSeed + noiseValue.r*1.78, noiseValue.r) - 1.0)*2.0; 145 | float randY = (rand(particleSeed + noiseValue.r*2.37, noiseValue.r) - 1.0)*2.0; 146 | absolutePosition = vec2(randX, randY); 147 | } 148 | 149 | if (absolutePosition.x > 1.0) { 150 | absolutePosition.x -= 2.0; 151 | } 152 | if (absolutePosition.y > 1.0) { 153 | absolutePosition.y -= 2.0; 154 | } 155 | if (absolutePosition.x < -1.0) { 156 | absolutePosition.x += 2.0; 157 | } 158 | if (absolutePosition.y < -1.0) { 159 | absolutePosition.y += 2.0; 160 | } 161 | 162 | newPosition = vec3(absolutePosition, noiseValue.b); 163 | } 164 | `; 165 | 166 | export const FLOW_STEP_FRAGMENT_SHADER = `#version 300 es 167 | precision highp float; 168 | void main() { 169 | } 170 | ` 171 | 172 | export const FLOW_DRAW_VERTEX_SHADER = `#version 300 es 173 | in vec4 position; 174 | uniform mat4 u_matrix_model; 175 | uniform mat4 u_matrix_view; 176 | uniform mat4 u_matrix_projection; 177 | 178 | out float v_particle_speed; 179 | 180 | void main() { 181 | gl_PointSize = 1.5; 182 | v_particle_speed = position.z; 183 | gl_Position = u_matrix_projection * u_matrix_view * u_matrix_model * position; 184 | } 185 | ` 186 | 187 | export const FLOW_DRAW_FRAGMENT_SHADER = `#version 300 es 188 | precision highp float; 189 | 190 | in float v_particle_speed; 191 | out vec4 outColor; 192 | 193 | float map(float value, float min1, float max1, float min2, float max2) { 194 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 195 | } 196 | 197 | void main() { 198 | float noiseMax = 0.8; 199 | float r = map(v_particle_speed, 0.0, noiseMax, 0.0, 0.8); 200 | float g = map(v_particle_speed, 0.0, noiseMax, 0.44, 0.0); 201 | float b = map(v_particle_speed, 0.0, noiseMax, 0.69, 0.12); 202 | outColor = vec4(r, g, b, 1); 203 | } 204 | ` 205 | 206 | const SIMPLEX_METHODS = ` 207 | // 208 | // Description : Array and textureless GLSL 2D/3D/4D simplex 209 | // noise functions. 210 | // Author : Ian McEwan, Ashima Arts. 211 | // Maintainer : stegu 212 | // Lastmod : 20110822 (ijm) 213 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 214 | // Distributed under the MIT License. See LICENSE file. 215 | // https://github.com/ashima/webgl-noise 216 | // https://github.com/stegu/webgl-noise 217 | // 218 | 219 | vec4 mod289(vec4 x) { 220 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 221 | 222 | float mod289(float x) { 223 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 224 | 225 | vec4 permute(vec4 x) { 226 | return mod289(((x*34.0)+1.0)*x); 227 | } 228 | 229 | float permute(float x) { 230 | return mod289(((x*34.0)+1.0)*x); 231 | } 232 | 233 | vec4 taylorInvSqrt(vec4 r) 234 | { 235 | return 1.79284291400159 - 0.85373472095314 * r; 236 | } 237 | 238 | float taylorInvSqrt(float r) 239 | { 240 | return 1.79284291400159 - 0.85373472095314 * r; 241 | } 242 | 243 | vec4 grad4(float j, vec4 ip) 244 | { 245 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 246 | vec4 p,s; 247 | 248 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 249 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 250 | s = vec4(lessThan(p, vec4(0.0))); 251 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 252 | 253 | return p; 254 | } 255 | 256 | // (sqrt(5) - 1)/4 = F4, used once below 257 | #define F4 0.309016994374947451 258 | 259 | float snoise(vec4 v) 260 | { 261 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 262 | 0.276393202250021, // 2 * G4 263 | 0.414589803375032, // 3 * G4 264 | -0.447213595499958); // -1 + 4 * G4 265 | 266 | // First corner 267 | vec4 i = floor(v + dot(v, vec4(F4)) ); 268 | vec4 x0 = v - i + dot(i, C.xxxx); 269 | 270 | // Other corners 271 | 272 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 273 | vec4 i0; 274 | vec3 isX = step( x0.yzw, x0.xxx ); 275 | vec3 isYZ = step( x0.zww, x0.yyz ); 276 | // i0.x = dot( isX, vec3( 1.0 ) ); 277 | i0.x = isX.x + isX.y + isX.z; 278 | i0.yzw = 1.0 - isX; 279 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 280 | i0.y += isYZ.x + isYZ.y; 281 | i0.zw += 1.0 - isYZ.xy; 282 | i0.z += isYZ.z; 283 | i0.w += 1.0 - isYZ.z; 284 | 285 | // i0 now contains the unique values 0,1,2,3 in each channel 286 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 287 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 288 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 289 | 290 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 291 | // x1 = x0 - i1 + 1.0 * C.xxxx 292 | // x2 = x0 - i2 + 2.0 * C.xxxx 293 | // x3 = x0 - i3 + 3.0 * C.xxxx 294 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 295 | vec4 x1 = x0 - i1 + C.xxxx; 296 | vec4 x2 = x0 - i2 + C.yyyy; 297 | vec4 x3 = x0 - i3 + C.zzzz; 298 | vec4 x4 = x0 + C.wwww; 299 | 300 | // Permutations 301 | i = mod289(i); 302 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 303 | vec4 j1 = permute( permute( permute( permute ( 304 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 305 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 306 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 307 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 308 | 309 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 310 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 311 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 312 | 313 | vec4 p0 = grad4(j0, ip); 314 | vec4 p1 = grad4(j1.x, ip); 315 | vec4 p2 = grad4(j1.y, ip); 316 | vec4 p3 = grad4(j1.z, ip); 317 | vec4 p4 = grad4(j1.w, ip); 318 | 319 | // Normalise gradients 320 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 321 | p0 *= norm.x; 322 | p1 *= norm.y; 323 | p2 *= norm.z; 324 | p3 *= norm.w; 325 | p4 *= taylorInvSqrt(dot(p4,p4)); 326 | 327 | // Mix contributions from the five corners 328 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 329 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 330 | m0 = m0 * m0; 331 | m1 = m1 * m1; 332 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 333 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 334 | 335 | } 336 | 337 | // demo code: 338 | float color(vec3 xyz, float time) { return snoise(vec4(xyz, 0.3*time)); } 339 | ` 340 | 341 | export const SIMPLEXA_VERTEX_SHADER = `#version 300 es 342 | 343 | in vec4 a_position; 344 | uniform mat4 u_matrix_model; 345 | uniform mat4 u_matrix_view; 346 | uniform mat4 u_matrix_projection; 347 | 348 | out vec2 v_texcoord; 349 | 350 | void main() { 351 | gl_Position = u_matrix_projection * u_matrix_view * u_matrix_model * a_position; 352 | v_texcoord = a_position.xy; 353 | } 354 | ` 355 | 356 | export const SIMPLEXB_FRAGMENT_SHADER = `#version 300 es 357 | precision highp float; 358 | 359 | ${SIMPLEX_METHODS} 360 | 361 | uniform float u_time; 362 | uniform float u_z_layer; 363 | uniform vec2 u_screen_size; 364 | // 0 - draw combined 365 | // 1 - draw xy angle only 366 | // 2 - draw xz angle only 367 | // 3 - draw vector abs size only 368 | uniform float u_filter_visualization; 369 | out vec4 outColor; 370 | in vec2 v_texcoord; 371 | 372 | // used to sample values for angle in xy plane 373 | const float X_ANGLE_OFFSET = 0.0; 374 | // used to sample values for angle in xz plane 375 | const float Y_ANGLE_OFFSET = -50.0; 376 | // used to sample vector size 377 | const float SIZE_OFFSET = -100.0; 378 | 379 | 380 | float sampleNoise(vec3 xyz) { 381 | vec3 step = vec3(1.3, 1.7, 2.1); 382 | 383 | float n = color(xyz, u_time); 384 | n += 0.5 * color(xyz * 2.0 - step, u_time); 385 | n += 0.25 * color(xyz * 4.0 - 2.0 * step, u_time); 386 | n += 0.125 * color(xyz * 8.0 - 3.0 * step, u_time); 387 | n += 0.0625 * color(xyz * 16.0 - 4.0 * step, u_time); 388 | n += 0.03125 * color(xyz * 32.0 - 5.0 * step, u_time); 389 | 390 | return n; 391 | } 392 | 393 | void main() { 394 | vec2 p = v_texcoord; 395 | vec3 xyz = vec3(p, u_z_layer); 396 | float n = sampleNoise(xyz); 397 | 398 | vec3 combinedNoiseTexel = vec3(0, 0, 0); 399 | for (int i = 0; i < 3; i++) { 400 | float offset = 0.0; 401 | vec3 mask_vector = vec3(1, 0, 0); 402 | if (i == 1) { 403 | offset = Y_ANGLE_OFFSET; 404 | mask_vector = vec3(0, 1, 0); 405 | } else if (i == 2) { 406 | offset = SIZE_OFFSET; 407 | mask_vector = vec3(0, 0, 1); 408 | } 409 | 410 | vec3 xyzToSample = vec3(p, u_z_layer + offset); 411 | float sampledValue = sampleNoise(xyzToSample); 412 | vec3 scaledSample = mix(0.0, 0.5 + 0.5 * sampledValue, smoothstep(0.0, 0.003, 1.0)) * mask_vector; 413 | combinedNoiseTexel += scaledSample; 414 | } 415 | 416 | if (u_filter_visualization == 0.0) { 417 | outColor = vec4(combinedNoiseTexel, 1); 418 | } else if (u_filter_visualization == 1.0) { 419 | outColor = vec4(combinedNoiseTexel.r, combinedNoiseTexel.r, combinedNoiseTexel.r, 1); 420 | } else if (u_filter_visualization == 2.0) { 421 | outColor = vec4(combinedNoiseTexel.g, combinedNoiseTexel.g, combinedNoiseTexel.g, 1); 422 | } else if (u_filter_visualization == 3.0) { 423 | outColor = vec4(combinedNoiseTexel.b, combinedNoiseTexel.b, combinedNoiseTexel.b, 1); 424 | } 425 | 426 | // testing values, XY angle, XZ angle, size 427 | // outColor = vec4(0.25, 0, 1.0, 1); 428 | // outColor = vec4(0.5, 0, 1.0, 1); 429 | // outColor = vec4(1.0, 0, 1.0, 1); 430 | } 431 | ` 432 | 433 | export const FLIGHT_VERTEX_SHADER = `#version 300 es 434 | in vec4 a_position; 435 | uniform mat4 u_matrix_model; 436 | uniform mat4 u_matrix_view; 437 | uniform mat4 u_matrix_projection; 438 | 439 | void main() { 440 | gl_Position = u_matrix_projection * u_matrix_view * u_matrix_model * a_position; 441 | } 442 | ` 443 | 444 | export const FLIGHT_FRAGMENT_SHADER = `#version 300 es 445 | precision highp float; 446 | uniform float u_color_r; 447 | out vec4 outColor; 448 | void main() { 449 | outColor = vec4(0, 0, 1.0 - u_color_r, 1); 450 | } 451 | ` 452 | 453 | export const SAMPLE_NOISE_VERTEX_SHADER = `#version 300 es 454 | ${SIMPLEX_METHODS} 455 | in vec3 a_positionToSample; 456 | out vec3 sampledValue; 457 | 458 | uniform float u_time; 459 | 460 | // used to sample values for angle in xy plane 461 | const float X_ANGLE_OFFSET = 0.0; 462 | // used to sample values for angle in xz plane 463 | const float Y_ANGLE_OFFSET = -50.0; 464 | // used to sample vector size 465 | const float SIZE_OFFSET = -100.0; 466 | 467 | 468 | float sampleNoise(vec3 xyz) { 469 | vec3 step = vec3(1.3, 1.7, 2.1); 470 | 471 | float n = color(xyz, u_time); 472 | n += 0.5 * color(xyz * 2.0 - step, u_time); 473 | n += 0.25 * color(xyz * 4.0 - 2.0 * step, u_time); 474 | n += 0.125 * color(xyz * 8.0 - 3.0 * step, u_time); 475 | n += 0.0625 * color(xyz * 16.0 - 4.0 * step, u_time); 476 | n += 0.03125 * color(xyz * 32.0 - 5.0 * step, u_time); 477 | 478 | return n; 479 | } 480 | 481 | float map(float value, float min1, float max1, float min2, float max2) { 482 | return min2 + (value - min1) * (max2 - min2) / (max1 - min1); 483 | } 484 | 485 | void main() { 486 | 487 | vec3 p = a_positionToSample; 488 | vec3 combinedNoiseTexel = vec3(0, 0, 0); 489 | 490 | for (int i = 0; i < 3; i++) { 491 | float offset = 0.0; 492 | vec3 mask_vector = vec3(1, 0, 0); 493 | if (i == 1) { 494 | offset = Y_ANGLE_OFFSET; 495 | mask_vector = vec3(0, 1, 0); 496 | } else if (i == 2) { 497 | offset = SIZE_OFFSET; 498 | mask_vector = vec3(0, 0, 1); 499 | } 500 | 501 | vec3 xyzToSample = vec3(p.x, p.z, p.y + offset); 502 | float sampledValue = sampleNoise(xyzToSample); 503 | vec3 scaledSample = mix(0.0, 0.5 + 0.5 * sampledValue, smoothstep(0.0, 0.003, 1.0)) * mask_vector; 504 | combinedNoiseTexel += scaledSample; 505 | } 506 | 507 | sampledValue = combinedNoiseTexel.xyz; 508 | 509 | // testing values, XY angle, XZ angle, size 510 | // sampledValue = vec3(0.25, 0.0, 0.5); 511 | // sampledValue = vec3(0.5, 0.0, 0.5); 512 | // sampledValue = vec3(1, 0.0, 0.5); 513 | } 514 | `; 515 | 516 | export const SAMPLE_NOISE_FRAGMENT_SHADER = `#version 300 es 517 | precision highp float; 518 | void main() { 519 | } 520 | ` 521 | 522 | 523 | -------------------------------------------------------------------------------- /src/client/webgl2/glutils.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "./logger" 2 | 3 | export class GLUtils { 4 | 5 | static gl: WebGL2RenderingContext 6 | static canvas: HTMLCanvasElement 7 | static resolution = { 8 | x: window.innerWidth, 9 | y: window.innerHeight 10 | } 11 | 12 | static init(gl: WebGL2RenderingContext, canvas: HTMLCanvasElement) { 13 | this.gl = gl 14 | this.canvas = canvas 15 | 16 | this.setDimensions() 17 | } 18 | 19 | private static setDimensions() { 20 | const dpr = window.devicePixelRatio; 21 | this.canvas!.height = Math.round(this.resolution.y*dpr) 22 | this.canvas!.width = Math.round(this.resolution.x*dpr) 23 | } 24 | 25 | static getAspectRatio() : number { 26 | return this.resolution.x / this.resolution.y 27 | } 28 | 29 | static setupScene() { 30 | this.setDefaultViewport() 31 | 32 | this.gl.clearColor(0, 0, 0, 1) 33 | this.gl.clear(this.gl.COLOR_BUFFER_BIT) 34 | this.gl.enable(this.gl.DEPTH_TEST) 35 | } 36 | 37 | static setDefaultViewport() { 38 | this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height) 39 | } 40 | 41 | static setCustomViewport(width: number, height: number) { 42 | this.gl.viewport(0, 0, width, height) 43 | } 44 | 45 | static compileShader(type: any, source: string) : WebGLShader | null { 46 | let shader = this.gl.createShader(type) 47 | 48 | if (!shader) { 49 | Logger.log("Shader not created") 50 | } else { 51 | this.gl.shaderSource(shader, source) 52 | this.gl.compileShader(shader) 53 | const shaderStatus = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS) 54 | 55 | if (shaderStatus) { 56 | Logger.log("Shader created") 57 | return shader 58 | } 59 | } 60 | 61 | return null 62 | 63 | } 64 | 65 | static linkProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader, transformFeedbackVaryings: [string] | null = null) : WebGLProgram | null { 66 | let program = this.gl.createProgram() 67 | 68 | if (!program) { 69 | Logger.log("Shader not created") 70 | } else { 71 | this.gl.attachShader(program, vertexShader) 72 | this.gl.attachShader(program, fragmentShader) 73 | 74 | if (transformFeedbackVaryings) { 75 | this.gl.transformFeedbackVaryings(program, transformFeedbackVaryings, this.gl.SEPARATE_ATTRIBS) 76 | } 77 | 78 | this.gl.linkProgram(program) 79 | 80 | const programStatus = this.gl.getProgramParameter(program, this.gl.LINK_STATUS) 81 | 82 | if (programStatus) { 83 | Logger.log("Program created") 84 | return program 85 | } 86 | } 87 | 88 | this.gl.deleteProgram(program) 89 | return null 90 | } 91 | 92 | static bindTexture(texture: WebGLTexture) { 93 | this.gl.bindTexture(this.gl.TEXTURE_2D, texture) 94 | } 95 | 96 | static createOffscreenTexture(targetTextureWidth: number, targetTextureHeight: number) : OffTextureData { 97 | let targetTexture = this.gl.createTexture()!; 98 | this.gl.bindTexture(this.gl.TEXTURE_2D, targetTexture); 99 | 100 | let level = 0; 101 | let internalFormat = this.gl.RGBA 102 | let border = 0 103 | let format = this.gl.RGBA 104 | let typeTex = this.gl.UNSIGNED_BYTE 105 | let data = null 106 | this.gl.texImage2D(this.gl.TEXTURE_2D, level, internalFormat, 107 | targetTextureWidth, targetTextureHeight, border, 108 | format, typeTex, data) 109 | 110 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR) 111 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE) 112 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE) 113 | 114 | const fb = this.gl.createFramebuffer() 115 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fb) 116 | 117 | const attachmentPoint = this.gl.COLOR_ATTACHMENT0 118 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, attachmentPoint, this.gl.TEXTURE_2D, targetTexture, level) 119 | 120 | this.gl.clearColor(1, 1, 1, 1) 121 | this.gl.viewport(0, 0, targetTextureWidth, targetTextureHeight) 122 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT) 123 | 124 | return new OffTextureData(targetTexture, fb!) 125 | } 126 | 127 | static createTexture(targetTextureWidth: number, targetTextureHeight: number, data: Uint8Array) : WebGLTexture { 128 | let targetTexture = this.gl.createTexture()!; 129 | this.gl.bindTexture(this.gl.TEXTURE_2D, targetTexture); 130 | 131 | let level = 0; 132 | let internalFormat = this.gl.RGBA 133 | let border = 0 134 | let format = this.gl.RGBA 135 | let typeTex = this.gl.UNSIGNED_BYTE 136 | this.gl.texImage2D(this.gl.TEXTURE_2D, level, internalFormat, 137 | targetTextureWidth, targetTextureHeight, border, 138 | format, typeTex, data) 139 | 140 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR) 141 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE) 142 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE) 143 | 144 | return targetTexture 145 | } 146 | 147 | static resetDrawingContext() { 148 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); 149 | this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height); 150 | } 151 | 152 | static degToRad(deg : number) : number { 153 | return deg*Math.PI/180 154 | } 155 | } 156 | 157 | export const clamp = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max) 158 | 159 | export class OffTextureData { 160 | constructor(public targetTexture: WebGLTexture, public frameBuffer: WebGLFramebuffer) {} 161 | } -------------------------------------------------------------------------------- /src/client/webgl2/interpolationHelper.ts: -------------------------------------------------------------------------------- 1 | import { Interpolator, createAkimaSplineInterpolator } from "./mathUtils" 2 | import { FlightHelper } from "./flightHelper" 3 | 4 | export class InterpolationHelper { 5 | 6 | // domain + values 7 | pathOriginalPoints: Array>> 8 | pathAkimaXZCoefficients: Array[]> 9 | pathAkimaXYCoefficients: Array[]> 10 | 11 | // vector field values sampled along the line integral - same domain as path: pathOriginalPoints[0] 12 | fieldOriginalXYPoints!: Array 13 | fieldOriginalXZPoints!: Array 14 | fieldOriginalAbsSizePoints!: Array 15 | fieldAkimaCoefficients!: ArrayLike[] 16 | fieldAkimaXCoefficients!: Array[]> 17 | fieldAkimaYCoefficients!: Array[]> 18 | fieldAkimaZCoefficients!: Array[]> 19 | 20 | // number of splines 21 | sampleSize: number 22 | maxIndex = -1 23 | 24 | timeNode: Text 25 | indexNode: Text 26 | worker: Worker 27 | workerInProgress = false 28 | 29 | constructor(sampleSize: number) { 30 | this.sampleSize = sampleSize 31 | this.worker = new Worker(new URL('interpolationWorker.ts', import.meta.url)) 32 | 33 | this.pathOriginalPoints = new Array>>(FlightHelper.flightPaths.length) 34 | this.pathAkimaXZCoefficients = new Array[]>(FlightHelper.flightPaths.length) 35 | this.pathAkimaXYCoefficients = new Array[]>(FlightHelper.flightPaths.length) 36 | 37 | this.fieldAkimaXCoefficients = new Array[]>(FlightHelper.flightPaths.length) 38 | this.fieldAkimaYCoefficients = new Array[]>(FlightHelper.flightPaths.length) 39 | this.fieldAkimaZCoefficients = new Array[]>(FlightHelper.flightPaths.length) 40 | 41 | for (let i = 0; i < FlightHelper.flightPaths.length; i++) { 42 | this.pathOriginalPoints[i] = FlightHelper.getFlightPathAt(i).sampleFlightPath(sampleSize) 43 | 44 | // XZ coeffs 45 | let fnc = createAkimaSplineInterpolator(this.pathOriginalPoints[i][0], this.pathOriginalPoints[i][1]) 46 | this.pathAkimaXZCoefficients[i] = Interpolator.cachedCoefficients 47 | 48 | // XY coeffs 49 | fnc = createAkimaSplineInterpolator(this.pathOriginalPoints[i][0], this.pathOriginalPoints[i][2]) 50 | this.pathAkimaXYCoefficients[i] = Interpolator.cachedCoefficients 51 | } 52 | 53 | let timeElement = document.querySelector("#pathprices") 54 | this.timeNode = document.createTextNode(""); 55 | timeElement!.appendChild(this.timeNode) 56 | 57 | let indexElement = document.querySelector("#bestpriceindex") 58 | this.indexNode = document.createTextNode(""); 59 | indexElement!.appendChild(this.indexNode) 60 | } 61 | 62 | createNoiseInterpolation(fieldOriginalXYPoints: Float32Array, fieldOriginalXZPoints: Float32Array, fieldOriginalAbsSizePoints: Float32Array, flightIndex: number) { 63 | this.fieldOriginalXYPoints = Array.prototype.slice.call(fieldOriginalXYPoints) 64 | this.fieldOriginalXZPoints = Array.prototype.slice.call(fieldOriginalXZPoints) 65 | this.fieldOriginalAbsSizePoints = Array.prototype.slice.call(fieldOriginalAbsSizePoints) 66 | 67 | // convert from our polar space to Cartesian space 68 | let multipleCoeff = 2*Math.PI 69 | let xVector = new Array() 70 | let yVector = new Array() 71 | let zVector = new Array() 72 | for (let i = 0; i < this.pathOriginalPoints[0][0].length; i++) { 73 | xVector.push(this.fieldOriginalAbsSizePoints[i]*Math.cos(this.fieldOriginalXYPoints[i]*multipleCoeff)*Math.cos(this.fieldOriginalXZPoints[i]*multipleCoeff)) 74 | yVector.push(this.fieldOriginalAbsSizePoints[i]*Math.sin(this.fieldOriginalXYPoints[i]*multipleCoeff)*Math.cos(this.fieldOriginalXZPoints[i]*multipleCoeff)) 75 | zVector.push(this.fieldOriginalAbsSizePoints[i]*Math.cos(this.fieldOriginalXYPoints[i]*multipleCoeff)*Math.sin(this.fieldOriginalXZPoints[i]*multipleCoeff)) 76 | } 77 | 78 | let fncX = createAkimaSplineInterpolator(this.pathOriginalPoints[0][0], xVector) 79 | this.fieldAkimaXCoefficients[flightIndex] = Interpolator.cachedCoefficients 80 | 81 | let fncY = createAkimaSplineInterpolator(this.pathOriginalPoints[0][0], yVector) 82 | this.fieldAkimaYCoefficients[flightIndex] = Interpolator.cachedCoefficients 83 | 84 | let fncZ = createAkimaSplineInterpolator(this.pathOriginalPoints[0][0], zVector) 85 | this.fieldAkimaZCoefficients[flightIndex] = Interpolator.cachedCoefficients 86 | } 87 | 88 | calculateFieldWork() { 89 | 90 | if (this.workerInProgress) { 91 | return 92 | } 93 | 94 | this.workerInProgress = true 95 | 96 | this.timeNode.nodeValue = "Calculating: 0 %" 97 | 98 | this.worker.postMessage({ 99 | 'pathAkimaXZCoefficients': this.pathAkimaXZCoefficients, 100 | 'pathAkimaXYCoefficients': this.pathAkimaXYCoefficients, 101 | 'pathOriginalPoints': this.pathOriginalPoints, 102 | 'fieldAkimaXCoefficients': this.fieldAkimaXCoefficients, 103 | 'fieldAkimaYCoefficients': this.fieldAkimaYCoefficients, 104 | 'fieldAkimaZCoefficients': this.fieldAkimaZCoefficients, 105 | }) 106 | this.worker.onmessage = event => { 107 | if (event.data.progress) { 108 | this.timeNode.nodeValue = `Calculating: ${event.data.progress} %` 109 | } else { 110 | this.timeNode.nodeValue = "" + event.data.flightsPathCost 111 | this.workerInProgress = false 112 | 113 | let maxValue = -50 114 | event.data.flightsPathCost.forEach((element: number, index: number) => { 115 | if (element > maxValue) { 116 | maxValue = element 117 | this.maxIndex = index 118 | } 119 | }); 120 | 121 | this.indexNode.nodeValue = "" + this.maxIndex 122 | } 123 | } 124 | 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /src/client/webgl2/interpolationWorker.ts: -------------------------------------------------------------------------------- 1 | let nerdamer = require("nerdamer/all.min") 2 | import { Expression } from "nerdamer" 3 | 4 | onmessage = (message) => { 5 | 6 | let pathAkimaXZCoefficients = message.data.pathAkimaXZCoefficients 7 | let pathAkimaXYCoefficients = message.data.pathAkimaXYCoefficients 8 | let fieldAkimaXCoefficients = message.data.fieldAkimaXCoefficients 9 | let fieldAkimaYCoefficients = message.data.fieldAkimaYCoefficients 10 | let fieldAkimaZCoefficients = message.data.fieldAkimaZCoefficients 11 | let pathOriginalPoints = message.data.pathOriginalPoints 12 | 13 | let flightsPathCost = Array() 14 | 15 | for (let flightIndex = 0; flightIndex < pathAkimaXZCoefficients.length; flightIndex++) { 16 | let pathXZEquations = generatePathEquations(pathAkimaXZCoefficients[flightIndex], pathOriginalPoints[flightIndex]) 17 | let pathXYEquations = generatePathEquations(pathAkimaXYCoefficients[flightIndex], pathOriginalPoints[flightIndex]) 18 | // x vector field 19 | let fieldXEquations = generateFieldEquations(fieldAkimaXCoefficients[flightIndex], pathOriginalPoints[flightIndex]) 20 | // y vector field 21 | let fieldYEquations = generateFieldEquations(fieldAkimaYCoefficients[flightIndex], pathOriginalPoints[flightIndex]) 22 | // z vector field - is actually y direction 23 | let fieldZEquations = generateFieldEquations(fieldAkimaZCoefficients[flightIndex], pathOriginalPoints[flightIndex]) 24 | 25 | let pathXZDiffEquations = new Array() 26 | pathXZEquations.forEach(pathEquation => { 27 | pathXZDiffEquations.push(nerdamer.diff(pathEquation, 'x')) 28 | }) 29 | 30 | let pathXYDiffEquations = new Array() 31 | pathXYEquations.forEach(pathEquation => { 32 | pathXYDiffEquations.push(nerdamer.diff(pathEquation, 'x')) 33 | }) 34 | 35 | let numIntegrands = new Array() 36 | let finalIntegrals = new Array() 37 | let pathCost = 0 38 | 39 | for (let i = 0; i < pathXZDiffEquations.length; i++) { 40 | let dotXProduct = fieldXEquations[i] 41 | let dotYProduct = fieldYEquations[i].multiply(pathXZDiffEquations[i]) 42 | let dotZProduct = fieldZEquations[i].multiply(pathXYDiffEquations[i]) 43 | 44 | let integrand = dotXProduct.add(dotYProduct).add(dotZProduct) 45 | 46 | numIntegrands.push(integrand) 47 | let finalIntegral = nerdamer.integrate(integrand.expand(), 'x') 48 | finalIntegrals.push(finalIntegral) 49 | 50 | } 51 | 52 | finalIntegrals.forEach((finalIntegral, index) => { 53 | let integralToEval = finalIntegral.buildFunction() 54 | pathCost += integralToEval(pathOriginalPoints[flightIndex][0][index+1]) - integralToEval(pathOriginalPoints[flightIndex][0][index]) 55 | }) 56 | 57 | flightsPathCost.push(pathCost) 58 | 59 | const progress = (100 * (flightIndex + 1) / pathAkimaXZCoefficients.length).toFixed(1) 60 | postMessage({progress}) 61 | } 62 | 63 | postMessage({flightsPathCost}) 64 | } 65 | 66 | function generatePathEquations(akimaCoefficients: ArrayLike[], pathOriginalPoints: any) : Array{ 67 | let splines = akimaCoefficients.length 68 | let splineEquations = new Array() 69 | let coeffs = new Array(4) 70 | for (let i = 0; i < splines; i++) { 71 | 72 | for (let j = 0; j < 4; j++) { 73 | if (akimaCoefficients[i][j]) { 74 | coeffs[j] = akimaCoefficients[i][j] 75 | } else { 76 | coeffs[j] = 0 77 | } 78 | } 79 | 80 | let expr = `${coeffs[0]} + ${coeffs[1]}*(x-${pathOriginalPoints[0][i]}) + ${coeffs[2]}*(x-${pathOriginalPoints[0][i]})^2 + ${coeffs[3]}*(x-${pathOriginalPoints[0][i]})^3` 81 | splineEquations.push(nerdamer(expr)) 82 | } 83 | 84 | return splineEquations 85 | } 86 | 87 | function generateFieldEquations(akimaCoefficients: ArrayLike[], pathOriginalPoints: any) : Array{ 88 | let splines = akimaCoefficients.length 89 | let splineEquations = new Array() 90 | let coeffs = new Array(4) 91 | 92 | for (let i = 0; i < splines; i++) { 93 | 94 | for (let j = 0; j < 4; j++) { 95 | if (akimaCoefficients[i][j]) { 96 | coeffs[j] = akimaCoefficients[i][j] 97 | } else { 98 | coeffs[j] = 0 99 | } 100 | } 101 | 102 | let expr = `${coeffs[0]} + ${coeffs[1]}*(x-${pathOriginalPoints[0][i]}) + ${coeffs[2]}*(x-${pathOriginalPoints[0][i]})^2 + ${coeffs[3]}*(x-${pathOriginalPoints[0][i]})^3` 103 | splineEquations.push(nerdamer(expr)) 104 | } 105 | 106 | return splineEquations 107 | } -------------------------------------------------------------------------------- /src/client/webgl2/logger.ts: -------------------------------------------------------------------------------- 1 | export class Logger { 2 | 3 | private static allowDebug = false 4 | 5 | public static log(msg: String) { 6 | if (this.allowDebug) { 7 | console.log(msg) 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/client/webgl2/mathUtils.ts: -------------------------------------------------------------------------------- 1 | // Original code: https://github.com/chdh/commons-math-interpolation 2 | 3 | // MIT License 4 | 5 | // Copyright 2017-2019 these people https://github.com/chdh/commons-math-interpolation/graphs/contributors 6 | // Copyright 2001-2019 The Apache Software Foundation 7 | 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | 14 | 15 | const EPSILON = Number.EPSILON 16 | type UniFunction = (x: number) => number 17 | 18 | export class Interpolator { 19 | static cachedCoefficients: ArrayLike[] 20 | // static cachedCoefficients: any //!: ArrayLike[] 21 | } 22 | 23 | // export let cachedCoefficients!: ArrayLike[] 24 | 25 | /** 26 | * Returns a function that computes a cubic spline interpolation for the data 27 | * set using the Akima algorithm, as originally formulated by Hiroshi Akima in 28 | * his 1970 paper "A New Method of Interpolation and Smooth Curve Fitting Based 29 | * on Local Procedures." 30 | * J. ACM 17, 4 (October 1970), 589-602. DOI=10.1145/321607.321609 31 | * http://doi.acm.org/10.1145/321607.321609 32 | * 33 | * This implementation is based on the Akima implementation in the CubicSpline 34 | * class in the Math.NET Numerics library. The method referenced is 35 | * CubicSpline.InterpolateAkimaSorted. 36 | * 37 | * Returns a polynomial spline function consisting of n cubic polynomials, 38 | * defined over the subintervals determined by the x values, 39 | * x[0] < x[1] < ... < x[n-1]. 40 | * The Akima algorithm requires that n >= 5. 41 | * 42 | * @param xVals 43 | * The arguments of the interpolation points, in strictly increasing order. 44 | * @param yVals 45 | * The values of the interpolation points. 46 | * @returns 47 | * A function which interpolates the dataset. 48 | */ 49 | export function createAkimaSplineInterpolator(xVals: ArrayLike, yVals: ArrayLike) : UniFunction { 50 | const segmentCoeffs = computeAkimaPolyCoefficients(xVals, yVals); 51 | Interpolator.cachedCoefficients = segmentCoeffs 52 | const xValsCopy = Float64Array.from(xVals); // clone to break dependency on passed values 53 | return (x: number) => evaluatePolySegment(xValsCopy, segmentCoeffs, x); 54 | } 55 | 56 | /** 57 | * Computes the polynomial coefficients for the Akima cubic spline 58 | * interpolation of a dataset. 59 | * 60 | * @param xVals 61 | * The arguments of the interpolation points, in strictly increasing order. 62 | * @param yVals 63 | * The values of the interpolation points. 64 | * @returns 65 | * Polynomial coefficients of the segments. 66 | */ 67 | export function computeAkimaPolyCoefficients(xVals: ArrayLike, yVals: ArrayLike) : Float64Array[] { 68 | if (xVals.length != yVals.length) { 69 | throw new Error("Dimension mismatch for xVals and yVals."); 70 | } 71 | if (xVals.length < 5) { 72 | throw new Error("Number of points is too small."); 73 | } 74 | checkStrictlyIncreasing(xVals); 75 | const n = xVals.length - 1; // number of segments 76 | 77 | const differences = new Float64Array(n); 78 | const weights = new Float64Array(n); 79 | 80 | for (let i = 0; i < n; i++) { 81 | differences[i] = (yVals[i + 1] - yVals[i]) / (xVals[i + 1] - xVals[i]); 82 | } 83 | 84 | for (let i = 1; i < n; i++) { 85 | weights[i] = Math.abs(differences[i] - differences[i - 1]); 86 | } 87 | 88 | // Prepare Hermite interpolation scheme. 89 | const firstDerivatives = new Float64Array(n + 1); 90 | 91 | for (let i = 2; i < n - 1; i++) { 92 | const wP = weights[i + 1]; 93 | const wM = weights[i - 1]; 94 | if (Math.abs(wP) < EPSILON && Math.abs(wM) < EPSILON) { 95 | const xv = xVals[i]; 96 | const xvP = xVals[i + 1]; 97 | const xvM = xVals[i - 1]; 98 | firstDerivatives[i] = (((xvP - xv) * differences[i - 1]) + ((xv - xvM) * differences[i])) / (xvP - xvM); 99 | } else { 100 | firstDerivatives[i] = ((wP * differences[i - 1]) + (wM * differences[i])) / (wP + wM); 101 | } 102 | } 103 | 104 | firstDerivatives[0] = differentiateThreePoint(xVals, yVals, 0, 0, 1, 2); 105 | firstDerivatives[1] = differentiateThreePoint(xVals, yVals, 1, 0, 1, 2); 106 | firstDerivatives[n - 1] = differentiateThreePoint(xVals, yVals, n - 1, n - 2, n - 1, n); 107 | firstDerivatives[n] = differentiateThreePoint(xVals, yVals, n , n - 2, n - 1, n); 108 | 109 | return computeHermitePolyCoefficients(xVals, yVals, firstDerivatives); 110 | } 111 | 112 | /** 113 | * Three point differentiation helper, modeled off of the same method in the 114 | * Math.NET CubicSpline class. 115 | * 116 | * @param xVals 117 | * x values to calculate the numerical derivative with. 118 | * @param yVals 119 | * y values to calculate the numerical derivative with. 120 | * @param indexOfDifferentiation 121 | * Index of the elemnt we are calculating the derivative around. 122 | * @param indexOfFirstSample 123 | * Index of the first element to sample for the three point method. 124 | * @param indexOfSecondsample 125 | * index of the second element to sample for the three point method. 126 | * @param indexOfThirdSample 127 | * Index of the third element to sample for the three point method. 128 | * @returns 129 | * The derivative. 130 | */ 131 | function differentiateThreePoint(xVals: ArrayLike, yVals: ArrayLike, 132 | indexOfDifferentiation: number, indexOfFirstSample: number, 133 | indexOfSecondsample: number, indexOfThirdSample: number) : number { 134 | 135 | const x0 = yVals[indexOfFirstSample]; 136 | const x1 = yVals[indexOfSecondsample]; 137 | const x2 = yVals[indexOfThirdSample]; 138 | 139 | const t = xVals[indexOfDifferentiation] - xVals[indexOfFirstSample]; 140 | const t1 = xVals[indexOfSecondsample] - xVals[indexOfFirstSample]; 141 | const t2 = xVals[indexOfThirdSample] - xVals[indexOfFirstSample]; 142 | 143 | const a = (x2 - x0 - (t2 / t1 * (x1 - x0))) / (t2 * t2 - t1 * t2); 144 | const b = (x1 - x0 - a * t1 * t1) / t1; 145 | 146 | return (2 * a * t) + b; 147 | } 148 | 149 | /** 150 | * Computes the polynomial coefficients for the Hermite cubic spline interpolation 151 | * for a set of (x,y) value pairs and their derivatives. This is modeled off of 152 | * the InterpolateHermiteSorted method in the Math.NET CubicSpline class. 153 | * 154 | * @param xVals 155 | * x values for interpolation. 156 | * @param yVals 157 | * y values for interpolation. 158 | * @param firstDerivatives 159 | * First derivative values of the function. 160 | * @returns 161 | * Polynomial coefficients of the segments. 162 | */ 163 | function computeHermitePolyCoefficients(xVals: ArrayLike, yVals: ArrayLike, firstDerivatives: ArrayLike) : Float64Array[] { 164 | if (xVals.length != yVals.length || xVals.length != firstDerivatives.length) { 165 | throw new Error("Dimension mismatch"); 166 | } 167 | if (xVals.length < 2) { 168 | throw new Error("Not enough points."); 169 | } 170 | const n = xVals.length - 1; // number of segments 171 | 172 | const segmentCoeffs : Float64Array[] = new Array(n); 173 | for (let i = 0; i < n; i++) { 174 | const w = xVals[i + 1] - xVals[i]; 175 | const w2 = w * w; 176 | 177 | const yv = yVals[i]; 178 | const yvP = yVals[i + 1]; 179 | 180 | const fd = firstDerivatives[i]; 181 | const fdP = firstDerivatives[i + 1]; 182 | 183 | const coeffs = new Float64Array(4); 184 | coeffs[0] = yv; 185 | coeffs[1] = firstDerivatives[i]; 186 | coeffs[2] = (3 * (yvP - yv) / w - 2 * fd - fdP) / w; 187 | coeffs[3] = (2 * (yv - yvP) / w + fd + fdP) / w2; 188 | segmentCoeffs[i] = trimPoly(coeffs); 189 | } 190 | return segmentCoeffs; 191 | } 192 | 193 | // Evaluates the polynomial of the segment corresponding to the specified x value. 194 | export function evaluatePolySegment(xVals: ArrayLike, segmentCoeffs: ArrayLike[], x: number) : number { 195 | let i = binarySearch(xVals, x); 196 | if (i < 0) { 197 | i = -i - 2; 198 | } 199 | i = Math.max(0, Math.min(i, segmentCoeffs.length - 1)); 200 | return evaluatePoly(segmentCoeffs[i], x - xVals[i]); 201 | } 202 | 203 | // Evaluates the value of a polynomial. 204 | // c contains the polynomial coefficients in ascending order. 205 | export function evaluatePoly(c: ArrayLike, x: number) : number { 206 | const n = c.length; 207 | if (n == 0) { 208 | return 0; 209 | } 210 | let v = c[n - 1]; 211 | for (let i = n - 2; i >= 0; i--) { 212 | v = x * v + c[i]; 213 | } 214 | return v; 215 | } 216 | 217 | // Trims top order polynomial coefficients which are zero. 218 | export function trimPoly(c: Float64Array) : Float64Array { 219 | let n = c.length; 220 | while (n > 1 && c[n - 1] == 0) { 221 | n--; 222 | } 223 | return (n == c.length) ? c : c.subarray(0, n); 224 | } 225 | 226 | // Checks that a number sequence is strictly increasing and all values are finite. 227 | export function checkStrictlyIncreasing(a: ArrayLike) { 228 | for (let i = 0; i < a.length; i++) { 229 | if (!isFinite(a[i])) { 230 | throw new Error("Non-finite number detected."); 231 | } 232 | if (i > 0 && a[i] <= a[i - 1]) { 233 | throw new Error("Number sequence is not strictly increasing."); 234 | } 235 | } 236 | } 237 | 238 | // Corresponds to java.util.Arrays.binarySearch(). 239 | // Returns the index of the search key, if it is contained in the array. 240 | // Otherwise it returns -(insertionPoint + 1). 241 | // The insertion point is defined as the point at which the key would be 242 | // inserted into the array: the index of the first element greater than 243 | // the key, or a.length if all elements in the array are less than the 244 | // specified key. 245 | export function binarySearch(a: ArrayLike, key: number) : number { 246 | let low = 0; 247 | let high = a.length - 1; 248 | while (low <= high) { 249 | const mid = (low + high) >>> 1; // tslint:disable-line:no-bitwise 250 | const midVal = a[mid]; 251 | if (midVal < key) { 252 | low = mid + 1; 253 | } else if (midVal > key) { 254 | high = mid - 1; 255 | } else if (midVal == key) { 256 | return mid; 257 | } else { // values might be NaN 258 | throw new Error("Invalid number encountered in binary search."); 259 | } 260 | } 261 | return -(low + 1); // key not found 262 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/arrowProgram.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils"; 2 | import { AttributeWrapper, Program } from "./program"; 3 | 4 | export class ArrowProgram extends Program { 5 | 6 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 7 | let vectorPositions = new Float32Array([ 8 | 0, 0, 9 | 0, 0.1, 10 | 0.2, 0, 11 | 0, 0.1, 12 | 0.2, 0.1, 13 | 0.2, 0, 14 | 0.2, 0.15, 15 | 0.3, 0.05, 16 | 0.2, -0.05 17 | ]) 18 | this.vertexCount = 9 19 | let positionAttribute = new AttributeWrapper("a_position", vectorPositions, 2, GLUtils.gl.FLOAT) 20 | this.setAttributes([positionAttribute]) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/flightProgram.ts: -------------------------------------------------------------------------------- 1 | import { FlightHelper } from "../flightHelper"; 2 | import { GLUtils, clamp } from "../glutils"; 3 | import { AttributeWrapper, Program } from "./program"; 4 | 5 | export class FlightProgram extends Program { 6 | 7 | pathPoints!: Float32Array 8 | flightIndex = 0 9 | numOfParts = 0 10 | 11 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 12 | if (this.pathPoints) { 13 | return 14 | } 15 | 16 | this.vertexCount = numOfParts + 1 17 | this.numOfParts = numOfParts 18 | 19 | this.pathPoints = new Float32Array(3 * (this.vertexCount)) 20 | const step = 2.0 / this.numOfParts 21 | for (let i = 0; i <= this.numOfParts; i++ ) { 22 | //bump is defined at <-1;1> 23 | let position = step * i - 1 24 | 25 | this.pathPoints[3*i + 0] = position 26 | this.pathPoints[3*i + 1] = FlightHelper.getFlightPathAt(this.flightIndex).flightYPathAt(position) 27 | this.pathPoints[3*i + 2] = FlightHelper.getFlightPathAt(this.flightIndex).flightZPathAt(position) 28 | 29 | this.pathPoints[3*i + 3] = position 30 | this.pathPoints[3*i + 4] = FlightHelper.getFlightPathAt(this.flightIndex).flightYPathAt(position) 31 | this.pathPoints[3*i + 5] = FlightHelper.getFlightPathAt(this.flightIndex).flightZPathAt(position) + 0.02 32 | } 33 | 34 | let positionAttribute = new AttributeWrapper("a_position", this.pathPoints, 3, GLUtils.gl.FLOAT) 35 | this.setAttributes([positionAttribute]) 36 | } 37 | 38 | setFlightIndexAndRegenerate(flightIndex: number) { 39 | this.flightIndex = flightIndex 40 | 41 | this.pathPoints = new Float32Array(3 * (this.vertexCount)) 42 | const step = 2.0 / this.numOfParts 43 | for (let i = 0; i <= this.numOfParts; i++ ) { 44 | //bump is defined at <-1;1> 45 | let position = step * i - 1 46 | 47 | this.pathPoints[3*i + 0] = position 48 | this.pathPoints[3*i + 1] = FlightHelper.getFlightPathAt(this.flightIndex).flightYPathAt(position) 49 | this.pathPoints[3*i + 2] = FlightHelper.getFlightPathAt(this.flightIndex).flightZPathAt(position) 50 | 51 | this.pathPoints[3*i + 3] = position 52 | this.pathPoints[3*i + 4] = FlightHelper.getFlightPathAt(this.flightIndex).flightYPathAt(position) 53 | this.pathPoints[3*i + 5] = FlightHelper.getFlightPathAt(this.flightIndex).flightZPathAt(position) + 0.02 54 | } 55 | 56 | let positionAttribute = new AttributeWrapper("a_position", this.pathPoints, 3, GLUtils.gl.FLOAT) 57 | this.updateAttribute(positionAttribute) 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/flowCalculationProgram.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils"; 2 | import { AttributeWrapper, Program } from "./program"; 3 | 4 | export class FlowCalculationProgram extends Program { 5 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 6 | this.flipVaos = true 7 | let unitSize = 3 8 | 9 | let flowDraw1AttributePosition = new AttributeWrapper("position", null, unitSize, GLUtils.gl.FLOAT, GLUtils.gl.STATIC_DRAW, false, customBuffers![1]) 10 | this.setAttributes([flowDraw1AttributePosition]) 11 | let flowDraw2AttributePosition = new AttributeWrapper("position", null, unitSize, GLUtils.gl.FLOAT, GLUtils.gl.STATIC_DRAW, false, customBuffers![0]) 12 | this.setAttributes([flowDraw2AttributePosition]) 13 | 14 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, null); 15 | GLUtils.gl.bindBuffer(GLUtils.gl.TRANSFORM_FEEDBACK_BUFFER, null); 16 | } 17 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/flowHistoryProgram.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils"; 2 | import { AttributeWrapper, Program } from "./program"; 3 | 4 | export class FlowHistoryProgram extends Program { 5 | 6 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 7 | let flowHistoryPositions = new Float32Array([ 8 | -1, -1, 9 | -1, 1, 10 | 1, -1, 11 | -1, 1, 12 | 1, 1, 13 | 1, -1, 14 | ]) 15 | 16 | let flowHistoryCoords = new Float32Array([ 17 | 0, 0, 18 | 0, 1, 19 | 1, 0, 20 | 0, 1, 21 | 1, 1, 22 | 1, 0, 23 | ]) 24 | 25 | this.vertexCount = 6 26 | let positionAttribute = new AttributeWrapper("a_position", flowHistoryPositions, 2, GLUtils.gl.FLOAT) 27 | let coordAttribute = new AttributeWrapper("a_texcoord", flowHistoryCoords, 2, GLUtils.gl.FLOAT) 28 | this.setAttributes([positionAttribute, coordAttribute]) 29 | } 30 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/flowUpdateProgram.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils"; 2 | import { AttributeWrapper, Program } from "./program"; 3 | 4 | export class UpdateFlowProgram extends Program { 5 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 6 | this.vertexCount = 6 7 | 8 | let unitSize = 3 9 | let particlesPositions = new Float32Array(numOfParts * unitSize) 10 | 11 | for (let i = 0; i < numOfParts; i++) { 12 | particlesPositions[unitSize*i] = Math.random()*2 - 1 13 | particlesPositions[unitSize*i + 1] = Math.random()*2 - 1 14 | particlesPositions[unitSize*i + 2] = 1 15 | } 16 | 17 | let flowAttributePosition = new AttributeWrapper("oldPosition", particlesPositions, unitSize, GLUtils.gl.FLOAT, GLUtils.gl.DYNAMIC_DRAW, true) 18 | 19 | this.setAttributes([flowAttributePosition]) 20 | this.setAttributes([flowAttributePosition]) 21 | this.flipVaos = true 22 | this.setTransform() 23 | } 24 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/program.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils" 2 | 3 | export abstract class Program { 4 | 5 | program: WebGLProgram 6 | primitiveType: number 7 | vertexCount = 0 8 | vaos: WebGLVertexArrayObject[] 9 | transformBuffers: WebGLTransformFeedback[] 10 | buffers: WebGLBuffer[] 11 | attributeBuffer = new Map() 12 | 13 | flipVaos = false 14 | flipIndex = 0 15 | prg = "" 16 | 17 | constructor(gl: WebGL2RenderingContext, sourceVertex: string, sourceFragment: string, transformFeedbackVaryings: [string] | null = null, primitiveType: number = GLUtils.gl.TRIANGLES) { 18 | let vertexShader = GLUtils.compileShader(GLUtils.gl.VERTEX_SHADER, sourceVertex)! 19 | let fragmentShader = GLUtils.compileShader(GLUtils.gl.FRAGMENT_SHADER, sourceFragment)! 20 | this.program = GLUtils.linkProgram(vertexShader, fragmentShader, transformFeedbackVaryings)! 21 | 22 | this.vaos = Array() 23 | this.transformBuffers = Array() 24 | this.buffers = Array() 25 | this.primitiveType = primitiveType 26 | } 27 | 28 | useProgram() { 29 | 30 | GLUtils.gl.useProgram(this.program) 31 | 32 | // ping pong used for transform buffers 33 | GLUtils.gl.bindVertexArray(this.vaos[this.flipIndex]) 34 | 35 | if (this.transformBuffers && this.transformBuffers.length > 1) { 36 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, this.transformBuffers[(this.flipIndex + 1) % 2]) 37 | } else if (this.transformBuffers) { 38 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, this.transformBuffers[0]) 39 | } 40 | 41 | if (this.flipVaos) { 42 | this.flipIndex = (this.flipIndex + 1) % 2 43 | } 44 | } 45 | 46 | bindCurrentVao() { 47 | GLUtils.gl.bindVertexArray(this.vaos[this.flipIndex]) 48 | } 49 | 50 | setAttributes(attributes: AttributeWrapper[]) { 51 | let vao = GLUtils.gl.createVertexArray() 52 | this.vaos.push(vao!) 53 | GLUtils.gl.bindVertexArray(vao) 54 | 55 | attributes.forEach(attribute => { 56 | let attributeLocation = GLUtils.gl.getAttribLocation(this.program, attribute.name) 57 | let buffer = null 58 | 59 | if (attribute.customBuffer) { 60 | buffer = attribute.customBuffer 61 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, buffer) 62 | } else { 63 | buffer = GLUtils.gl.createBuffer() 64 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, buffer) 65 | GLUtils.gl.bufferData(GLUtils.gl.ARRAY_BUFFER, attribute.data, attribute.bufferUsage) 66 | this.attributeBuffer.set(attribute.name, buffer!) 67 | } 68 | 69 | GLUtils.gl.enableVertexAttribArray(attributeLocation) 70 | 71 | // might be added as a param 72 | let normalize = false; 73 | let stride = 0; 74 | let offset = 0; 75 | GLUtils.gl.vertexAttribPointer(attributeLocation, attribute.size, attribute.type, normalize, stride, offset) 76 | 77 | if(attribute.transform) { 78 | this.buffers.push(buffer!) 79 | } 80 | 81 | }) 82 | } 83 | 84 | //Updates attribute's buffer if created before 85 | updateAttribute(attribute: AttributeWrapper) { 86 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, this.attributeBuffer.get(attribute.name)!) 87 | 88 | GLUtils.gl.bufferSubData(GLUtils.gl.ARRAY_BUFFER, 0, attribute.data!) 89 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, null); 90 | } 91 | 92 | // TODO : inspect index i, possible bug 93 | setTransform() { 94 | this.buffers.forEach(buffer => { 95 | const tf = GLUtils.gl.createTransformFeedback(); 96 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, tf); 97 | GLUtils.gl.bindBufferBase(GLUtils.gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer); 98 | this.transformBuffers.push(tf!) 99 | 100 | }); 101 | } 102 | 103 | setUniform(name: string, data: any, type: UNIFORM_TYPE) { 104 | let uniformLocation = GLUtils.gl.getUniformLocation(this.program, name); 105 | switch (type) { 106 | case UNIFORM_TYPE.f: 107 | GLUtils.gl.uniform1f(uniformLocation, data) 108 | break; 109 | case UNIFORM_TYPE.fv: 110 | GLUtils.gl.uniform2fv(uniformLocation, data) 111 | break; 112 | case UNIFORM_TYPE.iv: 113 | GLUtils.gl.uniform2iv(uniformLocation, data) 114 | break; 115 | case UNIFORM_TYPE.mat: 116 | GLUtils.gl.uniformMatrix4fv(uniformLocation, false, data) 117 | break; 118 | default: 119 | break; 120 | } 121 | } 122 | 123 | makeTransformFeedback() { 124 | 125 | } 126 | 127 | abstract generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null) : void 128 | 129 | draw() { 130 | let offset = 0 131 | GLUtils.gl.drawArrays(this.primitiveType, offset, this.vertexCount) 132 | } 133 | } 134 | 135 | export enum UNIFORM_TYPE { 136 | f, fv, mat, iv 137 | } 138 | 139 | export class AttributeWrapper { 140 | constructor(public name: string, public data: Float32Array | null, public size: number, public type: any, public bufferUsage: any = GLUtils.gl.STATIC_DRAW, public transform: boolean = false, public customBuffer: WebGLBuffer | null = null) { 141 | 142 | } 143 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/sampleSimplexProgram.ts: -------------------------------------------------------------------------------- 1 | import { FlightHelper } from "../flightHelper"; 2 | import { GLUtils } from "../glutils"; 3 | import { AttributeWrapper, Program } from "./program"; 4 | 5 | export class SampleSimplexProgram extends Program { 6 | 7 | flightIndex = 0 8 | numOfParts = 0 9 | 10 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 11 | this.vertexCount = 6 12 | this.numOfParts = numOfParts 13 | 14 | let particlesPositions = new Float32Array((numOfParts+1) * 3) 15 | let pathOriginalPoints = FlightHelper.getFlightPathAt(this.flightIndex).sampleFlightPath(numOfParts) 16 | for (let i = 0; i <= numOfParts; i++) { 17 | particlesPositions[3*i] = pathOriginalPoints[0][i] 18 | particlesPositions[3*i + 1] = pathOriginalPoints[1][i] 19 | //this is actually y 20 | particlesPositions[3*i + 2] = pathOriginalPoints[2][i] 21 | } 22 | 23 | let sampleSimplexAttributePosition = new AttributeWrapper("a_positionToSample", particlesPositions, 3, GLUtils.gl.FLOAT, GLUtils.gl.STATIC_DRAW, false) 24 | this.setAttributes([sampleSimplexAttributePosition]) 25 | 26 | const tf = GLUtils.gl.createTransformFeedback(); 27 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, tf); 28 | let buffer = GLUtils.gl.createBuffer() 29 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, buffer) 30 | GLUtils.gl.bufferData(GLUtils.gl.ARRAY_BUFFER, (numOfParts+1) * 3 * 4, GLUtils.gl.STATIC_DRAW) 31 | GLUtils.gl.bindBufferBase(GLUtils.gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer); 32 | GLUtils.gl.bindTransformFeedback(GLUtils.gl.TRANSFORM_FEEDBACK, null); 33 | GLUtils.gl.bindBuffer(GLUtils.gl.ARRAY_BUFFER, null) 34 | 35 | this.transformBuffers.push(tf!) 36 | this.buffers.push(buffer!) 37 | 38 | } 39 | 40 | setFlightIndexAndRegenerate(flightIndex: number) { 41 | this.flightIndex = flightIndex 42 | 43 | let particlesPositions = new Float32Array((this.numOfParts+1) * 3) 44 | let pathOriginalPoints = FlightHelper.getFlightPathAt(flightIndex).sampleFlightPath(this.numOfParts) 45 | for (let i = 0; i <= this.numOfParts; i++) { 46 | particlesPositions[3*i] = pathOriginalPoints[0][i] 47 | particlesPositions[3*i + 1] = pathOriginalPoints[1][i] 48 | //this is actually y 49 | particlesPositions[3*i + 2] = pathOriginalPoints[2][i] 50 | } 51 | 52 | let sampleSimplexAttributePosition = new AttributeWrapper("a_positionToSample", particlesPositions, 3, GLUtils.gl.FLOAT, GLUtils.gl.STATIC_DRAW, false) 53 | this.updateAttribute(sampleSimplexAttributePosition) 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/client/webgl2/program/simplexProgram.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "../glutils"; 2 | import { AttributeWrapper, Program } from "./program"; 3 | 4 | export class SimplexProgram extends Program { 5 | 6 | generateData(numOfParts: number, customBuffers: WebGLBuffer[] | null): void { 7 | let simplexPositions = new Float32Array([ 8 | -1, -1, 9 | -1, 1, 10 | 1, -1, 11 | -1, 1, 12 | 1, 1, 13 | 1, -1, 14 | ]) 15 | this.vertexCount = 6 16 | let positionAttribute = new AttributeWrapper("a_position", simplexPositions, 2, GLUtils.gl.FLOAT) 17 | this.setAttributes([positionAttribute]) 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/client/webgl2/scene.ts: -------------------------------------------------------------------------------- 1 | import { GLUtils } from "./glutils" 2 | import { WebGLGUI } from "./webglgui" 3 | import { CameraControls } from "./cameraControls" 4 | import { InterpolationHelper } from "./interpolationHelper" 5 | import { FlightHelper } from "./flightHelper" 6 | import { DrawObjectsWrapper, DrawParams } from "./drawObject/drawObject" 7 | 8 | export class Scene { 9 | 10 | fieldVisualizationDensity = 20.0 11 | drawObjectsWrapper!: DrawObjectsWrapper 12 | drawParams!: DrawParams 13 | 14 | interpolationHelper!: InterpolationHelper 15 | cameraControls: CameraControls 16 | particlesTextureSize = { 17 | x: 100, 18 | y: 100 19 | } 20 | 21 | simplexTextureSize = { 22 | x: 100, 23 | y: 100 24 | } 25 | 26 | webglgui!: WebGLGUI 27 | 28 | constructor() { 29 | FlightHelper.init() 30 | this.interpolationHelper = new InterpolationHelper(20) 31 | this.webglgui = new WebGLGUI(this.interpolationHelper) 32 | GLUtils.setupScene() 33 | this.cameraControls = new CameraControls(this.webglgui) 34 | } 35 | 36 | setupPrograms() { 37 | 38 | this.particlesTextureSize = { 39 | x: GLUtils.resolution.x, 40 | y: GLUtils.resolution.y 41 | } 42 | 43 | this.drawParams = new DrawParams(this.fieldVisualizationDensity) 44 | this.drawParams.particlesTextureSize = this.particlesTextureSize 45 | this.drawObjectsWrapper = new DrawObjectsWrapper(this.cameraControls, this.webglgui, this.drawParams, this.interpolationHelper) 46 | } 47 | 48 | draw() { 49 | this.cameraControls.calculateViewMatrix() 50 | this.drawObjectsWrapper.draw() 51 | 52 | this.drawParams.time += 0.01 53 | requestAnimationFrame(() => this.draw()) 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/client/webgl2/webglgui.ts: -------------------------------------------------------------------------------- 1 | import { GUI } from "dat.gui" 2 | import { clamp } from "./glutils" 3 | import { CameraFeedback } from "./cameraControls" 4 | import { InterpolationHelper } from "./interpolationHelper" 5 | 6 | export class WebGLGUI implements CameraFeedback { 7 | 8 | gui: GUI 9 | sceneSettingsFolder: GUI | undefined 10 | guiOptions: any 11 | 12 | simplexViewState = 0.0 13 | 14 | constructor(public interpolationHelper: InterpolationHelper) { 15 | this.gui = new GUI() 16 | 17 | this.guiOptions = { 18 | rotateX: 0, 19 | rotateY: 0, 20 | rotateZ: 0, 21 | moveX: 0, 22 | moveY: 0, 23 | moveZ: 1.5, 24 | layers: 1, 25 | hideFlow: false, 26 | hideVectors: false, 27 | zprojection: true, 28 | hideNoise: true, 29 | noiseOptions: "All", 30 | calculate: () => { 31 | this.interpolationHelper.calculateFieldWork() 32 | } 33 | } 34 | 35 | this.initDefaultGui() 36 | } 37 | 38 | initDefaultGui() { 39 | //General scene settings 40 | this.sceneSettingsFolder = this.gui.addFolder('Scene settings') 41 | this.sceneSettingsFolder.add(this.guiOptions, 'rotateX', 0, 80, 1).onChange(value => { 42 | 43 | }) 44 | this.sceneSettingsFolder.add(this.guiOptions, 'rotateY', -90, 90, 1).onChange(value => { 45 | 46 | }) 47 | this.sceneSettingsFolder.add(this.guiOptions, 'rotateZ', -180, 180, 1).onChange(value => { 48 | 49 | }) 50 | this.sceneSettingsFolder.add(this.guiOptions, 'moveX', -1, 1, 0.01).onChange(value => { 51 | 52 | }) 53 | this.sceneSettingsFolder.add(this.guiOptions, 'moveY', -1, 1, 0.01).onChange(value => { 54 | 55 | }) 56 | this.sceneSettingsFolder.add(this.guiOptions, 'moveZ', 0, 20, 0.01).onChange(value => { 57 | 58 | }) 59 | this.sceneSettingsFolder.add(this.guiOptions, 'layers', 1, 10, 1).onChange(value => { 60 | 61 | }) 62 | this.sceneSettingsFolder.add(this.guiOptions, 'hideFlow').onChange(value => { 63 | 64 | }) 65 | this.sceneSettingsFolder.add(this.guiOptions, 'hideVectors').onChange(value => { 66 | 67 | }) 68 | this.sceneSettingsFolder.add(this.guiOptions, 'hideNoise').onChange(value => { 69 | 70 | }) 71 | this.sceneSettingsFolder.add(this.guiOptions, 'zprojection').onChange(value => { 72 | 73 | }) 74 | this.sceneSettingsFolder.add(this.guiOptions, 'calculate').onChange(value => { 75 | 76 | }) 77 | 78 | this.sceneSettingsFolder.add(this.guiOptions, 'noiseOptions', [ 'All', 'XY angle', 'XZ angle', 'Abs Size' ] ).onChange(value => { 79 | switch (value) { 80 | case "All": 81 | this.simplexViewState = 0.0 82 | break; 83 | case "XY angle": 84 | this.simplexViewState = 1.0 85 | break; 86 | case "XZ angle": 87 | this.simplexViewState = 2.0 88 | break; 89 | case "Abs Size": 90 | this.simplexViewState = 3.0 91 | break; 92 | default: 93 | break; 94 | } 95 | }) 96 | this.sceneSettingsFolder.open() 97 | } 98 | 99 | shiftedBy(xShift: number, yShift: number): void { 100 | this.guiOptions.moveX += xShift 101 | this.guiOptions.moveY += yShift 102 | } 103 | zoomedBy(zoom: number): void { 104 | this.guiOptions.moveZ += zoom 105 | } 106 | 107 | //Updates of camera movement 108 | rotatedBy(xRotation: number, yRotation: number): void { 109 | this.guiOptions.rotateX += -yRotation 110 | this.guiOptions.rotateY += -xRotation 111 | 112 | this.guiOptions.rotateX = clamp(this.guiOptions.rotateX, 0, 90) 113 | this.guiOptions.rotateY = clamp(this.guiOptions.rotateY, -90, 90) 114 | } 115 | } -------------------------------------------------------------------------------- /src/client/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/client/client.ts', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | use: 'ts-loader', 10 | exclude: /node_modules/, 11 | }, 12 | { 13 | test: /\.(png|jpe?g)$/i, 14 | type: 'asset/resource' 15 | }, 16 | { 17 | test: /\.json/, 18 | type: 'asset/resource' 19 | } 20 | ], 21 | }, 22 | resolve: { 23 | alias: { 24 | three: path.resolve('./node_modules/three') 25 | }, 26 | extensions: ['.tsx', '.ts', '.js', '.json'], 27 | }, 28 | output: { 29 | filename: 'bundle.js', 30 | path: path.resolve(__dirname, '../../dist/client'), 31 | } 32 | }; 33 | 34 | // test: /\.json/, -------------------------------------------------------------------------------- /src/client/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge') 2 | const common = require('./webpack.common.js') 3 | const path = require('path'); 4 | 5 | module.exports = merge(common, { 6 | mode: 'development', 7 | devtool: 'eval-source-map', 8 | devServer: { 9 | static: { 10 | directory: path.join(__dirname, '../../dist/client'), 11 | }, 12 | hot: true, 13 | }, 14 | }) -------------------------------------------------------------------------------- /src/client/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | 4 | module.exports = merge(common, { 5 | mode: 'production', 6 | performance: { 7 | hints: false 8 | } 9 | }); -------------------------------------------------------------------------------- /src/client/worker.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import * as geometryUtils from "./geometryUtil" 3 | 4 | onmessage = (message) => { 5 | let geometry: THREE.BufferGeometry | undefined 6 | let imgMinZ: number = 100 7 | let imgMaxZ: number = -100 8 | 9 | const scenarioDataWrapper = new geometryUtils.ScenarioDataWrapper(message.data.imageData) 10 | 11 | switch(message.data.targetGeometry) { 12 | case 'Waifus': { 13 | geometry = geometryUtils.getActBufferGeometry(scenarioDataWrapper, message.data.stepsNumber) 14 | break 15 | } 16 | case 'Farm': { 17 | const generatedWrapper = geometryUtils.getFarmBufferGeometry(scenarioDataWrapper, message.data.centerPointToUpdate, message.data.targetGeometry) 18 | geometry = generatedWrapper.geometry 19 | imgMinZ = generatedWrapper.imgMinZ 20 | imgMaxZ = generatedWrapper.imgMaxZ 21 | break 22 | } 23 | case 'Koala': { 24 | geometry = geometryUtils.getKoalaBufferGeometry(scenarioDataWrapper, message.data.stepsNumber) 25 | break 26 | } 27 | case 'ai_images': { 28 | geometry = geometryUtils.getAIImagesBufferGeometry(scenarioDataWrapper, message.data.centerPointToUpdate, message.data.targetGeometry) 29 | break 30 | } 31 | } 32 | 33 | postMessage({geometry, centerPointToUpdate: message.data.centerPointToUpdate, imgMinZ, imgMaxZ}); 34 | } --------------------------------------------------------------------------------