├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .npmignore ├── .postcssrc.js ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── examples ├── App.vue ├── components │ └── GithubCorner.vue └── main.js ├── package-lock.json ├── package.json ├── preview.png ├── public ├── favicon.ico ├── index.html ├── js │ └── libs │ │ └── draco │ │ ├── README.md │ │ ├── draco_decoder.js │ │ ├── draco_decoder.wasm │ │ ├── draco_wasm_wrapper.js │ │ └── gltf │ │ ├── draco_decoder.js │ │ ├── draco_decoder.wasm │ │ ├── draco_encoder.js │ │ └── draco_wasm_wrapper.js ├── models │ └── gltf │ │ ├── BoomBox │ │ ├── README.md │ │ ├── glTF-Binary │ │ │ └── BoomBox.glb │ │ └── screenshot │ │ │ └── screenshot.jpg │ │ ├── CesiumMan │ │ ├── README.md │ │ ├── glTF │ │ │ ├── CesiumMan.gltf │ │ │ ├── CesiumMan.jpg │ │ │ └── CesiumMan0.bin │ │ └── screenshot │ │ │ └── screenshot.gif │ │ └── DamagedHelmet │ │ ├── README.md │ │ └── glTF │ │ ├── DamagedHelmet.bin │ │ ├── DamagedHelmet.gltf │ │ ├── Default_AO.jpg │ │ ├── Default_albedo.jpg │ │ ├── Default_emissive.jpg │ │ ├── Default_metalRoughness.jpg │ │ └── Default_normal.jpg └── textures │ └── cube │ ├── Bridge2 │ ├── negx.jpg │ ├── negy.jpg │ ├── negz.jpg │ ├── posx.jpg │ ├── posy.jpg │ ├── posz.jpg │ └── readme.txt │ ├── SwedishRoyalCastle │ ├── nx.jpg │ ├── ny.jpg │ ├── nz.jpg │ ├── px.jpg │ ├── py.jpg │ ├── pz.jpg │ └── readme.txt │ └── skyboxsun25deg │ ├── nx.jpg │ ├── ny.jpg │ ├── nz.jpg │ ├── px.jpg │ ├── py.jpg │ ├── pz.jpg │ └── skyboxsun25degtest.txt ├── rollup.config.js ├── src ├── components │ └── ModelViewer.vue ├── controls │ └── OrbitControls.js ├── index.js ├── loaders │ ├── DDSLoader.js │ ├── DRACOLoader.js │ ├── GLTFLoader.js │ ├── ModelLoader.js │ └── index.js ├── mixins │ └── base-mixin.vue └── utils │ ├── BufferGeometryUtils.js │ └── index.js └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/controls 2 | src/loaders 3 | node_modules/ 4 | dist/ 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ./public/models/** filter=lfs diff=lfs merge=lfs -text 2 | *.js eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | .commithash 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .travis.yml 3 | .editorconfig 4 | .eslintrc.js 5 | .gitattributes 6 | .commithash 7 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - node 6 | 7 | cache: 8 | directories: 9 | - node_modules 10 | 11 | install: 12 | - npm install 13 | 14 | script: 15 | - npm run ci 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aleksandr Tar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [WIP] vue-3dmodel-viewer 2 | 3 |

4 | Build 5 |

6 | 7 | ## Overview 8 | 9 | Vue.JS 3D Model Viewer component, based on THREE.js, inspired by [model-tag](https://github.com/mrdoob/model-tag) and [Sketchfab](https://sketchfab.com/models?features=downloadable&sort_by=-likeCount&type=models) 10 | 11 | ### [Demo](https://xpyct.github.io/vue-3dmodel-viewer/) 12 | 13 |

14 | 15 |

16 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/App.vue: -------------------------------------------------------------------------------- 1 | 32 | 120 | -------------------------------------------------------------------------------- /examples/components/GithubCorner.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 51 | -------------------------------------------------------------------------------- /examples/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App) 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-3dmodel-viewer", 3 | "version": "0.1.0", 4 | "description": "Three.js model viewer for Vue.JS", 5 | "keywords": [ 6 | "vue", 7 | "three.js", 8 | "component", 9 | "3d", 10 | "model", 11 | "viewer" 12 | ], 13 | "main": "dist/vue-3dmodel-viewer.js", 14 | "module": "dist/vue-3dmodel-viewer.esm.js", 15 | "jsnext:main": "dist/vue-3dmodel-viewer.esm.js", 16 | "unpkg": "dist/vue-3dmodel-viewer.min.js", 17 | "files": [ 18 | "src", 19 | "dist" 20 | ], 21 | "author": "Aleksandr Tar ", 22 | "homepage": "https://github.com/XpycT/vue-3dmodel-viewer", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/XpycT/vue-3dmodel-viewer" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/XpycT/vue-3dmodel-viewer/issues" 29 | }, 30 | "private": false, 31 | "license": "MIT", 32 | "scripts": { 33 | "serve": "npm run demo:serve", 34 | "demo:serve": "vue-cli-service serve examples/main.js", 35 | "demo:build": "vue-cli-service build examples/main.js ", 36 | "demo:deploy": "gh-pages -d dist", 37 | "prebuild": "shx rm -rf dist && npm run lint", 38 | "build": "git rev-parse HEAD > .commithash && rollup -c", 39 | "lint": "vue-cli-service lint", 40 | "ci": "npm run lint && npm run build" 41 | }, 42 | "pre-commit": [ 43 | "lint" 44 | ], 45 | "dependencies": { 46 | "dat.gui": "^0.7.2", 47 | "screenfull-es6": "^0.1.3", 48 | "stats.js": "^0.17.0", 49 | "three": "^0.125.0", 50 | "vue": "^2.5.16" 51 | }, 52 | "devDependencies": { 53 | "@vue/cli-plugin-babel": "^3.0.0-beta.15", 54 | "@vue/cli-plugin-eslint": "^3.0.0-beta.15", 55 | "@vue/cli-service": "^3.0.0-beta.15", 56 | "autoprefixer": "latest", 57 | "chalk": "latest", 58 | "gh-pages": "^1.2.0", 59 | "rollup": "^0.62.0", 60 | "rollup-plugin-babel": "^3.0.7", 61 | "rollup-plugin-buble": "^0.19.2", 62 | "rollup-plugin-commonjs": "^9.1.3", 63 | "rollup-plugin-json": "^3.0.0", 64 | "rollup-plugin-livereload": "^0.6.0", 65 | "rollup-plugin-node-resolve": "^3.3.0", 66 | "rollup-plugin-postcss": "^1.6.2", 67 | "rollup-plugin-replace": "^2.0.0", 68 | "rollup-plugin-uglify": "^4.0.0", 69 | "rollup-plugin-vue": "^4.3.1", 70 | "shx": "^0.3.2", 71 | "vue-template-compiler": "^2.5.16" 72 | }, 73 | "engines": { 74 | "node": ">= 6.0.0", 75 | "npm": ">= 3.0.0" 76 | }, 77 | "browserslist": [ 78 | "> 1%", 79 | "last 2 versions", 80 | "not ie <= 8" 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XpycT/vue-3dmodel-viewer/3e97bdfcc27063732285f59d307a03b243ac1548/preview.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XpycT/vue-3dmodel-viewer/3e97bdfcc27063732285f59d307a03b243ac1548/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-3dmodel-viewer 9 | 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/js/libs/draco/README.md: -------------------------------------------------------------------------------- 1 | # Draco 3D Data Compression 2 | 3 | Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. 4 | 5 | [Website](https://google.github.io/draco/) | [GitHub](https://github.com/google/draco) 6 | 7 | ## Contents 8 | 9 | This folder contains three utilities: 10 | 11 | * `draco_decoder.js` — Emscripten-compiled decoder, compatible with any modern browser. 12 | * `draco_decoder.wasm` — WebAssembly decoder, compatible with newer browsers and devices. 13 | * `draco_wasm_wrapper.js` — JavaScript wrapper for the WASM decoder. 14 | 15 | Each file is provided in two variations: 16 | 17 | * **Default:** Latest stable builds, tracking the project's [master branch](https://github.com/google/draco). 18 | * **glTF:** Builds targeted by the [glTF mesh compression extension](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression), tracking the [corresponding Draco branch](https://github.com/google/draco/tree/gltf_2.0_draco_extension). 19 | 20 | Either variation may be used with `THREE.DRACOLoader`: 21 | 22 | ```js 23 | THREE.DRACOLoader.setDecoderPath('path/to/decoders/'); 24 | THREE.DRACOLoader.setDecoderConfig({type: 'js'}); // (Optional) Override detection of WASM support. 25 | var dracoLoader = new THREE.DRACOLoader(); 26 | ``` 27 | 28 | Further [documentation on GitHub](https://github.com/google/draco/tree/master/javascript/example#static-loading-javascript-decoder). 29 | 30 | ## License 31 | 32 | [Apache License 2.0](https://github.com/google/draco/blob/master/LICENSE) 33 | -------------------------------------------------------------------------------- /public/js/libs/draco/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XpycT/vue-3dmodel-viewer/3e97bdfcc27063732285f59d307a03b243ac1548/public/js/libs/draco/draco_decoder.wasm -------------------------------------------------------------------------------- /public/js/libs/draco/gltf/draco_decoder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XpycT/vue-3dmodel-viewer/3e97bdfcc27063732285f59d307a03b243ac1548/public/js/libs/draco/gltf/draco_decoder.wasm -------------------------------------------------------------------------------- /public/js/libs/draco/gltf/draco_wasm_wrapper.js: -------------------------------------------------------------------------------- 1 | var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(d,k,f){d!=Array.prototype&&d!=Object.prototype&&(d[k]=f.value)};$jscomp.getGlobal=function(d){return"undefined"!=typeof window&&window===d?d:"undefined"!=typeof global&&null!=global?global:d};$jscomp.global=$jscomp.getGlobal(this); 2 | $jscomp.polyfill=function(d,k,f,u){if(k){f=$jscomp.global;d=d.split(".");for(u=0;u>>16&65535)*h+k*(f>>>16&65535)<<16>>>0)|0}},"es6","es3"); 3 | $jscomp.polyfill("Math.clz32",function(d){return d?d:function(d){d=Number(d)>>>0;if(0===d)return 32;var f=0;0===(d&4294901760)&&(d<<=16,f+=16);0===(d&4278190080)&&(d<<=8,f+=8);0===(d&4026531840)&&(d<<=4,f+=4);0===(d&3221225472)&&(d<<=2,f+=2);0===(d&2147483648)&&f++;return f}},"es6","es3");$jscomp.polyfill("Math.trunc",function(d){return d?d:function(d){d=Number(d);if(isNaN(d)||Infinity===d||-Infinity===d||0===d)return d;var f=Math.floor(Math.abs(d));return 0>d?-f:f}},"es6","es3"); 4 | $jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var d=0;return function(k){return $jscomp.SYMBOL_PREFIX+(k||"")+d++}}(); 5 | $jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var d=$jscomp.global.Symbol.iterator;d||(d=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[d]&&$jscomp.defineProperty(Array.prototype,d,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(d){var k=0;return $jscomp.iteratorPrototype(function(){return k>0];b|=e;if(0==e&&!c)break;d++;if(c&&d==c)break}c||(c=d);e="";if(128>b){for(;0e?b+=String.fromCharCode(e):(e-=65536,b+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else b+=String.fromCharCode(e)}}function ha(a,c){0< 16 | a%c&&(a+=c-a%c);return a}function r(){a.HEAP8=ia=new Int8Array(D);a.HEAP16=Ja=new Int16Array(D);a.HEAP32=E=new Int32Array(D);a.HEAPU8=W=new Uint8Array(D);a.HEAPU16=new Uint16Array(D);a.HEAPU32=new Uint32Array(D);a.HEAPF32=new Float32Array(D);a.HEAPF64=new Float64Array(D)}function B(e){for(;0>2]=e;e=la.buffer;for(var d=0;d>2],c.adjusted=e,(sa(p[d]),e)|0;e=E[e>>2]; 18 | return(sa(b),e)|0}function Z(e,c){w.varargs=c;try{var b=w.get(),p=w.get(),d=w.get();e=0;Z.buffers||(Z.buffers=[null,[],[]],Z.printChar=function(c,b){var e=Z.buffers[c];f(e);0===b||10===b?((1===c?a.print:a.printErr)(h(e,0)),e.length=0):e.push(b)});for(c=0;c>2],k=E[p+(8*c+4)>>2],l=0;l=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++b)&1023);127>=e?++c:c=2047>=e?c+2:65535>=e?c+3:2097151>=e?c+4:67108863>=e?c+5:c+6}c=Array(c+1);b=0;e=c.length;if(0=f&&(f=65536+((f&1023)<<10)|a.charCodeAt(++d)&1023);if(127>=f){if(b>=e)break;c[b++]=f}else{if(2047>=f){if(b+1>=e)break;c[b++]=192|f>>6}else{if(65535>=f){if(b+2>=e)break;c[b++]=224|f>>12}else{if(2097151>=f){if(b+ 22 | 3>=e)break;c[b++]=240|f>>18}else{if(67108863>=f){if(b+4>=e)break;c[b++]=248|f>>24}else{if(b+5>=e)break;c[b++]=252|f>>30;c[b++]=128|f>>24&63}c[b++]=128|f>>18&63}c[b++]=128|f>>12&63}c[b++]=128|f>>6&63}c[b++]=128|f&63}}c[b]=0}a=l.alloc(c,ia);l.copy(c,ia,a)}return a}function z(){throw"cannot construct a Status, no constructor in IDL";}function F(){this.ptr=Wa();t(F)[this.ptr]=this}function G(){this.ptr=Xa();t(G)[this.ptr]=this}function H(){this.ptr=Ya();t(H)[this.ptr]=this}function I(){this.ptr=Za(); 23 | t(I)[this.ptr]=this}function J(){this.ptr=$a();t(J)[this.ptr]=this}function n(){this.ptr=ab();t(n)[this.ptr]=this}function P(){this.ptr=bb();t(P)[this.ptr]=this}function x(){this.ptr=cb();t(x)[this.ptr]=this}function K(){this.ptr=db();t(K)[this.ptr]=this}function q(){this.ptr=eb();t(q)[this.ptr]=this}function L(){this.ptr=fb();t(L)[this.ptr]=this}function M(){this.ptr=gb();t(M)[this.ptr]=this}function V(){this.ptr=hb();t(V)[this.ptr]=this}function Q(){this.ptr=ib();t(Q)[this.ptr]=this}function g(){this.ptr= 24 | jb();t(g)[this.ptr]=this}function C(){this.ptr=kb();t(C)[this.ptr]=this}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function N(){this.ptr=lb();t(N)[this.ptr]=this}function R(){this.ptr=mb();t(R)[this.ptr]=this}d=d||{};var a="undefined"!==typeof d?d:{},Qa=!1,Ra=!1;a.onRuntimeInitialized=function(){Qa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Ra=!0;if(Qa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported= 25 | function(a){if("string"!==typeof a)return!1;a=a.split(".");return 2>a.length||3=a[1]?!0:0!=a[0]||10>2]},getStr:function(){return u(w.get())},get64:function(){var a=w.get(),c=w.get();0<=a?f(0===c):f(-1===c);return a},getZero:function(){f(0===w.get())}},va={},Ha=1;ka=function(a){f(!Sa);var c=ba;ba=ba+ 40 | a+15&-16;return c}(4);Ca=ta=k(ba);ua=Ca+Fa;Da=k(ua);E[ka>>2]=Da;Sa=!0;a.wasmTableSize=468;a.wasmMaxTableSize=468;a.asmGlobalArg={};a.asmLibraryArg={abort:O,assert:f,enlargeMemory:function(){var e=a.usingWasm?65536:16777216,c=2147483648-e;if(E[ka>>2]>c)return!1;var b=A;for(A=Math.max(A,16777216);A>2];)A=536870912>=A?ha(2*A,e):Math.min(ha((3*A+2147483648)/4,e),c);e=a.reallocBuffer(A);if(!e||e.byteLength!=A)return A=b,!1;a.buffer=D=e;r();return!0},getTotalMemory:function(){return A},abortOnCannotGrowMemory:function(){O("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+ 41 | A+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")},invoke_ii:function(e,c){try{return a.dynCall_ii(e,c)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;a.setThrew(1,0)}},invoke_iii:function(e,c,b){try{return a.dynCall_iii(e,c,b)}catch(p){if("number"!==typeof p&&"longjmp"!==p)throw p;a.setThrew(1,0)}},invoke_iiii:function(e,c,b,d){try{return a.dynCall_iiii(e, 42 | c,b,d)}catch(S){if("number"!==typeof S&&"longjmp"!==S)throw S;a.setThrew(1,0)}},invoke_iiiiiii:function(e,c,b,d,f,g,h){try{return a.dynCall_iiiiiii(e,c,b,d,f,g,h)}catch(da){if("number"!==typeof da&&"longjmp"!==da)throw da;a.setThrew(1,0)}},invoke_v:function(e){try{a.dynCall_v(e)}catch(c){if("number"!==typeof c&&"longjmp"!==c)throw c;a.setThrew(1,0)}},invoke_vi:function(e,c){try{a.dynCall_vi(e,c)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;a.setThrew(1,0)}},invoke_vii:function(e,c,b){try{a.dynCall_vii(e, 43 | c,b)}catch(p){if("number"!==typeof p&&"longjmp"!==p)throw p;a.setThrew(1,0)}},invoke_viii:function(e,c,b,d){try{a.dynCall_viii(e,c,b,d)}catch(S){if("number"!==typeof S&&"longjmp"!==S)throw S;a.setThrew(1,0)}},invoke_viiii:function(e,c,b,d,f){try{a.dynCall_viiii(e,c,b,d,f)}catch(xa){if("number"!==typeof xa&&"longjmp"!==xa)throw xa;a.setThrew(1,0)}},invoke_viiiii:function(e,c,b,d,f,g){try{a.dynCall_viiiii(e,c,b,d,f,g)}catch(ca){if("number"!==typeof ca&&"longjmp"!==ca)throw ca;a.setThrew(1,0)}},invoke_viiiiii:function(e, 44 | c,b,d,f,g,h){try{a.dynCall_viiiiii(e,c,b,d,f,g,h)}catch(da){if("number"!==typeof da&&"longjmp"!==da)throw da;a.setThrew(1,0)}},__ZSt18uncaught_exceptionv:v,___cxa_allocate_exception:function(a){return Ka(a)},___cxa_begin_catch:function(a){var c=y.infos[a];c&&!c.caught&&(c.caught=!0,v.uncaught_exception--);c&&(c.rethrown=!1);y.caught.push(a);y.addRef(y.deAdjust(a));return a},___cxa_find_matching_catch:la,___cxa_pure_virtual:function(){oa=!0;throw"Pure virtual function called!";},___cxa_throw:function(a, 45 | c,b){y.infos[a]={ptr:a,adjusted:a,type:c,destructor:b,refcount:0,caught:!1,rethrown:!1};y.last=a;"uncaught_exception"in v?v.uncaught_exception++:v.uncaught_exception=1;throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";},___gxx_personality_v0:function(){},___resumeException:function(a){y.last||(y.last=a);throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."; 46 | },___setErrNo:function(d){a.___errno_location&&(E[a.___errno_location()>>2]=d);return d},___syscall140:function(a,c){w.varargs=c;try{var b=w.getStreamFromFD();w.get();var d=w.get(),e=w.get(),f=w.get();FS.llseek(b,d,f);E[e>>2]=b.position;b.getdents&&0===d&&0===f&&(b.getdents=null);return 0}catch(ca){return"undefined"!==typeof FS&&ca instanceof FS.ErrnoError||O(ca),-ca.errno}},___syscall146:Z,___syscall6:function(a,c){w.varargs=c;try{var b=w.getStreamFromFD();FS.close(b);return 0}catch(p){return"undefined"!== 47 | typeof FS&&p instanceof FS.ErrnoError||O(p),-p.errno}},_abort:function(){a.abort()},_emscripten_memcpy_big:function(a,c,b){W.set(W.subarray(c,c+b),a);return a},_llvm_trap:function(){O("trap!")},_pthread_getspecific:function(a){return va[a]||0},_pthread_key_create:function(a,c){if(0==a)return 22;E[a>>2]=Ha;va[Ha]=0;Ha++;return 0},_pthread_once:ma,_pthread_setspecific:function(a,c){if(!(a in va))return 22;va[a]=c;return 0},flush_NO_FILESYSTEM:function(){var d=a._fflush;d&&d(0);if(d=Z.printChar){var c= 48 | Z.buffers;c[1].length&&d(1,10);c[2].length&&d(2,10)}},DYNAMICTOP_PTR:ka,tempDoublePtr:qb,ABORT:oa,STACKTOP:ta,STACK_MAX:ua};var Ua=a.asm(a.asmGlobalArg,a.asmLibraryArg,D);a.asm=Ua;a.___cxa_can_catch=function(){return a.asm.___cxa_can_catch.apply(null,arguments)};a.___cxa_is_pointer_type=function(){return a.asm.___cxa_is_pointer_type.apply(null,arguments)};var $a=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0.apply(null, 49 | arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1.apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform___destroy___0.apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0.apply(null, 50 | arguments)},cb=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0.apply(null,arguments)},ub=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1.apply(null,arguments)},vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform___destroy___0.apply(null, 51 | arguments)},wb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_min_value_1.apply(null,arguments)},xb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0.apply(null,arguments)},yb=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_range_0.apply(null, 52 | arguments)},bb=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return a.asm._emscripten_bind_AttributeTransformData_AttributeTransformData_0.apply(null,arguments)},zb=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return a.asm._emscripten_bind_AttributeTransformData___destroy___0.apply(null,arguments)},Ab=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return a.asm._emscripten_bind_AttributeTransformData_transform_type_0.apply(null, 53 | arguments)},ib=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return a.asm._emscripten_bind_DecoderBuffer_DecoderBuffer_0.apply(null,arguments)},Bb=a._emscripten_bind_DecoderBuffer_Init_2=function(){return a.asm._emscripten_bind_DecoderBuffer_Init_2.apply(null,arguments)},Cb=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return a.asm._emscripten_bind_DecoderBuffer___destroy___0.apply(null,arguments)},Db=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToMesh_2.apply(null, 54 | arguments)},Eb=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToPointCloud_2.apply(null,arguments)},jb=a._emscripten_bind_Decoder_Decoder_0=function(){return a.asm._emscripten_bind_Decoder_Decoder_0.apply(null,arguments)},Fb=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeByUniqueId_2.apply(null,arguments)},Gb=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3= 55 | function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3.apply(null,arguments)},Hb=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloat_3.apply(null,arguments)},Ib=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3.apply(null,arguments)},Jb=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByName_2.apply(null, 56 | arguments)},Kb=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeId_2.apply(null,arguments)},Lb=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3.apply(null,arguments)},Mb=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3.apply(null,arguments)},Nb=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3= 57 | function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3.apply(null,arguments)},Ob=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3.apply(null,arguments)},Pb=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeMetadata_2.apply(null,arguments)},Qb=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3.apply(null, 58 | arguments)},Rb=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3.apply(null,arguments)},Sb=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3.apply(null,arguments)},Tb=a._emscripten_bind_Decoder_GetAttribute_2=function(){return a.asm._emscripten_bind_Decoder_GetAttribute_2.apply(null,arguments)},Ub=a._emscripten_bind_Decoder_GetEncodedGeometryType_1= 59 | function(){return a.asm._emscripten_bind_Decoder_GetEncodedGeometryType_1.apply(null,arguments)},Vb=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return a.asm._emscripten_bind_Decoder_GetFaceFromMesh_3.apply(null,arguments)},Wb=a._emscripten_bind_Decoder_GetMetadata_1=function(){return a.asm._emscripten_bind_Decoder_GetMetadata_1.apply(null,arguments)},Xb=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return a.asm._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2.apply(null, 60 | arguments)},Yb=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return a.asm._emscripten_bind_Decoder_SkipAttributeTransform_1.apply(null,arguments)},Zb=a._emscripten_bind_Decoder___destroy___0=function(){return a.asm._emscripten_bind_Decoder___destroy___0.apply(null,arguments)},gb=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0.apply(null,arguments)},$b=a._emscripten_bind_DracoFloat32Array_GetValue_1= 61 | function(){return a.asm._emscripten_bind_DracoFloat32Array_GetValue_1.apply(null,arguments)},ac=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoFloat32Array___destroy___0.apply(null,arguments)},bc=a._emscripten_bind_DracoFloat32Array_size_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_size_0.apply(null,arguments)},fb=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return a.asm._emscripten_bind_DracoInt16Array_DracoInt16Array_0.apply(null, 62 | arguments)},cc=a._emscripten_bind_DracoInt16Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt16Array_GetValue_1.apply(null,arguments)},dc=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt16Array___destroy___0.apply(null,arguments)},ec=a._emscripten_bind_DracoInt16Array_size_0=function(){return a.asm._emscripten_bind_DracoInt16Array_size_0.apply(null,arguments)},lb=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return a.asm._emscripten_bind_DracoInt32Array_DracoInt32Array_0.apply(null, 63 | arguments)},fc=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt32Array_GetValue_1.apply(null,arguments)},gc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt32Array___destroy___0.apply(null,arguments)},hc=a._emscripten_bind_DracoInt32Array_size_0=function(){return a.asm._emscripten_bind_DracoInt32Array_size_0.apply(null,arguments)},db=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return a.asm._emscripten_bind_DracoInt8Array_DracoInt8Array_0.apply(null, 64 | arguments)},ic=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt8Array_GetValue_1.apply(null,arguments)},jc=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt8Array___destroy___0.apply(null,arguments)},kc=a._emscripten_bind_DracoInt8Array_size_0=function(){return a.asm._emscripten_bind_DracoInt8Array_size_0.apply(null,arguments)},Wa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return a.asm._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0.apply(null, 65 | arguments)},lc=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt16Array_GetValue_1.apply(null,arguments)},mc=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt16Array___destroy___0.apply(null,arguments)},nc=a._emscripten_bind_DracoUInt16Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt16Array_size_0.apply(null,arguments)},Za=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return a.asm._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0.apply(null, 66 | arguments)},oc=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt32Array_GetValue_1.apply(null,arguments)},pc=a._emscripten_bind_DracoUInt32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt32Array___destroy___0.apply(null,arguments)},qc=a._emscripten_bind_DracoUInt32Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt32Array_size_0.apply(null,arguments)},Ya=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return a.asm._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0.apply(null, 67 | arguments)},rc=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt8Array_GetValue_1.apply(null,arguments)},sc=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt8Array___destroy___0.apply(null,arguments)},tc=a._emscripten_bind_DracoUInt8Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt8Array_size_0.apply(null,arguments)},hb=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return a.asm._emscripten_bind_GeometryAttribute_GeometryAttribute_0.apply(null, 68 | arguments)},uc=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return a.asm._emscripten_bind_GeometryAttribute___destroy___0.apply(null,arguments)},kb=a._emscripten_bind_Mesh_Mesh_0=function(){return a.asm._emscripten_bind_Mesh_Mesh_0.apply(null,arguments)},vc=a._emscripten_bind_Mesh___destroy___0=function(){return a.asm._emscripten_bind_Mesh___destroy___0.apply(null,arguments)},wc=a._emscripten_bind_Mesh_num_attributes_0=function(){return a.asm._emscripten_bind_Mesh_num_attributes_0.apply(null, 69 | arguments)},xc=a._emscripten_bind_Mesh_num_faces_0=function(){return a.asm._emscripten_bind_Mesh_num_faces_0.apply(null,arguments)},yc=a._emscripten_bind_Mesh_num_points_0=function(){return a.asm._emscripten_bind_Mesh_num_points_0.apply(null,arguments)},zc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetDoubleEntry_2.apply(null,arguments)},Ac=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetEntryName_2.apply(null, 70 | arguments)},Bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetIntEntry_2.apply(null,arguments)},Cc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetStringEntry_2.apply(null,arguments)},Dc=a._emscripten_bind_MetadataQuerier_HasDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasDoubleEntry_2.apply(null,arguments)},Ec=a._emscripten_bind_MetadataQuerier_HasEntry_2= 71 | function(){return a.asm._emscripten_bind_MetadataQuerier_HasEntry_2.apply(null,arguments)},Fc=a._emscripten_bind_MetadataQuerier_HasIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasIntEntry_2.apply(null,arguments)},Gc=a._emscripten_bind_MetadataQuerier_HasStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasStringEntry_2.apply(null,arguments)},eb=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return a.asm._emscripten_bind_MetadataQuerier_MetadataQuerier_0.apply(null, 72 | arguments)},Hc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return a.asm._emscripten_bind_MetadataQuerier_NumEntries_1.apply(null,arguments)},Ic=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return a.asm._emscripten_bind_MetadataQuerier___destroy___0.apply(null,arguments)},mb=a._emscripten_bind_Metadata_Metadata_0=function(){return a.asm._emscripten_bind_Metadata_Metadata_0.apply(null,arguments)},Jc=a._emscripten_bind_Metadata___destroy___0=function(){return a.asm._emscripten_bind_Metadata___destroy___0.apply(null, 73 | arguments)},Kc=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return a.asm._emscripten_bind_PointAttribute_GetAttributeTransformData_0.apply(null,arguments)},ab=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return a.asm._emscripten_bind_PointAttribute_PointAttribute_0.apply(null,arguments)},Lc=a._emscripten_bind_PointAttribute___destroy___0=function(){return a.asm._emscripten_bind_PointAttribute___destroy___0.apply(null,arguments)},Mc=a._emscripten_bind_PointAttribute_attribute_type_0= 74 | function(){return a.asm._emscripten_bind_PointAttribute_attribute_type_0.apply(null,arguments)},Nc=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_offset_0.apply(null,arguments)},Oc=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_stride_0.apply(null,arguments)},Pc=a._emscripten_bind_PointAttribute_data_type_0=function(){return a.asm._emscripten_bind_PointAttribute_data_type_0.apply(null, 75 | arguments)},Qc=a._emscripten_bind_PointAttribute_normalized_0=function(){return a.asm._emscripten_bind_PointAttribute_normalized_0.apply(null,arguments)},Rc=a._emscripten_bind_PointAttribute_num_components_0=function(){return a.asm._emscripten_bind_PointAttribute_num_components_0.apply(null,arguments)},Sc=a._emscripten_bind_PointAttribute_size_0=function(){return a.asm._emscripten_bind_PointAttribute_size_0.apply(null,arguments)},Tc=a._emscripten_bind_PointAttribute_unique_id_0=function(){return a.asm._emscripten_bind_PointAttribute_unique_id_0.apply(null, 76 | arguments)},Xa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return a.asm._emscripten_bind_PointCloud_PointCloud_0.apply(null,arguments)},Uc=a._emscripten_bind_PointCloud___destroy___0=function(){return a.asm._emscripten_bind_PointCloud___destroy___0.apply(null,arguments)},Vc=a._emscripten_bind_PointCloud_num_attributes_0=function(){return a.asm._emscripten_bind_PointCloud_num_attributes_0.apply(null,arguments)},Wc=a._emscripten_bind_PointCloud_num_points_0=function(){return a.asm._emscripten_bind_PointCloud_num_points_0.apply(null, 77 | arguments)},Xc=a._emscripten_bind_Status___destroy___0=function(){return a.asm._emscripten_bind_Status___destroy___0.apply(null,arguments)},Yc=a._emscripten_bind_Status_code_0=function(){return a.asm._emscripten_bind_Status_code_0.apply(null,arguments)},Zc=a._emscripten_bind_Status_error_msg_0=function(){return a.asm._emscripten_bind_Status_error_msg_0.apply(null,arguments)},$c=a._emscripten_bind_Status_ok_0=function(){return a.asm._emscripten_bind_Status_ok_0.apply(null,arguments)},ad=a._emscripten_bind_VoidPtr___destroy___0= 78 | function(){return a.asm._emscripten_bind_VoidPtr___destroy___0.apply(null,arguments)},bd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM.apply(null,arguments)},cd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM.apply(null,arguments)},dd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM= 79 | function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM.apply(null,arguments)},ed=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM.apply(null,arguments)},fd=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE.apply(null, 80 | arguments)},gd=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD.apply(null,arguments)},hd=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH.apply(null,arguments)},id=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_COLOR.apply(null,arguments)},jd= 81 | a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_GENERIC.apply(null,arguments)},kd=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_INVALID.apply(null,arguments)},ld=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_NORMAL.apply(null,arguments)},md=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION= 82 | function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_POSITION.apply(null,arguments)},nd=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD.apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_ERROR.apply(null,arguments)},pd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return a.asm._emscripten_enum_draco_StatusCode_INVALID_PARAMETER.apply(null, 83 | arguments)},qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_IO_ERROR.apply(null,arguments)},rd=a._emscripten_enum_draco_StatusCode_OK=function(){return a.asm._emscripten_enum_draco_StatusCode_OK.apply(null,arguments)},sd=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION.apply(null,arguments)},td=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION.apply(null, 84 | arguments)},nb=a._emscripten_replace_memory=function(){return a.asm._emscripten_replace_memory.apply(null,arguments)};a._free=function(){return a.asm._free.apply(null,arguments)};a._llvm_bswap_i32=function(){return a.asm._llvm_bswap_i32.apply(null,arguments)};var Ka=a._malloc=function(){return a.asm._malloc.apply(null,arguments)};a._memcpy=function(){return a.asm._memcpy.apply(null,arguments)};a._memmove=function(){return a.asm._memmove.apply(null,arguments)};a._memset=function(){return a.asm._memset.apply(null, 85 | arguments)};a._sbrk=function(){return a.asm._sbrk.apply(null,arguments)};a.establishStackSpace=function(){return a.asm.establishStackSpace.apply(null,arguments)};a.getTempRet0=function(){return a.asm.getTempRet0.apply(null,arguments)};a.runPostSets=function(){return a.asm.runPostSets.apply(null,arguments)};var sa=a.setTempRet0=function(){return a.asm.setTempRet0.apply(null,arguments)};a.setThrew=function(){return a.asm.setThrew.apply(null,arguments)};a.stackAlloc=function(){return a.asm.stackAlloc.apply(null, 86 | arguments)};a.stackRestore=function(){return a.asm.stackRestore.apply(null,arguments)};a.stackSave=function(){return a.asm.stackSave.apply(null,arguments)};a.dynCall_ii=function(){return a.asm.dynCall_ii.apply(null,arguments)};a.dynCall_iii=function(){return a.asm.dynCall_iii.apply(null,arguments)};a.dynCall_iiii=function(){return a.asm.dynCall_iiii.apply(null,arguments)};a.dynCall_iiiiiii=function(){return a.asm.dynCall_iiiiiii.apply(null,arguments)};a.dynCall_v=function(){return a.asm.dynCall_v.apply(null, 87 | arguments)};a.dynCall_vi=function(){return a.asm.dynCall_vi.apply(null,arguments)};a.dynCall_vii=function(){return a.asm.dynCall_vii.apply(null,arguments)};a.dynCall_viii=function(){return a.asm.dynCall_viii.apply(null,arguments)};a.dynCall_viiii=function(){return a.asm.dynCall_viiii.apply(null,arguments)};a.dynCall_viiiii=function(){return a.asm.dynCall_viiiii.apply(null,arguments)};a.dynCall_viiiiii=function(){return a.asm.dynCall_viiiiii.apply(null,arguments)};a.asm=Ua;a.then=function(d){if(a.calledRun)d(a); 88 | else{var c=a.onRuntimeInitialized;a.onRuntimeInitialized=function(){c&&c();d(a)}}return a};na.prototype=Error();na.prototype.constructor=na;ra=function c(){a.calledRun||wa();a.calledRun||(ra=c)};a.run=wa;a.exit=function(c,b){if(!b||!a.noExitRuntime||0!==c){if(!a.noExitRuntime&&(oa=!0,ta=void 0,B(ob),a.onExit))a.onExit(c);qa&&process.exit(c);a.quit(c,new na(c))}};a.abort=O;if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=l.size?(f(0>= 91 | 1;break;case 4:d>>=2;break;case 8:d>>=3}for(var c=0;c 30 | * 31 | * ${pkg.homepage} 32 | * 33 | * Released under the MIT License. 34 | */`; 35 | 36 | let plugins = [ 37 | json(), 38 | vue({ 39 | css: true 40 | }), 41 | postcss({ 42 | plugins: [ 43 | autoprefixer() 44 | ] 45 | }), 46 | buble({ 47 | objectAssign: 'Object.assign' 48 | }), 49 | resolve({ 50 | customResolveOptions: { 51 | moduleDirectory: 'node_modules' 52 | } 53 | }), 54 | commonjs() 55 | ]; 56 | 57 | export default [{ 58 | input: path.join(__dirname, 'src', 'index.js'), 59 | plugins, 60 | output: [ 61 | {file: pkg.module, format: 'es', sourcemap: true, banner}, 62 | {file: pkg.main, format: 'umd', sourcemap: true, banner, name: "Vue3DModelViewer"} 63 | ] 64 | }, { 65 | input: path.join(__dirname, 'src', 'index.js'), 66 | plugins: [uglify()].concat(plugins || []), 67 | output: {file: pkg.unpkg, format: 'umd', sourcemap: true, banner, name: "Vue3DModelViewer"} 68 | 69 | }]; 70 | -------------------------------------------------------------------------------- /src/components/ModelViewer.vue: -------------------------------------------------------------------------------- 1 | 67 | -------------------------------------------------------------------------------- /src/controls/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | 9 | // This set of controls performs orbiting, dollying (zooming), and panning. 10 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). 11 | // 12 | // Orbit - left mouse / touch: one-finger move 13 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish 14 | // Pan - right mouse, or arrow keys / touch: two-finger move 15 | import * as THREE from 'three'; 16 | 17 | // https://github.com/mrdoob/three.js/blob/dev/examples/js/controls/OrbitControls.js 18 | 19 | const OrbitControls = function ( object, domElement ) { 20 | 21 | this.object = object; 22 | 23 | this.domElement = ( domElement !== undefined ) ? domElement : document; 24 | 25 | // Set to false to disable this control 26 | this.enabled = true; 27 | 28 | // "target" sets the location of focus, where the object orbits around 29 | this.target = new THREE.Vector3(); 30 | 31 | // How far you can dolly in and out ( PerspectiveCamera only ) 32 | this.minDistance = 0; 33 | this.maxDistance = Infinity; 34 | 35 | // How far you can zoom in and out ( OrthographicCamera only ) 36 | this.minZoom = 0; 37 | this.maxZoom = Infinity; 38 | 39 | // How far you can orbit vertically, upper and lower limits. 40 | // Range is 0 to Math.PI radians. 41 | this.minPolarAngle = 0; // radians 42 | this.maxPolarAngle = Math.PI; // radians 43 | 44 | // How far you can orbit horizontally, upper and lower limits. 45 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 46 | this.minAzimuthAngle = - Infinity; // radians 47 | this.maxAzimuthAngle = Infinity; // radians 48 | 49 | // Set to true to enable damping (inertia) 50 | // If damping is enabled, you must call controls.update() in your animation loop 51 | this.enableDamping = false; 52 | this.dampingFactor = 0.25; 53 | 54 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. 55 | // Set to false to disable zooming 56 | this.enableZoom = true; 57 | this.zoomSpeed = 1.0; 58 | 59 | // Set to false to disable rotating 60 | this.enableRotate = true; 61 | this.rotateSpeed = 1.0; 62 | 63 | // Set to false to disable panning 64 | this.enablePan = true; 65 | this.panSpeed = 1.0; 66 | this.screenSpacePanning = false; // if true, pan in screen-space 67 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 68 | 69 | // Set to true to automatically rotate around the target 70 | // If auto-rotate is enabled, you must call controls.update() in your animation loop 71 | this.autoRotate = false; 72 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 73 | 74 | // Set to false to disable use of the keys 75 | this.enableKeys = true; 76 | 77 | // The four arrow keys 78 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 79 | 80 | // Mouse buttons 81 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 82 | 83 | // for reset 84 | this.target0 = this.target.clone(); 85 | this.position0 = this.object.position.clone(); 86 | this.zoom0 = this.object.zoom; 87 | 88 | // 89 | // public methods 90 | // 91 | 92 | this.getPolarAngle = function () { 93 | 94 | return spherical.phi; 95 | 96 | }; 97 | 98 | this.getAzimuthalAngle = function () { 99 | 100 | return spherical.theta; 101 | 102 | }; 103 | 104 | this.saveState = function () { 105 | 106 | scope.target0.copy( scope.target ); 107 | scope.position0.copy( scope.object.position ); 108 | scope.zoom0 = scope.object.zoom; 109 | 110 | }; 111 | 112 | this.reset = function () { 113 | 114 | scope.target.copy( scope.target0 ); 115 | scope.object.position.copy( scope.position0 ); 116 | scope.object.zoom = scope.zoom0; 117 | 118 | scope.object.updateProjectionMatrix(); 119 | scope.dispatchEvent( changeEvent ); 120 | 121 | scope.update(); 122 | 123 | state = STATE.NONE; 124 | 125 | }; 126 | 127 | // this method is exposed, but perhaps it would be better if we can make it private... 128 | this.update = function () { 129 | 130 | var offset = new THREE.Vector3(); 131 | 132 | // so camera.up is the orbit axis 133 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 134 | var quatInverse = quat.clone().inverse(); 135 | 136 | var lastPosition = new THREE.Vector3(); 137 | var lastQuaternion = new THREE.Quaternion(); 138 | 139 | return function update() { 140 | 141 | var position = scope.object.position; 142 | 143 | offset.copy( position ).sub( scope.target ); 144 | 145 | // rotate offset to "y-axis-is-up" space 146 | offset.applyQuaternion( quat ); 147 | 148 | // angle from z-axis around y-axis 149 | spherical.setFromVector3( offset ); 150 | 151 | if ( scope.autoRotate && state === STATE.NONE ) { 152 | 153 | rotateLeft( getAutoRotationAngle() ); 154 | 155 | } 156 | 157 | spherical.theta += sphericalDelta.theta; 158 | spherical.phi += sphericalDelta.phi; 159 | 160 | // restrict theta to be between desired limits 161 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); 162 | 163 | // restrict phi to be between desired limits 164 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); 165 | 166 | spherical.makeSafe(); 167 | 168 | 169 | spherical.radius *= scale; 170 | 171 | // restrict radius to be between desired limits 172 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); 173 | 174 | // move target to panned location 175 | scope.target.add( panOffset ); 176 | 177 | offset.setFromSpherical( spherical ); 178 | 179 | // rotate offset back to "camera-up-vector-is-up" space 180 | offset.applyQuaternion( quatInverse ); 181 | 182 | position.copy( scope.target ).add( offset ); 183 | 184 | scope.object.lookAt( scope.target ); 185 | 186 | if ( scope.enableDamping === true ) { 187 | 188 | sphericalDelta.theta *= ( 1 - scope.dampingFactor ); 189 | sphericalDelta.phi *= ( 1 - scope.dampingFactor ); 190 | 191 | panOffset.multiplyScalar( 1 - scope.dampingFactor ); 192 | 193 | } else { 194 | 195 | sphericalDelta.set( 0, 0, 0 ); 196 | 197 | panOffset.set( 0, 0, 0 ); 198 | 199 | } 200 | 201 | scale = 1; 202 | 203 | // update condition is: 204 | // min(camera displacement, camera rotation in radians)^2 > EPS 205 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 206 | 207 | if ( zoomChanged || 208 | lastPosition.distanceToSquared( scope.object.position ) > EPS || 209 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { 210 | 211 | scope.dispatchEvent( changeEvent ); 212 | 213 | lastPosition.copy( scope.object.position ); 214 | lastQuaternion.copy( scope.object.quaternion ); 215 | zoomChanged = false; 216 | 217 | return true; 218 | 219 | } 220 | 221 | return false; 222 | 223 | }; 224 | 225 | }(); 226 | 227 | this.dispose = function () { 228 | 229 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 230 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 231 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); 232 | 233 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); 234 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); 235 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); 236 | 237 | document.removeEventListener( 'mousemove', onMouseMove, false ); 238 | document.removeEventListener( 'mouseup', onMouseUp, false ); 239 | 240 | window.removeEventListener( 'keydown', onKeyDown, false ); 241 | 242 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 243 | 244 | }; 245 | 246 | // 247 | // internals 248 | // 249 | 250 | var scope = this; 251 | 252 | var changeEvent = { type: 'change' }; 253 | var startEvent = { type: 'start' }; 254 | var endEvent = { type: 'end' }; 255 | 256 | var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; 257 | 258 | var state = STATE.NONE; 259 | 260 | var EPS = 0.000001; 261 | 262 | // current position in spherical coordinates 263 | var spherical = new THREE.Spherical(); 264 | var sphericalDelta = new THREE.Spherical(); 265 | 266 | var scale = 1; 267 | var panOffset = new THREE.Vector3(); 268 | var zoomChanged = false; 269 | 270 | var rotateStart = new THREE.Vector2(); 271 | var rotateEnd = new THREE.Vector2(); 272 | var rotateDelta = new THREE.Vector2(); 273 | 274 | var panStart = new THREE.Vector2(); 275 | var panEnd = new THREE.Vector2(); 276 | var panDelta = new THREE.Vector2(); 277 | 278 | var dollyStart = new THREE.Vector2(); 279 | var dollyEnd = new THREE.Vector2(); 280 | var dollyDelta = new THREE.Vector2(); 281 | 282 | function getAutoRotationAngle() { 283 | 284 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 285 | 286 | } 287 | 288 | function getZoomScale() { 289 | 290 | return Math.pow( 0.95, scope.zoomSpeed ); 291 | 292 | } 293 | 294 | function rotateLeft( angle ) { 295 | 296 | sphericalDelta.theta -= angle; 297 | 298 | } 299 | 300 | function rotateUp( angle ) { 301 | 302 | sphericalDelta.phi -= angle; 303 | 304 | } 305 | 306 | var panLeft = function () { 307 | 308 | var v = new THREE.Vector3(); 309 | 310 | return function panLeft( distance, objectMatrix ) { 311 | 312 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix 313 | v.multiplyScalar( - distance ); 314 | 315 | panOffset.add( v ); 316 | 317 | }; 318 | 319 | }(); 320 | 321 | var panUp = function () { 322 | 323 | var v = new THREE.Vector3(); 324 | 325 | return function panUp( distance, objectMatrix ) { 326 | 327 | if ( scope.screenSpacePanning === true ) { 328 | 329 | v.setFromMatrixColumn( objectMatrix, 1 ); 330 | 331 | } else { 332 | 333 | v.setFromMatrixColumn( objectMatrix, 0 ); 334 | v.crossVectors( scope.object.up, v ); 335 | 336 | } 337 | 338 | v.multiplyScalar( distance ); 339 | 340 | panOffset.add( v ); 341 | 342 | }; 343 | 344 | }(); 345 | 346 | // deltaX and deltaY are in pixels; right and down are positive 347 | var pan = function () { 348 | 349 | var offset = new THREE.Vector3(); 350 | 351 | return function pan( deltaX, deltaY ) { 352 | 353 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 354 | 355 | if ( scope.object.isPerspectiveCamera ) { 356 | 357 | // perspective 358 | var position = scope.object.position; 359 | offset.copy( position ).sub( scope.target ); 360 | var targetDistance = offset.length(); 361 | 362 | // half of the fov is center to top of screen 363 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 364 | 365 | // we use only clientHeight here so aspect ratio does not distort speed 366 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); 367 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); 368 | 369 | } else if ( scope.object.isOrthographicCamera ) { 370 | 371 | // orthographic 372 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); 373 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); 374 | 375 | } else { 376 | 377 | // camera neither orthographic nor perspective 378 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 379 | scope.enablePan = false; 380 | 381 | } 382 | 383 | }; 384 | 385 | }(); 386 | 387 | function dollyIn( dollyScale ) { 388 | 389 | if ( scope.object.isPerspectiveCamera ) { 390 | 391 | scale /= dollyScale; 392 | 393 | } else if ( scope.object.isOrthographicCamera ) { 394 | 395 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); 396 | scope.object.updateProjectionMatrix(); 397 | zoomChanged = true; 398 | 399 | } else { 400 | 401 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 402 | scope.enableZoom = false; 403 | 404 | } 405 | 406 | } 407 | 408 | function dollyOut( dollyScale ) { 409 | 410 | if ( scope.object.isPerspectiveCamera ) { 411 | 412 | scale *= dollyScale; 413 | 414 | } else if ( scope.object.isOrthographicCamera ) { 415 | 416 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); 417 | scope.object.updateProjectionMatrix(); 418 | zoomChanged = true; 419 | 420 | } else { 421 | 422 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 423 | scope.enableZoom = false; 424 | 425 | } 426 | 427 | } 428 | 429 | // 430 | // event callbacks - update the object state 431 | // 432 | 433 | function handleMouseDownRotate( event ) { 434 | 435 | //console.log( 'handleMouseDownRotate' ); 436 | 437 | rotateStart.set( event.clientX, event.clientY ); 438 | 439 | } 440 | 441 | function handleMouseDownDolly( event ) { 442 | 443 | //console.log( 'handleMouseDownDolly' ); 444 | 445 | dollyStart.set( event.clientX, event.clientY ); 446 | 447 | } 448 | 449 | function handleMouseDownPan( event ) { 450 | 451 | //console.log( 'handleMouseDownPan' ); 452 | 453 | panStart.set( event.clientX, event.clientY ); 454 | 455 | } 456 | 457 | function handleMouseMoveRotate( event ) { 458 | 459 | //console.log( 'handleMouseMoveRotate' ); 460 | 461 | rotateEnd.set( event.clientX, event.clientY ); 462 | 463 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); 464 | 465 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 466 | 467 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height 468 | 469 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); 470 | 471 | rotateStart.copy( rotateEnd ); 472 | 473 | scope.update(); 474 | 475 | } 476 | 477 | function handleMouseMoveDolly( event ) { 478 | 479 | //console.log( 'handleMouseMoveDolly' ); 480 | 481 | dollyEnd.set( event.clientX, event.clientY ); 482 | 483 | dollyDelta.subVectors( dollyEnd, dollyStart ); 484 | 485 | if ( dollyDelta.y > 0 ) { 486 | 487 | dollyIn( getZoomScale() ); 488 | 489 | } else if ( dollyDelta.y < 0 ) { 490 | 491 | dollyOut( getZoomScale() ); 492 | 493 | } 494 | 495 | dollyStart.copy( dollyEnd ); 496 | 497 | scope.update(); 498 | 499 | } 500 | 501 | function handleMouseMovePan( event ) { 502 | 503 | //console.log( 'handleMouseMovePan' ); 504 | 505 | panEnd.set( event.clientX, event.clientY ); 506 | 507 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); 508 | 509 | pan( panDelta.x, panDelta.y ); 510 | 511 | panStart.copy( panEnd ); 512 | 513 | scope.update(); 514 | 515 | } 516 | // eslint-disable-next-line no-unused-vars 517 | function handleMouseUp( event ) { 518 | 519 | // console.log( 'handleMouseUp' ); 520 | 521 | } 522 | 523 | function handleMouseWheel( event ) { 524 | 525 | // console.log( 'handleMouseWheel' ); 526 | 527 | if ( event.deltaY < 0 ) { 528 | 529 | dollyOut( getZoomScale() ); 530 | 531 | } else if ( event.deltaY > 0 ) { 532 | 533 | dollyIn( getZoomScale() ); 534 | 535 | } 536 | 537 | scope.update(); 538 | 539 | } 540 | 541 | function handleKeyDown( event ) { 542 | 543 | //console.log( 'handleKeyDown' ); 544 | 545 | switch ( event.keyCode ) { 546 | 547 | case scope.keys.UP: 548 | pan( 0, scope.keyPanSpeed ); 549 | scope.update(); 550 | break; 551 | 552 | case scope.keys.BOTTOM: 553 | pan( 0, - scope.keyPanSpeed ); 554 | scope.update(); 555 | break; 556 | 557 | case scope.keys.LEFT: 558 | pan( scope.keyPanSpeed, 0 ); 559 | scope.update(); 560 | break; 561 | 562 | case scope.keys.RIGHT: 563 | pan( - scope.keyPanSpeed, 0 ); 564 | scope.update(); 565 | break; 566 | 567 | } 568 | 569 | } 570 | 571 | function handleTouchStartRotate( event ) { 572 | 573 | //console.log( 'handleTouchStartRotate' ); 574 | 575 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 576 | 577 | } 578 | 579 | function handleTouchStartDollyPan( event ) { 580 | 581 | //console.log( 'handleTouchStartDollyPan' ); 582 | 583 | if ( scope.enableZoom ) { 584 | 585 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 586 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 587 | 588 | var distance = Math.sqrt( dx * dx + dy * dy ); 589 | 590 | dollyStart.set( 0, distance ); 591 | 592 | } 593 | 594 | if ( scope.enablePan ) { 595 | 596 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 597 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 598 | 599 | panStart.set( x, y ); 600 | 601 | } 602 | 603 | } 604 | 605 | function handleTouchMoveRotate( event ) { 606 | 607 | //console.log( 'handleTouchMoveRotate' ); 608 | 609 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 610 | 611 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); 612 | 613 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 614 | 615 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height 616 | 617 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); 618 | 619 | rotateStart.copy( rotateEnd ); 620 | 621 | scope.update(); 622 | 623 | } 624 | 625 | function handleTouchMoveDollyPan( event ) { 626 | 627 | //console.log( 'handleTouchMoveDollyPan' ); 628 | 629 | if ( scope.enableZoom ) { 630 | 631 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 632 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 633 | 634 | var distance = Math.sqrt( dx * dx + dy * dy ); 635 | 636 | dollyEnd.set( 0, distance ); 637 | 638 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); 639 | 640 | dollyIn( dollyDelta.y ); 641 | 642 | dollyStart.copy( dollyEnd ); 643 | 644 | } 645 | 646 | if ( scope.enablePan ) { 647 | 648 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); 649 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); 650 | 651 | panEnd.set( x, y ); 652 | 653 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); 654 | 655 | pan( panDelta.x, panDelta.y ); 656 | 657 | panStart.copy( panEnd ); 658 | 659 | } 660 | 661 | scope.update(); 662 | 663 | } 664 | 665 | // eslint-disable-next-line no-unused-vars 666 | function handleTouchEnd( event ) { 667 | 668 | //console.log( 'handleTouchEnd' ); 669 | 670 | } 671 | 672 | // 673 | // event handlers - FSM: listen for events and reset state 674 | // 675 | 676 | function onMouseDown( event ) { 677 | 678 | if ( scope.enabled === false ) return; 679 | 680 | event.preventDefault(); 681 | 682 | switch ( event.button ) { 683 | 684 | case scope.mouseButtons.ORBIT: 685 | 686 | if ( scope.enableRotate === false ) return; 687 | 688 | handleMouseDownRotate( event ); 689 | 690 | state = STATE.ROTATE; 691 | 692 | break; 693 | 694 | case scope.mouseButtons.ZOOM: 695 | 696 | if ( scope.enableZoom === false ) return; 697 | 698 | handleMouseDownDolly( event ); 699 | 700 | state = STATE.DOLLY; 701 | 702 | break; 703 | 704 | case scope.mouseButtons.PAN: 705 | 706 | if ( scope.enablePan === false ) return; 707 | 708 | handleMouseDownPan( event ); 709 | 710 | state = STATE.PAN; 711 | 712 | break; 713 | 714 | } 715 | 716 | if ( state !== STATE.NONE ) { 717 | 718 | document.addEventListener( 'mousemove', onMouseMove, false ); 719 | document.addEventListener( 'mouseup', onMouseUp, false ); 720 | 721 | scope.dispatchEvent( startEvent ); 722 | 723 | } 724 | 725 | } 726 | 727 | function onMouseMove( event ) { 728 | 729 | if ( scope.enabled === false ) return; 730 | 731 | event.preventDefault(); 732 | 733 | switch ( state ) { 734 | 735 | case STATE.ROTATE: 736 | 737 | if ( scope.enableRotate === false ) return; 738 | 739 | handleMouseMoveRotate( event ); 740 | 741 | break; 742 | 743 | case STATE.DOLLY: 744 | 745 | if ( scope.enableZoom === false ) return; 746 | 747 | handleMouseMoveDolly( event ); 748 | 749 | break; 750 | 751 | case STATE.PAN: 752 | 753 | if ( scope.enablePan === false ) return; 754 | 755 | handleMouseMovePan( event ); 756 | 757 | break; 758 | 759 | } 760 | 761 | } 762 | 763 | function onMouseUp( event ) { 764 | 765 | if ( scope.enabled === false ) return; 766 | 767 | handleMouseUp( event ); 768 | 769 | document.removeEventListener( 'mousemove', onMouseMove, false ); 770 | document.removeEventListener( 'mouseup', onMouseUp, false ); 771 | 772 | scope.dispatchEvent( endEvent ); 773 | 774 | state = STATE.NONE; 775 | 776 | } 777 | 778 | function onMouseWheel( event ) { 779 | 780 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; 781 | 782 | event.preventDefault(); 783 | event.stopPropagation(); 784 | 785 | scope.dispatchEvent( startEvent ); 786 | 787 | handleMouseWheel( event ); 788 | 789 | scope.dispatchEvent( endEvent ); 790 | 791 | } 792 | 793 | function onKeyDown( event ) { 794 | 795 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 796 | 797 | handleKeyDown( event ); 798 | 799 | } 800 | 801 | function onTouchStart( event ) { 802 | 803 | if ( scope.enabled === false ) return; 804 | 805 | event.preventDefault(); 806 | 807 | switch ( event.touches.length ) { 808 | 809 | case 1: // one-fingered touch: rotate 810 | 811 | if ( scope.enableRotate === false ) return; 812 | 813 | handleTouchStartRotate( event ); 814 | 815 | state = STATE.TOUCH_ROTATE; 816 | 817 | break; 818 | 819 | case 2: // two-fingered touch: dolly-pan 820 | 821 | if ( scope.enableZoom === false && scope.enablePan === false ) return; 822 | 823 | handleTouchStartDollyPan( event ); 824 | 825 | state = STATE.TOUCH_DOLLY_PAN; 826 | 827 | break; 828 | 829 | default: 830 | 831 | state = STATE.NONE; 832 | 833 | } 834 | 835 | if ( state !== STATE.NONE ) { 836 | 837 | scope.dispatchEvent( startEvent ); 838 | 839 | } 840 | 841 | } 842 | 843 | function onTouchMove( event ) { 844 | 845 | if ( scope.enabled === false ) return; 846 | 847 | event.preventDefault(); 848 | event.stopPropagation(); 849 | 850 | switch ( event.touches.length ) { 851 | 852 | case 1: // one-fingered touch: rotate 853 | 854 | if ( scope.enableRotate === false ) return; 855 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? 856 | 857 | handleTouchMoveRotate( event ); 858 | 859 | break; 860 | 861 | case 2: // two-fingered touch: dolly-pan 862 | 863 | if ( scope.enableZoom === false && scope.enablePan === false ) return; 864 | if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? 865 | 866 | handleTouchMoveDollyPan( event ); 867 | 868 | break; 869 | 870 | default: 871 | 872 | state = STATE.NONE; 873 | 874 | } 875 | 876 | } 877 | 878 | function onTouchEnd( event ) { 879 | 880 | if ( scope.enabled === false ) return; 881 | 882 | handleTouchEnd( event ); 883 | 884 | scope.dispatchEvent( endEvent ); 885 | 886 | state = STATE.NONE; 887 | 888 | } 889 | 890 | function onContextMenu( event ) { 891 | 892 | if ( scope.enabled === false ) return; 893 | 894 | event.preventDefault(); 895 | 896 | } 897 | 898 | // 899 | 900 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 901 | 902 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 903 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); 904 | 905 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 906 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 907 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 908 | 909 | window.addEventListener( 'keydown', onKeyDown, false ); 910 | 911 | // force an update at start 912 | 913 | this.update(); 914 | 915 | }; 916 | 917 | OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 918 | OrbitControls.prototype.constructor = OrbitControls; 919 | 920 | Object.defineProperties( OrbitControls.prototype, { 921 | 922 | center: { 923 | 924 | get: function () { 925 | 926 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); 927 | return this.target; 928 | 929 | } 930 | 931 | }, 932 | 933 | // backward compatibility 934 | 935 | noZoom: { 936 | 937 | get: function () { 938 | 939 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 940 | return ! this.enableZoom; 941 | 942 | }, 943 | 944 | set: function ( value ) { 945 | 946 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 947 | this.enableZoom = ! value; 948 | 949 | } 950 | 951 | }, 952 | 953 | noRotate: { 954 | 955 | get: function () { 956 | 957 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 958 | return ! this.enableRotate; 959 | 960 | }, 961 | 962 | set: function ( value ) { 963 | 964 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 965 | this.enableRotate = ! value; 966 | 967 | } 968 | 969 | }, 970 | 971 | noPan: { 972 | 973 | get: function () { 974 | 975 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 976 | return ! this.enablePan; 977 | 978 | }, 979 | 980 | set: function ( value ) { 981 | 982 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 983 | this.enablePan = ! value; 984 | 985 | } 986 | 987 | }, 988 | 989 | noKeys: { 990 | 991 | get: function () { 992 | 993 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 994 | return ! this.enableKeys; 995 | 996 | }, 997 | 998 | set: function ( value ) { 999 | 1000 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 1001 | this.enableKeys = ! value; 1002 | 1003 | } 1004 | 1005 | }, 1006 | 1007 | staticMoving: { 1008 | 1009 | get: function () { 1010 | 1011 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 1012 | return ! this.enableDamping; 1013 | 1014 | }, 1015 | 1016 | set: function ( value ) { 1017 | 1018 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 1019 | this.enableDamping = ! value; 1020 | 1021 | } 1022 | 1023 | }, 1024 | 1025 | dynamicDampingFactor: { 1026 | 1027 | get: function () { 1028 | 1029 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1030 | return this.dampingFactor; 1031 | 1032 | }, 1033 | 1034 | set: function ( value ) { 1035 | 1036 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1037 | this.dampingFactor = value; 1038 | 1039 | } 1040 | 1041 | } 1042 | 1043 | } ); 1044 | 1045 | export { OrbitControls }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ModelViewer from './components/ModelViewer.vue'; 2 | 3 | const components = [ 4 | ModelViewer 5 | ]; 6 | 7 | const install = ( Vue ) => { 8 | components.map( component => { 9 | Vue.component( component.name, component ); 10 | } ); 11 | }; 12 | 13 | if ( typeof window !== 'undefined' && window.Vue ) { 14 | install( window.Vue ); 15 | } 16 | 17 | /*export default { 18 | install, 19 | ModelViewer 20 | }*/ 21 | 22 | export { 23 | install, 24 | ModelViewer 25 | } 26 | -------------------------------------------------------------------------------- /src/loaders/DDSLoader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | import * as THREE from 'three'; 5 | 6 | const DDSLoader = function ( manager ) { 7 | 8 | THREE.CompressedTextureLoader.call( this, manager ); 9 | 10 | this._parser = DDSLoader.parse; 11 | 12 | }; 13 | 14 | DDSLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype ); 15 | DDSLoader.prototype.constructor = DDSLoader; 16 | 17 | DDSLoader.parse = function ( buffer, loadMipmaps ) { 18 | 19 | var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; 20 | 21 | // Adapted from @toji's DDS utils 22 | // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js 23 | 24 | // All values and structures referenced from: 25 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ 26 | 27 | var DDS_MAGIC = 0x20534444; 28 | 29 | var DDSD_CAPS = 0x1, 30 | DDSD_HEIGHT = 0x2, 31 | DDSD_WIDTH = 0x4, 32 | DDSD_PITCH = 0x8, 33 | DDSD_PIXELFORMAT = 0x1000, 34 | DDSD_MIPMAPCOUNT = 0x20000, 35 | DDSD_LINEARSIZE = 0x80000, 36 | DDSD_DEPTH = 0x800000; 37 | 38 | var DDSCAPS_COMPLEX = 0x8, 39 | DDSCAPS_MIPMAP = 0x400000, 40 | DDSCAPS_TEXTURE = 0x1000; 41 | 42 | var DDSCAPS2_CUBEMAP = 0x200, 43 | DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, 44 | DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, 45 | DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, 46 | DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, 47 | DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, 48 | DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, 49 | DDSCAPS2_VOLUME = 0x200000; 50 | 51 | var DDPF_ALPHAPIXELS = 0x1, 52 | DDPF_ALPHA = 0x2, 53 | DDPF_FOURCC = 0x4, 54 | DDPF_RGB = 0x40, 55 | DDPF_YUV = 0x200, 56 | DDPF_LUMINANCE = 0x20000; 57 | 58 | function fourCCToInt32( value ) { 59 | 60 | return value.charCodeAt( 0 ) + 61 | ( value.charCodeAt( 1 ) << 8 ) + 62 | ( value.charCodeAt( 2 ) << 16 ) + 63 | ( value.charCodeAt( 3 ) << 24 ); 64 | 65 | } 66 | 67 | function int32ToFourCC( value ) { 68 | 69 | return String.fromCharCode( 70 | value & 0xff, 71 | ( value >> 8 ) & 0xff, 72 | ( value >> 16 ) & 0xff, 73 | ( value >> 24 ) & 0xff 74 | ); 75 | 76 | } 77 | 78 | function loadARGBMip( buffer, dataOffset, width, height ) { 79 | 80 | var dataLength = width * height * 4; 81 | var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); 82 | var byteArray = new Uint8Array( dataLength ); 83 | var dst = 0; 84 | var src = 0; 85 | for ( var y = 0; y < height; y ++ ) { 86 | 87 | for ( var x = 0; x < width; x ++ ) { 88 | 89 | var b = srcBuffer[ src ]; src ++; 90 | var g = srcBuffer[ src ]; src ++; 91 | var r = srcBuffer[ src ]; src ++; 92 | var a = srcBuffer[ src ]; src ++; 93 | byteArray[ dst ] = r; dst ++; //r 94 | byteArray[ dst ] = g; dst ++; //g 95 | byteArray[ dst ] = b; dst ++; //b 96 | byteArray[ dst ] = a; dst ++; //a 97 | 98 | } 99 | 100 | } 101 | return byteArray; 102 | 103 | } 104 | 105 | var FOURCC_DXT1 = fourCCToInt32( "DXT1" ); 106 | var FOURCC_DXT3 = fourCCToInt32( "DXT3" ); 107 | var FOURCC_DXT5 = fourCCToInt32( "DXT5" ); 108 | var FOURCC_ETC1 = fourCCToInt32( "ETC1" ); 109 | 110 | var headerLengthInt = 31; // The header length in 32 bit ints 111 | 112 | // Offsets into the header array 113 | 114 | var off_magic = 0; 115 | 116 | var off_size = 1; 117 | var off_flags = 2; 118 | var off_height = 3; 119 | var off_width = 4; 120 | 121 | var off_mipmapCount = 7; 122 | 123 | var off_pfFlags = 20; 124 | var off_pfFourCC = 21; 125 | var off_RGBBitCount = 22; 126 | var off_RBitMask = 23; 127 | var off_GBitMask = 24; 128 | var off_BBitMask = 25; 129 | var off_ABitMask = 26; 130 | 131 | var off_caps = 27; 132 | var off_caps2 = 28; 133 | var off_caps3 = 29; 134 | var off_caps4 = 30; 135 | 136 | // Parse header 137 | 138 | var header = new Int32Array( buffer, 0, headerLengthInt ); 139 | 140 | if ( header[ off_magic ] !== DDS_MAGIC ) { 141 | 142 | console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); 143 | return dds; 144 | 145 | } 146 | 147 | if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { 148 | 149 | console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); 150 | return dds; 151 | 152 | } 153 | 154 | var blockBytes; 155 | 156 | var fourCC = header[ off_pfFourCC ]; 157 | 158 | var isRGBAUncompressed = false; 159 | 160 | switch ( fourCC ) { 161 | 162 | case FOURCC_DXT1: 163 | 164 | blockBytes = 8; 165 | dds.format = THREE.RGB_S3TC_DXT1_Format; 166 | break; 167 | 168 | case FOURCC_DXT3: 169 | 170 | blockBytes = 16; 171 | dds.format = THREE.RGBA_S3TC_DXT3_Format; 172 | break; 173 | 174 | case FOURCC_DXT5: 175 | 176 | blockBytes = 16; 177 | dds.format = THREE.RGBA_S3TC_DXT5_Format; 178 | break; 179 | 180 | case FOURCC_ETC1: 181 | 182 | blockBytes = 8; 183 | dds.format = THREE.RGB_ETC1_Format; 184 | break; 185 | 186 | default: 187 | 188 | if ( header[ off_RGBBitCount ] === 32 189 | && header[ off_RBitMask ] & 0xff0000 190 | && header[ off_GBitMask ] & 0xff00 191 | && header[ off_BBitMask ] & 0xff 192 | && header[ off_ABitMask ] & 0xff000000 ) { 193 | 194 | isRGBAUncompressed = true; 195 | blockBytes = 64; 196 | dds.format = THREE.RGBAFormat; 197 | 198 | } else { 199 | 200 | console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); 201 | return dds; 202 | 203 | } 204 | 205 | } 206 | 207 | dds.mipmapCount = 1; 208 | 209 | if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { 210 | 211 | dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); 212 | 213 | } 214 | 215 | var caps2 = header[ off_caps2 ]; 216 | dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false; 217 | if ( dds.isCubemap && ( 218 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) || 219 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) || 220 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) || 221 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) || 222 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) || 223 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ ) 224 | ) ) { 225 | 226 | console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' ); 227 | return dds; 228 | 229 | } 230 | 231 | dds.width = header[ off_width ]; 232 | dds.height = header[ off_height ]; 233 | 234 | var dataOffset = header[ off_size ] + 4; 235 | 236 | // Extract mipmaps buffers 237 | 238 | var faces = dds.isCubemap ? 6 : 1; 239 | 240 | for ( var face = 0; face < faces; face ++ ) { 241 | 242 | var width = dds.width; 243 | var height = dds.height; 244 | 245 | for ( var i = 0; i < dds.mipmapCount; i ++ ) { 246 | 247 | if ( isRGBAUncompressed ) { 248 | 249 | var byteArray = loadARGBMip( buffer, dataOffset, width, height ); 250 | var dataLength = byteArray.length; 251 | 252 | } else { 253 | 254 | var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; 255 | var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); 256 | 257 | } 258 | 259 | var mipmap = { "data": byteArray, "width": width, "height": height }; 260 | dds.mipmaps.push( mipmap ); 261 | 262 | dataOffset += dataLength; 263 | 264 | width = Math.max( width >> 1, 1 ); 265 | height = Math.max( height >> 1, 1 ); 266 | 267 | } 268 | 269 | } 270 | 271 | return dds; 272 | 273 | }; 274 | 275 | export { DDSLoader } 276 | -------------------------------------------------------------------------------- /src/loaders/DRACOLoader.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Draco Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 'use strict'; 16 | import * as THREE from 'three'; 17 | 18 | /** 19 | * @param {THREE.LoadingManager} manager 20 | */ 21 | const DRACOLoader = function(manager) { 22 | this.timeLoaded = 0; 23 | this.manager = manager || THREE.DefaultLoadingManager; 24 | this.materials = null; 25 | this.verbosity = 0; 26 | this.attributeOptions = {}; 27 | this.drawMode = THREE.TrianglesDrawMode; 28 | // Native Draco attribute type to Three.JS attribute type. 29 | this.nativeAttributeMap = { 30 | 'position' : 'POSITION', 31 | 'normal' : 'NORMAL', 32 | 'color' : 'COLOR', 33 | 'uv' : 'TEX_COORD' 34 | }; 35 | }; 36 | 37 | DRACOLoader.prototype = { 38 | 39 | constructor: DRACOLoader, 40 | 41 | load: function(url, onLoad, onProgress, onError) { 42 | var scope = this; 43 | var loader = new THREE.FileLoader(scope.manager); 44 | loader.setPath(this.path); 45 | loader.setResponseType('arraybuffer'); 46 | loader.load(url, function(blob) { 47 | scope.decodeDracoFile(blob, onLoad); 48 | }, onProgress, onError); 49 | }, 50 | 51 | setPath: function(value) { 52 | this.path = value; 53 | return this; 54 | }, 55 | 56 | setVerbosity: function(level) { 57 | this.verbosity = level; 58 | return this; 59 | }, 60 | 61 | /** 62 | * Sets desired mode for generated geometry indices. 63 | * Can be either: 64 | * THREE.TrianglesDrawMode 65 | * THREE.TriangleStripDrawMode 66 | */ 67 | setDrawMode: function(drawMode) { 68 | this.drawMode = drawMode; 69 | return this; 70 | }, 71 | 72 | /** 73 | * Skips dequantization for a specific attribute. 74 | * |attributeName| is the THREE.js name of the given attribute type. 75 | * The only currently supported |attributeName| is 'position', more may be 76 | * added in future. 77 | */ 78 | setSkipDequantization: function(attributeName, skip) { 79 | var skipDequantization = true; 80 | if (typeof skip !== 'undefined') 81 | skipDequantization = skip; 82 | this.getAttributeOptions(attributeName).skipDequantization = 83 | skipDequantization; 84 | return this; 85 | }, 86 | 87 | /** 88 | * |attributeUniqueIdMap| specifies attribute unique id for an attribute in 89 | * the geometry to be decoded. The name of the attribute must be one of the 90 | * supported attribute type in Three.JS, including: 91 | * 'position', 92 | * 'color', 93 | * 'normal', 94 | * 'uv', 95 | * 'uv2', 96 | * 'skinIndex', 97 | * 'skinWeight'. 98 | * The format is: 99 | * attributeUniqueIdMap[attributeName] = attributeId 100 | */ 101 | decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap, 102 | attributeTypeMap) { 103 | var scope = this; 104 | DRACOLoader.getDecoderModule() 105 | .then( function ( module ) { 106 | scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback, 107 | attributeUniqueIdMap || {}, attributeTypeMap || {}); 108 | }); 109 | }, 110 | 111 | decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback, 112 | attributeUniqueIdMap, attributeTypeMap) { 113 | /* 114 | * Here is how to use Draco Javascript decoder and get the geometry. 115 | */ 116 | var buffer = new dracoDecoder.DecoderBuffer(); 117 | buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength); 118 | var decoder = new dracoDecoder.Decoder(); 119 | 120 | /* 121 | * Determine what type is this file: mesh or point cloud. 122 | */ 123 | var geometryType = decoder.GetEncodedGeometryType(buffer); 124 | if (geometryType == dracoDecoder.TRIANGULAR_MESH) { 125 | if (this.verbosity > 0) { 126 | console.log('Loaded a mesh.'); 127 | } 128 | } else if (geometryType == dracoDecoder.POINT_CLOUD) { 129 | if (this.verbosity > 0) { 130 | console.log('Loaded a point cloud.'); 131 | } 132 | } else { 133 | var errorMsg = 'DRACOLoader: Unknown geometry type.' 134 | console.error(errorMsg); 135 | throw new Error(errorMsg); 136 | } 137 | callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder, 138 | geometryType, buffer, attributeUniqueIdMap, attributeTypeMap)); 139 | }, 140 | 141 | addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry, 142 | attributeName, attributeType, attribute, 143 | geometry, geometryBuffer) { 144 | if (attribute.ptr === 0) { 145 | var errorMsg = 'DRACOLoader: No attribute ' + attributeName; 146 | console.error(errorMsg); 147 | throw new Error(errorMsg); 148 | } 149 | 150 | var numComponents = attribute.num_components(); 151 | var numPoints = dracoGeometry.num_points(); 152 | var numValues = numPoints * numComponents; 153 | var attributeData; 154 | var TypedBufferAttribute; 155 | 156 | switch ( attributeType ) { 157 | 158 | case Float32Array: 159 | attributeData = new dracoDecoder.DracoFloat32Array(); 160 | decoder.GetAttributeFloatForAllPoints( 161 | dracoGeometry, attribute, attributeData); 162 | geometryBuffer[ attributeName ] = new Float32Array( numValues ); 163 | TypedBufferAttribute = THREE.Float32BufferAttribute; 164 | break; 165 | 166 | case Int8Array: 167 | attributeData = new dracoDecoder.DracoInt8Array(); 168 | decoder.GetAttributeInt8ForAllPoints( 169 | dracoGeometry, attribute, attributeData ); 170 | geometryBuffer[ attributeName ] = new Int8Array( numValues ); 171 | TypedBufferAttribute = THREE.Int8BufferAttribute; 172 | break; 173 | 174 | case Int16Array: 175 | attributeData = new dracoDecoder.DracoInt16Array(); 176 | decoder.GetAttributeInt16ForAllPoints( 177 | dracoGeometry, attribute, attributeData); 178 | geometryBuffer[ attributeName ] = new Int16Array( numValues ); 179 | TypedBufferAttribute = THREE.Int16BufferAttribute; 180 | break; 181 | 182 | case Int32Array: 183 | attributeData = new dracoDecoder.DracoInt32Array(); 184 | decoder.GetAttributeInt32ForAllPoints( 185 | dracoGeometry, attribute, attributeData); 186 | geometryBuffer[ attributeName ] = new Int32Array( numValues ); 187 | TypedBufferAttribute = THREE.Int32BufferAttribute; 188 | break; 189 | 190 | case Uint8Array: 191 | attributeData = new dracoDecoder.DracoUInt8Array(); 192 | decoder.GetAttributeUInt8ForAllPoints( 193 | dracoGeometry, attribute, attributeData); 194 | geometryBuffer[ attributeName ] = new Uint8Array( numValues ); 195 | TypedBufferAttribute = THREE.Uint8BufferAttribute; 196 | break; 197 | 198 | case Uint16Array: 199 | attributeData = new dracoDecoder.DracoUInt16Array(); 200 | decoder.GetAttributeUInt16ForAllPoints( 201 | dracoGeometry, attribute, attributeData); 202 | geometryBuffer[ attributeName ] = new Uint16Array( numValues ); 203 | TypedBufferAttribute = THREE.Uint16BufferAttribute; 204 | break; 205 | 206 | case Uint32Array: 207 | attributeData = new dracoDecoder.DracoUInt32Array(); 208 | decoder.GetAttributeUInt32ForAllPoints( 209 | dracoGeometry, attribute, attributeData); 210 | geometryBuffer[ attributeName ] = new Uint32Array( numValues ); 211 | TypedBufferAttribute = THREE.Uint32BufferAttribute; 212 | break; 213 | 214 | default: 215 | var errorMsg = 'DRACOLoader: Unexpected attribute type.'; 216 | console.error( errorMsg ); 217 | throw new Error( errorMsg ); 218 | 219 | } 220 | 221 | // Copy data from decoder. 222 | for (var i = 0; i < numValues; i++) { 223 | geometryBuffer[attributeName][i] = attributeData.GetValue(i); 224 | } 225 | // Add attribute to THREEJS geometry for rendering. 226 | geometry.addAttribute(attributeName, 227 | new TypedBufferAttribute(geometryBuffer[attributeName], 228 | numComponents)); 229 | dracoDecoder.destroy(attributeData); 230 | }, 231 | 232 | convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType, 233 | buffer, attributeUniqueIdMap, 234 | attributeTypeMap) { 235 | if (this.getAttributeOptions('position').skipDequantization === true) { 236 | decoder.SkipAttributeTransform(dracoDecoder.POSITION); 237 | } 238 | var dracoGeometry; 239 | var decodingStatus; 240 | const start_time = performance.now(); 241 | if (geometryType === dracoDecoder.TRIANGULAR_MESH) { 242 | dracoGeometry = new dracoDecoder.Mesh(); 243 | decodingStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry); 244 | } else { 245 | dracoGeometry = new dracoDecoder.PointCloud(); 246 | decodingStatus = 247 | decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); 248 | } 249 | if (!decodingStatus.ok() || dracoGeometry.ptr == 0) { 250 | var errorMsg = 'DRACOLoader: Decoding failed: '; 251 | errorMsg += decodingStatus.error_msg(); 252 | console.error(errorMsg); 253 | dracoDecoder.destroy(decoder); 254 | dracoDecoder.destroy(dracoGeometry); 255 | throw new Error(errorMsg); 256 | } 257 | 258 | var decode_end = performance.now(); 259 | dracoDecoder.destroy(buffer); 260 | /* 261 | * Example on how to retrieve mesh and attributes. 262 | */ 263 | var numFaces; 264 | if (geometryType == dracoDecoder.TRIANGULAR_MESH) { 265 | numFaces = dracoGeometry.num_faces(); 266 | if (this.verbosity > 0) { 267 | console.log('Number of faces loaded: ' + numFaces.toString()); 268 | } 269 | } else { 270 | numFaces = 0; 271 | } 272 | 273 | var numPoints = dracoGeometry.num_points(); 274 | var numAttributes = dracoGeometry.num_attributes(); 275 | if (this.verbosity > 0) { 276 | console.log('Number of points loaded: ' + numPoints.toString()); 277 | console.log('Number of attributes loaded: ' + 278 | numAttributes.toString()); 279 | } 280 | 281 | // Verify if there is position attribute. 282 | var posAttId = decoder.GetAttributeId(dracoGeometry, 283 | dracoDecoder.POSITION); 284 | if (posAttId == -1) { 285 | var errorMsg = 'DRACOLoader: No position attribute found.'; 286 | console.error(errorMsg); 287 | dracoDecoder.destroy(decoder); 288 | dracoDecoder.destroy(dracoGeometry); 289 | throw new Error(errorMsg); 290 | } 291 | var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId); 292 | 293 | // Structure for converting to THREEJS geometry later. 294 | var geometryBuffer = {}; 295 | // Import data to Three JS geometry. 296 | var geometry = new THREE.BufferGeometry(); 297 | 298 | // Add native Draco attribute type to geometry. 299 | for (var attributeName in this.nativeAttributeMap) { 300 | // The native attribute type is only used when no unique Id is 301 | // provided. For example, loading .drc files. 302 | if (attributeUniqueIdMap[attributeName] === undefined) { 303 | var attId = decoder.GetAttributeId(dracoGeometry, 304 | dracoDecoder[this.nativeAttributeMap[attributeName]]); 305 | if (attId !== -1) { 306 | if (this.verbosity > 0) { 307 | console.log('Loaded ' + attributeName + ' attribute.'); 308 | } 309 | var attribute = decoder.GetAttribute(dracoGeometry, attId); 310 | this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, 311 | attributeName, Float32Array, attribute, geometry, geometryBuffer); 312 | } 313 | } 314 | } 315 | 316 | // Add attributes of user specified unique id. E.g. GLTF models. 317 | for (var attributeName in attributeUniqueIdMap) { 318 | var attributeType = attributeTypeMap[attributeName] || Float32Array; 319 | var attributeId = attributeUniqueIdMap[attributeName]; 320 | var attribute = decoder.GetAttributeByUniqueId(dracoGeometry, 321 | attributeId); 322 | this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, 323 | attributeName, attributeType, attribute, geometry, geometryBuffer); 324 | } 325 | 326 | // For mesh, we need to generate the faces. 327 | if (geometryType == dracoDecoder.TRIANGULAR_MESH) { 328 | if (this.drawMode === THREE.TriangleStripDrawMode) { 329 | var stripsArray = new dracoDecoder.DracoInt32Array(); 330 | var numStrips = decoder.GetTriangleStripsFromMesh( 331 | dracoGeometry, stripsArray); 332 | geometryBuffer.indices = new Uint32Array(stripsArray.size()); 333 | for (var i = 0; i < stripsArray.size(); ++i) { 334 | geometryBuffer.indices[i] = stripsArray.GetValue(i); 335 | } 336 | dracoDecoder.destroy(stripsArray); 337 | } else { 338 | var numIndices = numFaces * 3; 339 | geometryBuffer.indices = new Uint32Array(numIndices); 340 | var ia = new dracoDecoder.DracoInt32Array(); 341 | for (var i = 0; i < numFaces; ++i) { 342 | decoder.GetFaceFromMesh(dracoGeometry, i, ia); 343 | var index = i * 3; 344 | geometryBuffer.indices[index] = ia.GetValue(0); 345 | geometryBuffer.indices[index + 1] = ia.GetValue(1); 346 | geometryBuffer.indices[index + 2] = ia.GetValue(2); 347 | } 348 | dracoDecoder.destroy(ia); 349 | } 350 | } 351 | 352 | geometry.drawMode = this.drawMode; 353 | if (geometryType == dracoDecoder.TRIANGULAR_MESH) { 354 | geometry.setIndex(new(geometryBuffer.indices.length > 65535 ? 355 | THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute) 356 | (geometryBuffer.indices, 1)); 357 | } 358 | var posTransform = new dracoDecoder.AttributeQuantizationTransform(); 359 | if (posTransform.InitFromAttribute(posAttribute)) { 360 | // Quantized attribute. Store the quantization parameters into the 361 | // THREE.js attribute. 362 | geometry.attributes['position'].isQuantized = true; 363 | geometry.attributes['position'].maxRange = posTransform.range(); 364 | geometry.attributes['position'].numQuantizationBits = 365 | posTransform.quantization_bits(); 366 | geometry.attributes['position'].minValues = new Float32Array(3); 367 | for (var i = 0; i < 3; ++i) { 368 | geometry.attributes['position'].minValues[i] = 369 | posTransform.min_value(i); 370 | } 371 | } 372 | dracoDecoder.destroy(posTransform); 373 | dracoDecoder.destroy(decoder); 374 | dracoDecoder.destroy(dracoGeometry); 375 | 376 | this.decode_time = decode_end - start_time; 377 | this.import_time = performance.now() - decode_end; 378 | 379 | if (this.verbosity > 0) { 380 | console.log('Decode time: ' + this.decode_time); 381 | console.log('Import time: ' + this.import_time); 382 | } 383 | return geometry; 384 | }, 385 | 386 | isVersionSupported: function(version, callback) { 387 | DRACOLoader.getDecoderModule() 388 | .then( function ( module ) { 389 | callback( module.decoder.isVersionSupported( version ) ); 390 | }); 391 | }, 392 | 393 | getAttributeOptions: function(attributeName) { 394 | if (typeof this.attributeOptions[attributeName] === 'undefined') 395 | this.attributeOptions[attributeName] = {}; 396 | return this.attributeOptions[attributeName]; 397 | } 398 | }; 399 | 400 | DRACOLoader.decoderPath = './'; 401 | DRACOLoader.decoderConfig = {}; 402 | DRACOLoader.decoderModulePromise = null; 403 | 404 | /** 405 | * Sets the base path for decoder source files. 406 | * @param {string} path 407 | */ 408 | DRACOLoader.setDecoderPath = function ( path ) { 409 | DRACOLoader.decoderPath = path; 410 | }; 411 | 412 | /** 413 | * Sets decoder configuration and releases singleton decoder module. Module 414 | * will be recreated with the next decoding call. 415 | * @param {Object} config 416 | */ 417 | DRACOLoader.setDecoderConfig = function ( config ) { 418 | var wasmBinary = DRACOLoader.decoderConfig.wasmBinary; 419 | DRACOLoader.decoderConfig = config || {}; 420 | DRACOLoader.releaseDecoderModule(); 421 | 422 | // Reuse WASM binary. 423 | if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary; 424 | }; 425 | 426 | /** 427 | * Releases the singleton DracoDecoderModule instance. Module will be recreated 428 | * with the next decoding call. 429 | */ 430 | DRACOLoader.releaseDecoderModule = function () { 431 | DRACOLoader.decoderModulePromise = null; 432 | }; 433 | 434 | /** 435 | * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule 436 | * after testing for browser support. Returns Promise that resolves when 437 | * module is available. 438 | * @return {Promise<{decoder: DracoDecoderModule}>} 439 | */ 440 | DRACOLoader.getDecoderModule = function () { 441 | var scope = this; 442 | var path = DRACOLoader.decoderPath; 443 | var config = DRACOLoader.decoderConfig; 444 | var promise = DRACOLoader.decoderModulePromise; 445 | 446 | if ( promise ) return promise; 447 | 448 | // Load source files. 449 | if ( typeof DracoDecoderModule !== 'undefined' ) { 450 | // Loaded externally. 451 | promise = Promise.resolve(); 452 | } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) { 453 | // Load with asm.js. 454 | promise = DRACOLoader._loadScript( path + 'draco_decoder.js' ); 455 | } else { 456 | // Load with WebAssembly. 457 | config.wasmBinaryFile = path + 'draco_decoder.wasm'; 458 | promise = DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' ) 459 | .then( function () { 460 | return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile ); 461 | } ) 462 | .then( function ( wasmBinary ) { 463 | config.wasmBinary = wasmBinary; 464 | } ); 465 | } 466 | 467 | // Wait for source files, then create and return a decoder. 468 | promise = promise.then( function () { 469 | return new Promise( function ( resolve ) { 470 | config.onModuleLoaded = function ( decoder ) { 471 | scope.timeLoaded = performance.now(); 472 | // Module is Promise-like. Wrap before resolving to avoid loop. 473 | resolve( { decoder: decoder } ); 474 | }; 475 | DracoDecoderModule( config ); 476 | } ); 477 | } ); 478 | 479 | DRACOLoader.decoderModulePromise = promise; 480 | return promise; 481 | }; 482 | 483 | /** 484 | * @param {string} src 485 | * @return {Promise} 486 | */ 487 | DRACOLoader._loadScript = function ( src ) { 488 | var prevScript = document.getElementById( 'decoder_script' ); 489 | if ( prevScript !== null ) { 490 | prevScript.parentNode.removeChild( prevScript ); 491 | } 492 | var head = document.getElementsByTagName( 'head' )[ 0 ]; 493 | var script = document.createElement( 'script' ); 494 | script.id = 'decoder_script'; 495 | script.type = 'text/javascript'; 496 | script.src = src; 497 | return new Promise( function ( resolve ) { 498 | script.onload = resolve; 499 | head.appendChild( script ); 500 | }); 501 | }; 502 | 503 | /** 504 | * @param {string} src 505 | * @return {Promise} 506 | */ 507 | DRACOLoader._loadArrayBuffer = function ( src ) { 508 | var loader = new THREE.FileLoader(); 509 | loader.setResponseType( 'arraybuffer' ); 510 | return new Promise( function( resolve, reject ) { 511 | loader.load( src, resolve, undefined, reject ); 512 | }); 513 | }; 514 | 515 | export { DRACOLoader } 516 | -------------------------------------------------------------------------------- /src/loaders/ModelLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Garrett Johnson / http://gkjohnson.github.io/ 3 | * https://github.com/gkjohnson/threejs-model-loader 4 | */ 5 | import * as THREE from 'three'; 6 | 7 | import {GLTFLoader, DRACOLoader} from "./index"; 8 | 9 | const ModelLoader = function (manager) { 10 | 11 | this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager; 12 | 13 | this.cachedLoaders = {}; 14 | this.loaderMap = { 15 | 16 | '3mf': '3MFLoader', 17 | 'amf': 'AMFLoader', 18 | 'bvh': 'BVHLoader', 19 | 'assimp': 'AssimpLoader', 20 | 'babylon': 'BabylonLoader', 21 | 'dae': 'ColladaLoader', 22 | 'drc': 'DRACOLoader', 23 | 'fbx': 'FBXLoader', 24 | 'gcode': 'GCodeLoader', 25 | 'gltf': 'GLTFLoader', 26 | 'glb': 'GLTFLoader', 27 | 'bin': 'GLTFLoader', 28 | 'kmz': 'KMZLoader', 29 | 'md2': 'MD2Loader', 30 | 'mmd': 'MMDLoader', 31 | 'obj': 'OBJLoader', 32 | 'ply': 'PLYLoader', 33 | 'pcd': 'PCDLoader', 34 | 'prwm': 'PRWMLoader', 35 | 'stl': 'STLLoader', 36 | 'tds': 'TDSLoader', 37 | 'vtk': 'VTKLoader', 38 | 'vtp': 'VTKLoader', 39 | 'x': 'XLoader', 40 | 'zae': 'ColladaArchiveLoader', 41 | 42 | }; 43 | 44 | this.loaderClass = { 45 | 'GLTFLoader': GLTFLoader 46 | } 47 | 48 | }; 49 | 50 | ModelLoader.prototype = { 51 | 52 | constructor: GLTFLoader, 53 | 54 | formResult: function (res, extension) { 55 | 56 | const mat = new THREE.MeshBasicMaterial({color: 0xffffff}); 57 | let model = res.scene || res.object || res; 58 | model = model.isBufferGeometry || model.isGeometry ? new THREE.Mesh(model, mat) : model; 59 | 60 | return { 61 | 62 | model, 63 | extension, 64 | originalResult: res 65 | 66 | }; 67 | 68 | }, 69 | 70 | getLoader: function (loaderName, manager, loadercb) { 71 | let loader = null; 72 | switch (loaderName) { 73 | case 'GLTFLoader': 74 | DRACOLoader.setDecoderPath('./js/libs/draco/gltf/'); 75 | loader = new this.loaderClass[loaderName](); 76 | loader.setDRACOLoader(new DRACOLoader()); 77 | break; 78 | default: 79 | } 80 | loadercb(loader); 81 | 82 | }, 83 | 84 | extToLoader: function (ext, maanger, loadercb, onError) { 85 | 86 | // Get the name of the loader we need 87 | ext = ext ? ext.toLowerCase() : null; 88 | var loaderName = this.loaderMap[ext] || null; 89 | if (loaderName == null) { 90 | 91 | onError(new Error(`Model Loader : No loader specified for '${ ext }' extension`)); 92 | 93 | return; 94 | 95 | } 96 | 97 | // If the loader isn't already cached the lets load it 98 | var loader = this.cachedLoaders[loaderName] || null; 99 | 100 | if (loader != null) { 101 | 102 | loadercb(loader); 103 | 104 | } else { 105 | 106 | this.getLoader(loaderName, this.manager, loader => { 107 | 108 | this.cachedLoaders[loaderName] = loader; 109 | loadercb(loader); 110 | 111 | }); 112 | 113 | } 114 | 115 | }, 116 | 117 | load: function (url, onLoad, onProgress, onError, extOverride = null) { 118 | 119 | onError = onError || (e => console.error(e)); 120 | 121 | // Get the extension associated the file so we can get the 122 | // appropriate loader 123 | var extMatches = url.match(/\.([^\.\/\\]+)$/); 124 | var urlext = extMatches ? extMatches[1] : null; 125 | var ext = extOverride || urlext; 126 | 127 | if (url == null) { 128 | 129 | onError(new Error('Model Loader : No file extension found')); 130 | return; 131 | 132 | } 133 | 134 | this.extToLoader(ext, this.manager, loader => { 135 | 136 | loader.load(url, res => { 137 | 138 | onLoad(this.formResult(res)); 139 | 140 | }, onProgress, onError); 141 | 142 | }, onError); 143 | 144 | }, 145 | 146 | parse: function (data, ext, onLoad, onError) { 147 | 148 | onError = onError || (e => console.error(e)); 149 | 150 | this.extToLoader(ext, this.manager, loader => { 151 | 152 | onLoad(this.formResult(loader.parse(data))); 153 | 154 | }, onError); 155 | 156 | } 157 | 158 | }; 159 | 160 | export {ModelLoader} 161 | -------------------------------------------------------------------------------- /src/loaders/index.js: -------------------------------------------------------------------------------- 1 | import { DDSLoader } from './DDSLoader'; 2 | import { DRACOLoader } from './DRACOLoader'; 3 | import { GLTFLoader } from './GLTFLoader'; 4 | import { ModelLoader } from "./ModelLoader"; 5 | 6 | export default { 7 | ModelLoader, 8 | DDSLoader, 9 | DRACOLoader, 10 | GLTFLoader 11 | } 12 | 13 | export { 14 | ModelLoader, 15 | DDSLoader, 16 | DRACOLoader, 17 | GLTFLoader 18 | } 19 | -------------------------------------------------------------------------------- /src/mixins/base-mixin.vue: -------------------------------------------------------------------------------- 1 | 523 | 524 | 828 | 829 | 889 | -------------------------------------------------------------------------------- /src/utils/BufferGeometryUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | import * as THREE from 'three'; 5 | 6 | const BufferGeometryUtils = { 7 | 8 | computeTangents: function ( geometry ) { 9 | 10 | var index = geometry.index; 11 | var attributes = geometry.attributes; 12 | 13 | // based on http://www.terathon.com/code/tangent.html 14 | // (per vertex tangents) 15 | 16 | if ( index === null || 17 | attributes.position === undefined || 18 | attributes.normal === undefined || 19 | attributes.uv === undefined ) { 20 | 21 | console.warn( 'THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); 22 | return; 23 | 24 | } 25 | 26 | var indices = index.array; 27 | var positions = attributes.position.array; 28 | var normals = attributes.normal.array; 29 | var uvs = attributes.uv.array; 30 | 31 | var nVertices = positions.length / 3; 32 | 33 | if ( attributes.tangent === undefined ) { 34 | 35 | geometry.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); 36 | 37 | } 38 | 39 | var tangents = attributes.tangent.array; 40 | 41 | var tan1 = [], tan2 = []; 42 | 43 | for ( let i = 0; i < nVertices; i ++ ) { 44 | 45 | tan1[ i ] = new THREE.Vector3(); 46 | tan2[ i ] = new THREE.Vector3(); 47 | 48 | } 49 | 50 | var vA = new THREE.Vector3(), 51 | vB = new THREE.Vector3(), 52 | vC = new THREE.Vector3(), 53 | 54 | uvA = new THREE.Vector2(), 55 | uvB = new THREE.Vector2(), 56 | uvC = new THREE.Vector2(), 57 | 58 | sdir = new THREE.Vector3(), 59 | tdir = new THREE.Vector3(); 60 | 61 | function handleTriangle( a, b, c ) { 62 | 63 | vA.fromArray( positions, a * 3 ); 64 | vB.fromArray( positions, b * 3 ); 65 | vC.fromArray( positions, c * 3 ); 66 | 67 | uvA.fromArray( uvs, a * 2 ); 68 | uvB.fromArray( uvs, b * 2 ); 69 | uvC.fromArray( uvs, c * 2 ); 70 | 71 | var x1 = vB.x - vA.x; 72 | var x2 = vC.x - vA.x; 73 | 74 | var y1 = vB.y - vA.y; 75 | var y2 = vC.y - vA.y; 76 | 77 | var z1 = vB.z - vA.z; 78 | var z2 = vC.z - vA.z; 79 | 80 | var s1 = uvB.x - uvA.x; 81 | var s2 = uvC.x - uvA.x; 82 | 83 | var t1 = uvB.y - uvA.y; 84 | var t2 = uvC.y - uvA.y; 85 | 86 | var r = 1.0 / ( s1 * t2 - s2 * t1 ); 87 | 88 | sdir.set( 89 | ( t2 * x1 - t1 * x2 ) * r, 90 | ( t2 * y1 - t1 * y2 ) * r, 91 | ( t2 * z1 - t1 * z2 ) * r 92 | ); 93 | 94 | tdir.set( 95 | ( s1 * x2 - s2 * x1 ) * r, 96 | ( s1 * y2 - s2 * y1 ) * r, 97 | ( s1 * z2 - s2 * z1 ) * r 98 | ); 99 | 100 | tan1[ a ].add( sdir ); 101 | tan1[ b ].add( sdir ); 102 | tan1[ c ].add( sdir ); 103 | 104 | tan2[ a ].add( tdir ); 105 | tan2[ b ].add( tdir ); 106 | tan2[ c ].add( tdir ); 107 | 108 | } 109 | 110 | let groups = geometry.groups; 111 | 112 | if ( groups.length === 0 ) { 113 | 114 | groups = [ { 115 | start: 0, 116 | count: indices.length 117 | } ]; 118 | 119 | } 120 | 121 | for ( let i = 0, il = groups.length; i < il; ++ i ) { 122 | 123 | let group = groups[ i ]; 124 | 125 | let start = group.start; 126 | let count = group.count; 127 | 128 | for ( let j = start, jl = start + count; j < jl; j += 3 ) { 129 | 130 | handleTriangle( 131 | indices[ j + 0 ], 132 | indices[ j + 1 ], 133 | indices[ j + 2 ] 134 | ); 135 | 136 | } 137 | 138 | } 139 | 140 | var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); 141 | var n = new THREE.Vector3(), n2 = new THREE.Vector3(); 142 | var w, t, test; 143 | 144 | function handleVertex( v ) { 145 | 146 | n.fromArray( normals, v * 3 ); 147 | n2.copy( n ); 148 | 149 | t = tan1[ v ]; 150 | 151 | // Gram-Schmidt orthogonalize 152 | 153 | tmp.copy( t ); 154 | tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); 155 | 156 | // Calculate handedness 157 | 158 | tmp2.crossVectors( n2, t ); 159 | test = tmp2.dot( tan2[ v ] ); 160 | w = ( test < 0.0 ) ? - 1.0 : 1.0; 161 | 162 | tangents[ v * 4 ] = tmp.x; 163 | tangents[ v * 4 + 1 ] = tmp.y; 164 | tangents[ v * 4 + 2 ] = tmp.z; 165 | tangents[ v * 4 + 3 ] = w; 166 | 167 | } 168 | 169 | for ( let i = 0, il = groups.length; i < il; ++ i ) { 170 | 171 | let group = groups[ i ]; 172 | 173 | let start = group.start; 174 | let count = group.count; 175 | 176 | for ( var j = start, jl = start + count; j < jl; j += 3 ) { 177 | 178 | handleVertex( indices[ j + 0 ] ); 179 | handleVertex( indices[ j + 1 ] ); 180 | handleVertex( indices[ j + 2 ] ); 181 | 182 | } 183 | 184 | } 185 | 186 | }, 187 | 188 | /** 189 | * @param {Array} geometries 190 | * @return {THREE.BufferGeometry} 191 | */ 192 | mergeBufferGeometries: function ( geometries, useGroups ) { 193 | 194 | var isIndexed = geometries[ 0 ].index !== null; 195 | 196 | var attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) ); 197 | var morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) ); 198 | 199 | var attributes = {}; 200 | var morphAttributes = {}; 201 | 202 | var mergedGeometry = new THREE.BufferGeometry(); 203 | 204 | var offset = 0; 205 | 206 | for ( let i = 0; i < geometries.length; ++ i ) { 207 | 208 | var geometry = geometries[ i ]; 209 | 210 | // ensure that all geometries are indexed, or none 211 | 212 | if ( isIndexed !== ( geometry.index !== null ) ) return null; 213 | 214 | // gather attributes, exit early if they're different 215 | 216 | for ( let name in geometry.attributes ) { 217 | 218 | if ( ! attributesUsed.has( name ) ) return null; 219 | 220 | if ( attributes[ name ] === undefined ) attributes[ name ] = []; 221 | 222 | attributes[ name ].push( geometry.attributes[ name ] ); 223 | 224 | } 225 | 226 | // gather morph attributes, exit early if they're different 227 | 228 | for ( let name in geometry.morphAttributes ) { 229 | 230 | if ( ! morphAttributesUsed.has( name ) ) return null; 231 | 232 | if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = []; 233 | 234 | morphAttributes[ name ].push( geometry.morphAttributes[ name ] ); 235 | 236 | } 237 | 238 | // gather .userData 239 | 240 | mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || []; 241 | mergedGeometry.userData.mergedUserData.push( geometry.userData ); 242 | 243 | if ( useGroups ) { 244 | 245 | var count; 246 | 247 | if ( isIndexed ) { 248 | 249 | count = geometry.index.count; 250 | 251 | } else if ( geometry.attributes.position !== undefined ) { 252 | 253 | count = geometry.attributes.position.count; 254 | 255 | } else { 256 | 257 | return null; 258 | 259 | } 260 | 261 | mergedGeometry.addGroup( offset, count, i ); 262 | 263 | offset += count; 264 | 265 | } 266 | 267 | } 268 | 269 | // merge indices 270 | 271 | if ( isIndexed ) { 272 | 273 | var indexOffset = 0; 274 | var mergedIndex = []; 275 | 276 | for ( let i = 0; i < geometries.length; ++ i ) { 277 | 278 | let index = geometries[ i ].index; 279 | 280 | for ( let j = 0; j < index.count; ++ j ) { 281 | 282 | mergedIndex.push( index.getX( j ) + indexOffset ); 283 | 284 | } 285 | 286 | indexOffset += geometries[ i ].attributes.position.count; 287 | 288 | } 289 | 290 | mergedGeometry.setIndex( mergedIndex ); 291 | 292 | } 293 | 294 | // merge attributes 295 | 296 | for ( let name in attributes ) { 297 | 298 | var mergedAttribute = this.mergeBufferAttributes( attributes[ name ] ); 299 | 300 | if ( ! mergedAttribute ) return null; 301 | 302 | mergedGeometry.addAttribute( name, mergedAttribute ); 303 | 304 | } 305 | 306 | // merge morph attributes 307 | 308 | for ( let name in morphAttributes ) { 309 | 310 | var numMorphTargets = morphAttributes[ name ][ 0 ].length; 311 | 312 | if ( numMorphTargets === 0 ) break; 313 | 314 | mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {}; 315 | mergedGeometry.morphAttributes[ name ] = []; 316 | 317 | for ( let i = 0; i < numMorphTargets; ++ i ) { 318 | 319 | var morphAttributesToMerge = []; 320 | 321 | for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) { 322 | 323 | morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] ); 324 | 325 | } 326 | 327 | var mergedMorphAttribute = this.mergeBufferAttributes( morphAttributesToMerge ); 328 | 329 | if ( ! mergedMorphAttribute ) return null; 330 | 331 | mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute ); 332 | 333 | } 334 | 335 | } 336 | 337 | return mergedGeometry; 338 | 339 | }, 340 | 341 | /** 342 | * @param {Array} attributes 343 | * @return {THREE.BufferAttribute} 344 | */ 345 | mergeBufferAttributes: function ( attributes ) { 346 | 347 | var TypedArray; 348 | var itemSize; 349 | var normalized; 350 | var arrayLength = 0; 351 | 352 | for ( let i = 0; i < attributes.length; ++ i ) { 353 | 354 | var attribute = attributes[ i ]; 355 | 356 | if ( attribute.isInterleavedBufferAttribute ) return null; 357 | 358 | if ( TypedArray === undefined ) TypedArray = attribute.array.constructor; 359 | if ( TypedArray !== attribute.array.constructor ) return null; 360 | 361 | if ( itemSize === undefined ) itemSize = attribute.itemSize; 362 | if ( itemSize !== attribute.itemSize ) return null; 363 | 364 | if ( normalized === undefined ) normalized = attribute.normalized; 365 | if ( normalized !== attribute.normalized ) return null; 366 | 367 | arrayLength += attribute.array.length; 368 | 369 | } 370 | 371 | var array = new TypedArray( arrayLength ); 372 | var offset = 0; 373 | 374 | for ( let i = 0; i < attributes.length; ++ i ) { 375 | 376 | array.set( attributes[ i ].array, offset ); 377 | 378 | offset += attributes[ i ].array.length; 379 | 380 | } 381 | 382 | return new THREE.BufferAttribute( array, itemSize, normalized ); 383 | 384 | } 385 | 386 | }; 387 | 388 | export { BufferGeometryUtils } 389 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import {Box3, Vector3} from 'three'; 2 | import {BufferGeometryUtils} from './BufferGeometryUtils' 3 | 4 | 5 | const box = new Box3(); 6 | 7 | function getSize(object) { 8 | box.setFromObject(object); 9 | return box.getSize(new Vector3()); 10 | } 11 | 12 | function getCenter(object) { 13 | box.setFromObject(object); 14 | return box.getCenter(new Vector3()); 15 | } 16 | 17 | export default { 18 | BufferGeometryUtils, 19 | getSize, getCenter 20 | } 21 | 22 | export { 23 | BufferGeometryUtils, 24 | getSize, getCenter 25 | } 26 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /*css: { extract: false }*/ 3 | baseUrl: process.env.NODE_ENV === 'production' 4 | ? '/vue-3dmodel-viewer/' 5 | : '/' 6 | } 7 | --------------------------------------------------------------------------------