├── assets └── suzanne.3ds ├── .editorconfig ├── src ├── style │ └── main.css ├── javascript │ ├── index.js │ ├── Utils │ │ ├── Time.js │ │ ├── Sizes.js │ │ └── EventEmitter.js │ ├── Application.js │ └── Loaders │ │ ├── OBJLoader.js │ │ ├── TDSLoader.js │ │ ├── AWDLoader.js │ │ └── GLTFLoader.js └── index.html ├── readme.md ├── package.json ├── .gitignore └── .eslintrc.json /assets/suzanne.3ds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunosimon/three.js-template/HEAD/assets/suzanne.3ds -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/style/main.css: -------------------------------------------------------------------------------- 1 | * 2 | { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | .canvas 8 | { 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 100%; 14 | } -------------------------------------------------------------------------------- /src/javascript/index.js: -------------------------------------------------------------------------------- 1 | import Application from './Application.js' 2 | 3 | window.application = new Application({ 4 | $canvas: document.querySelector('.js-canvas'), 5 | useComposer: true 6 | }) 7 | 8 | if(module.hot) 9 | { 10 | module.hot.dispose(() => 11 | { 12 | window.application.destructor() 13 | window.application = null 14 | }) 15 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Three.js template 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/javascript/Utils/Time.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from './EventEmitter.js' 2 | 3 | export default class Time extends EventEmitter 4 | { 5 | /** 6 | * Constructor 7 | */ 8 | constructor() 9 | { 10 | super() 11 | 12 | this.start = Date.now() 13 | this.current = this.start 14 | this.elapsed = 0 15 | this.delta = 16 16 | 17 | this.tick = this.tick.bind(this) 18 | this.tick() 19 | } 20 | 21 | /** 22 | * Tick 23 | */ 24 | tick() 25 | { 26 | this.ticker = window.requestAnimationFrame(this.tick) 27 | 28 | const current = Date.now() 29 | 30 | this.delta = current - this.current 31 | this.elapsed = current - this.start 32 | this.current = current 33 | 34 | if(this.delta > 60) 35 | { 36 | this.delta = 60 37 | } 38 | 39 | this.trigger('tick') 40 | } 41 | 42 | /** 43 | * Stop 44 | */ 45 | stop() 46 | { 47 | window.cancelAnimationFrame(this.ticker) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Three.js template 2 | > Simple Three.js starter 3 | 4 | ## Features 5 | - [Parcel.js](https://parceljs.org/) 6 | - [Postprocessing](https://www.npmjs.com/package/postprocessing) 7 | - [SMAA Pass](https://vanruesc.github.io/postprocessing/public/demo/#smaa) 8 | - [Orbit control](https://www.npmjs.com/package/three-orbit-controls) 9 | - EventEmitter class (used to trigger event) 10 | - Sizes class (used to get resize events and viewport sizes) 11 | - Time class (used to get RAF events) 12 | 13 | ## Setup 14 | Download [Node.js](https://nodejs.org/en/download/). 15 | Run this followed commands: 16 | 17 | ``` bash 18 | # Just be sure that you've got parcel js on you system 19 | npm install -g parcel-bundler 20 | 21 | # Install dependencies (only for first time) 22 | npm i 23 | 24 | # Serve at localhost:1234 25 | npm run dev 26 | 27 | # Build for production in the dist/ directory 28 | npm run build 29 | ``` 30 | 31 | ## Issues 32 | If you have some issues while try to build at first time, just check that parceljs(https://parceljs.org/getting_started.html) installed on your system. 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three.js-template", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "predev": "npm run clean", 8 | "dev": "parcel src/index.html -p 1234 --open", 9 | "prebuild": "npm run clean", 10 | "build": "parcel build src/index.html --out-dir ./dist --public-url . --detailed-report", 11 | "clean": "shx rm -rf dist", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/brunosimon/three.js-template.git" 17 | }, 18 | "author": "brunosimon", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/brunosimon/three.js-template/issues" 22 | }, 23 | "homepage": "https://github.com/brunosimon/three.js-template#readme", 24 | "devDependencies": { 25 | "dat.gui": "^0.7.3", 26 | "parcel-bundler": "^1.10.3", 27 | "parcel-plugin-asset-copier": "^1.0.0", 28 | "postprocessing": "^5.2.2", 29 | "shx": "^0.3.2", 30 | "three": "^0.98.0", 31 | "three-orbit-controls": "^82.1.0" 32 | }, 33 | "assetsPath": "assets" 34 | } 35 | -------------------------------------------------------------------------------- /src/javascript/Utils/Sizes.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from './EventEmitter.js' 2 | 3 | export default class Sizes extends EventEmitter 4 | { 5 | /** 6 | * Constructor 7 | */ 8 | constructor() 9 | { 10 | super() 11 | 12 | // Viewport size 13 | this.viewport = {} 14 | this.$sizeViewport = document.createElement('div') 15 | this.$sizeViewport.style.width = '100vw' 16 | this.$sizeViewport.style.height = '100vh' 17 | this.$sizeViewport.style.position = 'absolute' 18 | this.$sizeViewport.style.top = 0 19 | this.$sizeViewport.style.left = 0 20 | this.$sizeViewport.style.pointerEvents = 'none' 21 | 22 | // Resize event 23 | this.resize = this.resize.bind(this) 24 | window.addEventListener('resize', this.resize) 25 | 26 | this.resize() 27 | } 28 | 29 | /** 30 | * Resize 31 | */ 32 | resize() 33 | { 34 | document.body.appendChild(this.$sizeViewport) 35 | this.viewport.width = this.$sizeViewport.offsetWidth 36 | this.viewport.height = this.$sizeViewport.offsetHeight 37 | document.body.removeChild(this.$sizeViewport) 38 | 39 | this.width = window.innerWidth 40 | this.height = window.innerHeight 41 | 42 | this.trigger('resize') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project build 2 | dist/ 3 | 4 | # Parcel cache 5 | .cache 6 | 7 | # Created by https://www.gitignore.io/api/vim,node,macos,visualstudiocode 8 | # Edit at https://www.gitignore.io/?templates=vim,node,macos,visualstudiocode 9 | 10 | ### macOS ### 11 | # General 12 | .DS_Store 13 | .AppleDouble 14 | .LSOverride 15 | 16 | # Icon must end with two \r 17 | Icon 18 | 19 | # Thumbnails 20 | ._* 21 | 22 | # Files that might appear in the root of a volume 23 | .DocumentRevisions-V100 24 | .fseventsd 25 | .Spotlight-V100 26 | .TemporaryItems 27 | .Trashes 28 | .VolumeIcon.icns 29 | .com.apple.timemachine.donotpresent 30 | 31 | # Directories potentially created on remote AFP share 32 | .AppleDB 33 | .AppleDesktop 34 | Network Trash Folder 35 | Temporary Items 36 | .apdisk 37 | 38 | ### Node ### 39 | # Logs 40 | logs 41 | *.log 42 | npm-debug.log* 43 | yarn-debug.log* 44 | yarn-error.log* 45 | 46 | # Runtime data 47 | pids 48 | *.pid 49 | *.seed 50 | *.pid.lock 51 | 52 | # Directory for instrumented libs generated by jscoverage/JSCover 53 | lib-cov 54 | 55 | # Coverage directory used by tools like istanbul 56 | coverage 57 | 58 | # nyc test coverage 59 | .nyc_output 60 | 61 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 62 | .grunt 63 | 64 | # Bower dependency directory (https://bower.io/) 65 | bower_components 66 | 67 | # node-waf configuration 68 | .lock-wscript 69 | 70 | # Compiled binary addons (https://nodejs.org/api/addons.html) 71 | build/Release 72 | 73 | # Dependency directories 74 | node_modules/ 75 | jspm_packages/ 76 | 77 | # TypeScript v1 declaration files 78 | typings/ 79 | 80 | # Optional npm cache directory 81 | .npm 82 | 83 | # Optional eslint cache 84 | .eslintcache 85 | 86 | # Optional REPL history 87 | .node_repl_history 88 | 89 | # Output of 'npm pack' 90 | *.tgz 91 | 92 | # Yarn Integrity file 93 | .yarn-integrity 94 | 95 | # dotenv environment variables file 96 | .env 97 | 98 | # parcel-bundler cache (https://parceljs.org/) 99 | .cache 100 | 101 | # next.js build output 102 | .next 103 | 104 | # nuxt.js build output 105 | .nuxt 106 | 107 | # vuepress build output 108 | .vuepress/dist 109 | 110 | # Serverless directories 111 | .serverless 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | ### Vim ### 117 | # Swap 118 | [._]*.s[a-v][a-z] 119 | [._]*.sw[a-p] 120 | [._]s[a-rt-v][a-z] 121 | [._]ss[a-gi-z] 122 | [._]sw[a-p] 123 | 124 | # Session 125 | Session.vim 126 | 127 | # Temporary 128 | .netrwhist 129 | *~ 130 | # Auto-generated tag files 131 | tags 132 | # Persistent undo 133 | [._]*.un~ 134 | 135 | ### VisualStudioCode ### 136 | .vscode/* 137 | !.vscode/settings.json 138 | !.vscode/tasks.json 139 | !.vscode/launch.json 140 | !.vscode/extensions.json 141 | 142 | ### VisualStudioCode Patch ### 143 | # Ignore all local history of files 144 | .history 145 | 146 | # End of https://www.gitignore.io/api/vim,node,macos,visualstudiocode 147 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": 3 | { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "extends": ["eslint:recommended"], 8 | "parserOptions": 9 | { 10 | "sourceType": "module", 11 | "ecmaFeatures":{ 12 | "experimentalObjectRestSpread": true 13 | } 14 | }, 15 | "globals": 16 | { 17 | }, 18 | "rules": 19 | { 20 | "no-unused-vars": 1, 21 | "no-console": 0, 22 | "dot-notation": 1, 23 | "eqeqeq": 1, 24 | "no-else-return": 0, 25 | "no-new-func": 1, 26 | "no-param-reassign": [1, { "props": false }], 27 | "no-useless-concat": 1, 28 | "no-useless-escape": 1, 29 | "radix": [1, "as-needed"], 30 | "no-undef": 2, 31 | "array-bracket-spacing": [1, "never"], 32 | "brace-style": [1, "allman"], 33 | "camelcase": [1, { "properties": "never" }], 34 | "comma-dangle": [1, "never"], 35 | "comma-style": [1, "last"], 36 | "func-style": [1, "expression"], 37 | "id-length": 0, 38 | "indent": [1, 4, { "SwitchCase": 1 }], 39 | "keyword-spacing": [1, { "after": false, "before": true, "overrides": { "from": { "after": true }, "return": { "after": true }, "import": { "after": true }, "case": { "after": true } } }], 40 | "max-len": 0, 41 | "new-cap": [1, { "newIsCap": true, "newIsCapExceptions": [], "capIsNew": false, "capIsNewExceptions": ["Immutable.Map", "Immutable.Set", "Immutable.List"] }], 42 | "no-array-constructor": 1, 43 | "no-bitwise": 0, 44 | "no-mixed-operators": 0, 45 | "no-nested-ternary": 0, 46 | "no-new-object": 1, 47 | "no-plusplus": 0, 48 | "no-restricted-syntax": 0, 49 | "no-trailing-spaces": 1, 50 | "no-underscore-dangle": 0, 51 | "no-unneeded-ternary": 1, 52 | "no-whitespace-before-property": 1, 53 | "object-curly-spacing": [1, "always"], 54 | "one-var": [1, "never"], 55 | "padded-blocks": [1, "never"], 56 | "quote-props": [1, "as-needed"], 57 | "quotes": [1, "single"], 58 | "semi": [1, "never"], 59 | "space-before-blocks": [1, "always"], 60 | "space-before-function-paren": [1, { "anonymous": "never", "named": "never", "asyncArrow": "never" }], 61 | "space-in-parens": [1, "never"], 62 | "space-infix-ops": 1, 63 | "spaced-comment": [1, "always"], 64 | "arrow-body-style": 0, 65 | "arrow-parens": [1, "always"], 66 | "arrow-spacing": [1, { "before": true, "after": true }], 67 | "no-confusing-arrow": 0, 68 | "no-dupe-class-members": 1, 69 | "no-duplicate-imports": 0, 70 | "no-useless-constructor": 1, 71 | "no-var": 1, 72 | "object-shorthand": 0, 73 | "prefer-const": 1, 74 | "prefer-rest-params": 1, 75 | "prefer-spread": 1, 76 | "prefer-template": 0, 77 | "template-curly-spacing": [1, "never"] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/javascript/Application.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import ThreeOrbitControls from 'three-orbit-controls' 3 | import { EffectComposer, RenderPass, EffectPass, SMAAEffect } from 'postprocessing' 4 | import * as dat from 'dat.gui' 5 | 6 | import TDSLoader from './Loaders/TDSLoader.js' 7 | import Sizes from './Utils/Sizes.js' 8 | import Time from './Utils/Time.js' 9 | 10 | const OrbitControls = ThreeOrbitControls(THREE) 11 | 12 | export default class Application 13 | { 14 | /** 15 | * Constructor 16 | */ 17 | constructor(_options) 18 | { 19 | // Options 20 | this.$canvas = _options.$canvas 21 | this.useComposer = _options.useComposer 22 | 23 | // Set up 24 | this.time = new Time() 25 | this.sizes = new Sizes() 26 | this.tdsLoader = new TDSLoader() 27 | 28 | // Load resources 29 | this.resources = {} 30 | 31 | this.resources.searchImage = new Image() 32 | this.resources.searchImage.addEventListener('load', () => 33 | { 34 | this.resources.areaImage = new Image() 35 | this.resources.areaImage.addEventListener('load', () => 36 | { 37 | this.tdsLoader.load('suzanne.3ds', (_suzanne) => 38 | { 39 | this.resources.suzanne = _suzanne.children[0] 40 | 41 | // Set environment 42 | this.setEnvironment() 43 | 44 | // Set debug 45 | this.setDebug() 46 | }) 47 | }) 48 | this.resources.areaImage.src = SMAAEffect.areaImageDataURL 49 | }) 50 | this.resources.searchImage.src = SMAAEffect.searchImageDataURL 51 | } 52 | 53 | /** 54 | * Set environments 55 | */ 56 | setEnvironment() 57 | { 58 | // Scene 59 | this.scene = new THREE.Scene() 60 | 61 | // Renderer 62 | this.renderer = new THREE.WebGLRenderer({ canvas: this.$canvas }) 63 | this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) 64 | this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) 65 | 66 | // Camera 67 | this.camera = new THREE.PerspectiveCamera(75, this.sizes.viewport.width / this.sizes.viewport.height, 1, 100) 68 | this.camera.position.set(0, 1, -3) 69 | this.camera.lookAt(new THREE.Vector3()) 70 | this.scene.add(this.camera) 71 | 72 | // Controls 73 | this.controls = new OrbitControls(this.camera, this.$canvas) 74 | 75 | // Suzanne 76 | this.resources.suzanne.geometry.rotateX(- Math.PI * 0.5) 77 | this.resources.suzanne.geometry.rotateY(Math.PI) 78 | this.suzanne = new THREE.Mesh(this.resources.suzanne.geometry, new THREE.MeshNormalMaterial()) 79 | this.scene.add(this.suzanne) 80 | 81 | // Composer 82 | this.composer = new EffectComposer(this.renderer, { depthTexture: true }) 83 | 84 | // Passes 85 | this.passes = {} 86 | this.passes.list = [] 87 | this.passes.updateRenderToScreen = () => 88 | { 89 | let enabledPassFound = false 90 | 91 | for(let i = this.passes.list.length - 1; i >= 0; i--) 92 | { 93 | const pass = this.passes.list[i] 94 | 95 | if(pass.enabled && !enabledPassFound) 96 | { 97 | pass.renderToScreen = true 98 | enabledPassFound = true 99 | } 100 | else 101 | { 102 | pass.renderToScreen = false 103 | } 104 | } 105 | } 106 | 107 | this.passes.render = new RenderPass(this.scene, this.camera) 108 | this.composer.addPass(this.passes.render) 109 | this.passes.list.push(this.passes.render) 110 | 111 | this.passes.smaa = new EffectPass(this.camera, new SMAAEffect(this.resources.searchImage, this.resources.areaImage)) 112 | this.passes.smaa.enabled = window.devicePixelRatio <= 1 113 | this.composer.addPass(this.passes.smaa) 114 | this.passes.list.push(this.passes.smaa) 115 | 116 | this.passes.updateRenderToScreen() 117 | 118 | // Time tick 119 | this.time.on('tick', () => 120 | { 121 | this.suzanne.rotation.y += 0.01 122 | 123 | // Renderer 124 | if(this.useComposer) 125 | { 126 | this.composer.render(this.scene, this.camera) 127 | } 128 | else 129 | { 130 | this.renderer.render(this.scene, this.camera) 131 | } 132 | }) 133 | 134 | // Resize event 135 | this.sizes.on('resize', () => 136 | { 137 | this.camera.aspect = this.sizes.viewport.width / this.sizes.viewport.height 138 | this.camera.updateProjectionMatrix() 139 | 140 | this.renderer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) 141 | 142 | if(this.useComposer) 143 | { 144 | for(const _pass of this.passes.list) 145 | { 146 | if(_pass.setSize) 147 | { 148 | _pass.setSize(this.sizes.viewport.width, this.sizes.viewport.height) 149 | } 150 | } 151 | this.composer.setSize(this.sizes.viewport.width, this.sizes.viewport.height) 152 | } 153 | }) 154 | } 155 | 156 | /** 157 | * Set debug 158 | */ 159 | setDebug() 160 | { 161 | this.debug = new dat.GUI() 162 | 163 | this.debug.add(this.suzanne.scale, 'x', 0.01, 10, 0.001) 164 | this.debug.add(this.suzanne.scale, 'y', 0.01, 10, 0.001) 165 | this.debug.add(this.suzanne.scale, 'z', 0.01, 10, 0.001) 166 | } 167 | 168 | /** 169 | * Destructor 170 | */ 171 | destructor() 172 | { 173 | this.time.off('tick') 174 | this.sizes.off('resize') 175 | 176 | this.controls.dispose() 177 | this.renderer.dispose() 178 | this.composer.dispose() 179 | this.debug.destroy() 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/javascript/Utils/EventEmitter.js: -------------------------------------------------------------------------------- 1 | export default class 2 | { 3 | /** 4 | * Constructor 5 | */ 6 | constructor() 7 | { 8 | this.callbacks = {} 9 | this.callbacks.base = {} 10 | } 11 | 12 | /** 13 | * On 14 | */ 15 | on(_names, callback) 16 | { 17 | const that = this 18 | 19 | // Errors 20 | if(typeof _names === 'undefined' || _names === '') 21 | { 22 | console.warn('wrong names') 23 | return false 24 | } 25 | 26 | if(typeof callback === 'undefined') 27 | { 28 | console.warn('wrong callback') 29 | return false 30 | } 31 | 32 | // Resolve names 33 | const names = this.resolveNames(_names) 34 | 35 | // Each name 36 | names.forEach(function(_name) 37 | { 38 | // Resolve name 39 | const name = that.resolveName(_name) 40 | 41 | // Create namespace if not exist 42 | if(!(that.callbacks[ name.namespace ] instanceof Object)) 43 | that.callbacks[ name.namespace ] = {} 44 | 45 | // Create callback if not exist 46 | if(!(that.callbacks[ name.namespace ][ name.value ] instanceof Array)) 47 | that.callbacks[ name.namespace ][ name.value ] = [] 48 | 49 | // Add callback 50 | that.callbacks[ name.namespace ][ name.value ].push(callback) 51 | }) 52 | 53 | return this 54 | } 55 | 56 | /** 57 | * Off 58 | */ 59 | off(_names) 60 | { 61 | const that = this 62 | 63 | // Errors 64 | if(typeof _names === 'undefined' || _names === '') 65 | { 66 | console.warn('wrong name') 67 | return false 68 | } 69 | 70 | // Resolve names 71 | const names = this.resolveNames(_names) 72 | 73 | // Each name 74 | names.forEach(function(_name) 75 | { 76 | // Resolve name 77 | const name = that.resolveName(_name) 78 | 79 | // Remove namespace 80 | if(name.namespace !== 'base' && name.value === '') 81 | { 82 | delete that.callbacks[ name.namespace ] 83 | } 84 | 85 | // Remove specific callback in namespace 86 | else 87 | { 88 | // Default 89 | if(name.namespace === 'base') 90 | { 91 | // Try to remove from each namespace 92 | for(const namespace in that.callbacks) 93 | { 94 | if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array) 95 | { 96 | delete that.callbacks[ namespace ][ name.value ] 97 | 98 | // Remove namespace if empty 99 | if(Object.keys(that.callbacks[ namespace ]).length === 0) 100 | delete that.callbacks[ namespace ] 101 | } 102 | } 103 | } 104 | 105 | // Specified namespace 106 | else if(that.callbacks[ name.namespace ] instanceof Object && that.callbacks[ name.namespace ][ name.value ] instanceof Array) 107 | { 108 | delete that.callbacks[ name.namespace ][ name.value ] 109 | 110 | // Remove namespace if empty 111 | if(Object.keys(that.callbacks[ name.namespace ]).length === 0) 112 | delete that.callbacks[ name.namespace ] 113 | } 114 | } 115 | }) 116 | 117 | return this 118 | } 119 | 120 | /** 121 | * Trigger 122 | */ 123 | trigger(_name, _args) 124 | { 125 | // Errors 126 | if(typeof _name === 'undefined' || _name === '') 127 | { 128 | console.warn('wrong name') 129 | return false 130 | } 131 | 132 | const that = this 133 | let finalResult = null 134 | let result = null 135 | 136 | // Default args 137 | const args = !(_args instanceof Array) ? [] : _args 138 | 139 | // Resolve names (should on have one event) 140 | let name = this.resolveNames(_name) 141 | 142 | // Resolve name 143 | name = this.resolveName(name[ 0 ]) 144 | 145 | // Default namespace 146 | if(name.namespace === 'base') 147 | { 148 | // Try to find callback in each namespace 149 | for(const namespace in that.callbacks) 150 | { 151 | if(that.callbacks[ namespace ] instanceof Object && that.callbacks[ namespace ][ name.value ] instanceof Array) 152 | { 153 | that.callbacks[ namespace ][ name.value ].forEach(function(callback) 154 | { 155 | result = callback.apply(that, args) 156 | 157 | if(typeof finalResult === 'undefined') 158 | { 159 | finalResult = result 160 | } 161 | }) 162 | } 163 | } 164 | } 165 | 166 | // Specified namespace 167 | else if(this.callbacks[ name.namespace ] instanceof Object) 168 | { 169 | if(name.value === '') 170 | { 171 | console.warn('wrong name') 172 | return this 173 | } 174 | 175 | that.callbacks[ name.namespace ][ name.value ].forEach(function(callback) 176 | { 177 | result = callback.apply(that, args) 178 | 179 | if(typeof finalResult === 'undefined') 180 | finalResult = result 181 | }) 182 | } 183 | 184 | return finalResult 185 | } 186 | 187 | /** 188 | * Resolve names 189 | */ 190 | resolveNames(_names) 191 | { 192 | let names = _names 193 | names = names.replace(/[^a-zA-Z0-9 ,/.]/g, '') 194 | names = names.replace(/[,/]+/g, ' ') 195 | names = names.split(' ') 196 | 197 | return names 198 | } 199 | 200 | /** 201 | * Resolve name 202 | */ 203 | resolveName(name) 204 | { 205 | const newName = {} 206 | const parts = name.split('.') 207 | 208 | newName.original = name 209 | newName.value = parts[ 0 ] 210 | newName.namespace = 'base' // Base namespace 211 | 212 | // Specified namespace 213 | if(parts.length > 1 && parts[ 1 ] !== '') 214 | { 215 | newName.namespace = parts[ 1 ] 216 | } 217 | 218 | return newName 219 | } 220 | } -------------------------------------------------------------------------------- /src/javascript/Loaders/OBJLoader.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as THREE from 'three' 3 | 4 | /** 5 | * @author mrdoob / http://mrdoob.com/ 6 | */ 7 | 8 | const OBJLoader = function (manager) { 9 | 10 | this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager; 11 | 12 | this.materials = null; 13 | 14 | this.regexp = { 15 | // v float float float 16 | vertex_pattern: /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/, 17 | // vn float float float 18 | normal_pattern: /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/, 19 | // vt float float 20 | uv_pattern: /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/, 21 | // f vertex vertex vertex 22 | face_vertex: /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/, 23 | // f vertex/uv vertex/uv vertex/uv 24 | face_vertex_uv: /^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/, 25 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal 26 | face_vertex_uv_normal: /^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/, 27 | // f vertex//normal vertex//normal vertex//normal 28 | face_vertex_normal: /^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/, 29 | // o object_name | g group_name 30 | object_pattern: /^[og]\s*(.+)?/, 31 | // s boolean 32 | smoothing_pattern: /^s\s+(\d+|on|off)/, 33 | // mtllib file_reference 34 | material_library_pattern: /^mtllib /, 35 | // usemtl material_name 36 | material_use_pattern: /^usemtl / 37 | }; 38 | }; 39 | 40 | OBJLoader.prototype = { 41 | 42 | constructor: OBJLoader, 43 | 44 | load: function load(url, onLoad, onProgress, onError) { 45 | 46 | var scope = this; 47 | 48 | var loader = new THREE.FileLoader(scope.manager); 49 | loader.setPath(this.path); 50 | loader.load(url, function (text) { 51 | 52 | onLoad(scope.parse(text)); 53 | }, onProgress, onError); 54 | }, 55 | 56 | setPath: function setPath(value) { 57 | 58 | this.path = value; 59 | }, 60 | 61 | setMaterials: function setMaterials(materials) { 62 | 63 | this.materials = materials; 64 | }, 65 | 66 | _createParserState: function _createParserState() { 67 | 68 | var state = { 69 | objects: [], 70 | object: {}, 71 | 72 | vertices: [], 73 | normals: [], 74 | uvs: [], 75 | 76 | materialLibraries: [], 77 | 78 | startObject: function startObject(name, fromDeclaration) { 79 | 80 | // If the current object (initial from reset) is not from a g/o declaration in the parsed 81 | // file. We need to use it for the first parsed g/o to keep things in sync. 82 | if (this.object && this.object.fromDeclaration === false) { 83 | 84 | this.object.name = name; 85 | this.object.fromDeclaration = fromDeclaration !== false; 86 | return; 87 | } 88 | 89 | var previousMaterial = this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined; 90 | 91 | if (this.object && typeof this.object._finalize === 'function') { 92 | 93 | this.object._finalize(true); 94 | } 95 | 96 | this.object = { 97 | name: name || '', 98 | fromDeclaration: fromDeclaration !== false, 99 | 100 | geometry: { 101 | vertices: [], 102 | normals: [], 103 | uvs: [] 104 | }, 105 | materials: [], 106 | smooth: true, 107 | 108 | startMaterial: function startMaterial(name, libraries) { 109 | 110 | var previous = this._finalize(false); 111 | 112 | // New usemtl declaration overwrites an inherited material, except if faces were declared 113 | // after the material, then it must be preserved for proper MultiMaterial continuation. 114 | if (previous && (previous.inherited || previous.groupCount <= 0)) { 115 | 116 | this.materials.splice(previous.index, 1); 117 | } 118 | 119 | var material = { 120 | index: this.materials.length, 121 | name: name || '', 122 | mtllib: Array.isArray(libraries) && libraries.length > 0 ? libraries[libraries.length - 1] : '', 123 | smooth: previous !== undefined ? previous.smooth : this.smooth, 124 | groupStart: previous !== undefined ? previous.groupEnd : 0, 125 | groupEnd: -1, 126 | groupCount: -1, 127 | inherited: false, 128 | 129 | clone: function clone(index) { 130 | var cloned = { 131 | index: typeof index === 'number' ? index : this.index, 132 | name: this.name, 133 | mtllib: this.mtllib, 134 | smooth: this.smooth, 135 | groupStart: 0, 136 | groupEnd: -1, 137 | groupCount: -1, 138 | inherited: false 139 | }; 140 | cloned.clone = this.clone.bind(cloned); 141 | return cloned; 142 | } 143 | }; 144 | 145 | this.materials.push(material); 146 | 147 | return material; 148 | }, 149 | 150 | currentMaterial: function currentMaterial() { 151 | 152 | if (this.materials.length > 0) { 153 | return this.materials[this.materials.length - 1]; 154 | } 155 | 156 | return undefined; 157 | }, 158 | 159 | _finalize: function _finalize(end) { 160 | 161 | var lastMultiMaterial = this.currentMaterial(); 162 | if (lastMultiMaterial && lastMultiMaterial.groupEnd === -1) { 163 | 164 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; 165 | lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; 166 | lastMultiMaterial.inherited = false; 167 | } 168 | 169 | // Ignore objects tail materials if no face declarations followed them before a new o/g started. 170 | if (end && this.materials.length > 1) { 171 | 172 | for (var mi = this.materials.length - 1; mi >= 0; mi--) { 173 | if (this.materials[mi].groupCount <= 0) { 174 | this.materials.splice(mi, 1); 175 | } 176 | } 177 | } 178 | 179 | // Guarantee at least one empty material, this makes the creation later more straight forward. 180 | if (end && this.materials.length === 0) { 181 | 182 | this.materials.push({ 183 | name: '', 184 | smooth: this.smooth 185 | }); 186 | } 187 | 188 | return lastMultiMaterial; 189 | } 190 | }; 191 | 192 | // Inherit previous objects material. 193 | // Spec tells us that a declared material must be set to all objects until a new material is declared. 194 | // If a usemtl declaration is encountered while this new object is being parsed, it will 195 | // overwrite the inherited material. Exception being that there was already face declarations 196 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation. 197 | 198 | if (previousMaterial && previousMaterial.name && typeof previousMaterial.clone === "function") { 199 | 200 | var declared = previousMaterial.clone(0); 201 | declared.inherited = true; 202 | this.object.materials.push(declared); 203 | } 204 | 205 | this.objects.push(this.object); 206 | }, 207 | 208 | finalize: function finalize() { 209 | 210 | if (this.object && typeof this.object._finalize === 'function') { 211 | 212 | this.object._finalize(true); 213 | } 214 | }, 215 | 216 | parseVertexIndex: function parseVertexIndex(value, len) { 217 | 218 | var index = parseInt(value, 10); 219 | return (index >= 0 ? index - 1 : index + len / 3) * 3; 220 | }, 221 | 222 | parseNormalIndex: function parseNormalIndex(value, len) { 223 | 224 | var index = parseInt(value, 10); 225 | return (index >= 0 ? index - 1 : index + len / 3) * 3; 226 | }, 227 | 228 | parseUVIndex: function parseUVIndex(value, len) { 229 | 230 | var index = parseInt(value, 10); 231 | return (index >= 0 ? index - 1 : index + len / 2) * 2; 232 | }, 233 | 234 | addVertex: function addVertex(a, b, c) { 235 | 236 | var src = this.vertices; 237 | var dst = this.object.geometry.vertices; 238 | 239 | dst.push(src[a + 0]); 240 | dst.push(src[a + 1]); 241 | dst.push(src[a + 2]); 242 | dst.push(src[b + 0]); 243 | dst.push(src[b + 1]); 244 | dst.push(src[b + 2]); 245 | dst.push(src[c + 0]); 246 | dst.push(src[c + 1]); 247 | dst.push(src[c + 2]); 248 | }, 249 | 250 | addVertexLine: function addVertexLine(a) { 251 | 252 | var src = this.vertices; 253 | var dst = this.object.geometry.vertices; 254 | 255 | dst.push(src[a + 0]); 256 | dst.push(src[a + 1]); 257 | dst.push(src[a + 2]); 258 | }, 259 | 260 | addNormal: function addNormal(a, b, c) { 261 | 262 | var src = this.normals; 263 | var dst = this.object.geometry.normals; 264 | 265 | dst.push(src[a + 0]); 266 | dst.push(src[a + 1]); 267 | dst.push(src[a + 2]); 268 | dst.push(src[b + 0]); 269 | dst.push(src[b + 1]); 270 | dst.push(src[b + 2]); 271 | dst.push(src[c + 0]); 272 | dst.push(src[c + 1]); 273 | dst.push(src[c + 2]); 274 | }, 275 | 276 | addUV: function addUV(a, b, c) { 277 | 278 | var src = this.uvs; 279 | var dst = this.object.geometry.uvs; 280 | 281 | dst.push(src[a + 0]); 282 | dst.push(src[a + 1]); 283 | dst.push(src[b + 0]); 284 | dst.push(src[b + 1]); 285 | dst.push(src[c + 0]); 286 | dst.push(src[c + 1]); 287 | }, 288 | 289 | addUVLine: function addUVLine(a) { 290 | 291 | var src = this.uvs; 292 | var dst = this.object.geometry.uvs; 293 | 294 | dst.push(src[a + 0]); 295 | dst.push(src[a + 1]); 296 | }, 297 | 298 | addFace: function addFace(a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd) { 299 | 300 | var vLen = this.vertices.length; 301 | 302 | var ia = this.parseVertexIndex(a, vLen); 303 | var ib = this.parseVertexIndex(b, vLen); 304 | var ic = this.parseVertexIndex(c, vLen); 305 | var id; 306 | 307 | if (d === undefined) { 308 | 309 | this.addVertex(ia, ib, ic); 310 | } else { 311 | 312 | id = this.parseVertexIndex(d, vLen); 313 | 314 | this.addVertex(ia, ib, id); 315 | this.addVertex(ib, ic, id); 316 | } 317 | 318 | if (ua !== undefined) { 319 | 320 | var uvLen = this.uvs.length; 321 | 322 | ia = this.parseUVIndex(ua, uvLen); 323 | ib = this.parseUVIndex(ub, uvLen); 324 | ic = this.parseUVIndex(uc, uvLen); 325 | 326 | if (d === undefined) { 327 | 328 | this.addUV(ia, ib, ic); 329 | } else { 330 | 331 | id = this.parseUVIndex(ud, uvLen); 332 | 333 | this.addUV(ia, ib, id); 334 | this.addUV(ib, ic, id); 335 | } 336 | } 337 | 338 | if (na !== undefined) { 339 | 340 | // Normals are many times the same. If so, skip function call and parseInt. 341 | var nLen = this.normals.length; 342 | ia = this.parseNormalIndex(na, nLen); 343 | 344 | ib = na === nb ? ia : this.parseNormalIndex(nb, nLen); 345 | ic = na === nc ? ia : this.parseNormalIndex(nc, nLen); 346 | 347 | if (d === undefined) { 348 | 349 | this.addNormal(ia, ib, ic); 350 | } else { 351 | 352 | id = this.parseNormalIndex(nd, nLen); 353 | 354 | this.addNormal(ia, ib, id); 355 | this.addNormal(ib, ic, id); 356 | } 357 | } 358 | }, 359 | 360 | addLineGeometry: function addLineGeometry(vertices, uvs) { 361 | 362 | this.object.geometry.type = 'Line'; 363 | 364 | var vLen = this.vertices.length; 365 | var uvLen = this.uvs.length; 366 | 367 | for (var vi = 0, l = vertices.length; vi < l; vi++) { 368 | 369 | this.addVertexLine(this.parseVertexIndex(vertices[vi], vLen)); 370 | } 371 | 372 | for (var uvi = 0, l = uvs.length; uvi < l; uvi++) { 373 | 374 | this.addUVLine(this.parseUVIndex(uvs[uvi], uvLen)); 375 | } 376 | } 377 | 378 | }; 379 | 380 | state.startObject('', false); 381 | 382 | return state; 383 | }, 384 | 385 | parse: function parse(text) { 386 | 387 | console.time('OBJLoader'); 388 | 389 | var state = this._createParserState(); 390 | 391 | if (text.indexOf('\r\n') !== -1) { 392 | 393 | // This is faster than String.split with regex that splits on both 394 | text = text.replace(/\r\n/g, '\n'); 395 | } 396 | 397 | if (text.indexOf('\\\n') !== -1) { 398 | 399 | // join lines separated by a line continuation character (\) 400 | text = text.replace(/\\\n/g, ''); 401 | } 402 | 403 | var lines = text.split('\n'); 404 | var line = '', 405 | lineFirstChar = '', 406 | lineSecondChar = ''; 407 | var lineLength = 0; 408 | var result = []; 409 | 410 | // Faster to just trim left side of the line. Use if available. 411 | var trimLeft = typeof ''.trimLeft === 'function'; 412 | 413 | for (var i = 0, l = lines.length; i < l; i++) { 414 | 415 | line = lines[i]; 416 | 417 | line = trimLeft ? line.trimLeft() : line.trim(); 418 | 419 | lineLength = line.length; 420 | 421 | if (lineLength === 0) continue; 422 | 423 | lineFirstChar = line.charAt(0); 424 | 425 | // @todo invoke passed in handler if any 426 | if (lineFirstChar === '#') continue; 427 | 428 | if (lineFirstChar === 'v') { 429 | 430 | lineSecondChar = line.charAt(1); 431 | 432 | if (lineSecondChar === ' ' && (result = this.regexp.vertex_pattern.exec(line)) !== null) { 433 | 434 | // 0 1 2 3 435 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 436 | 437 | state.vertices.push(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])); 438 | } else if (lineSecondChar === 'n' && (result = this.regexp.normal_pattern.exec(line)) !== null) { 439 | 440 | // 0 1 2 3 441 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 442 | 443 | state.normals.push(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])); 444 | } else if (lineSecondChar === 't' && (result = this.regexp.uv_pattern.exec(line)) !== null) { 445 | 446 | // 0 1 2 447 | // ["vt 0.1 0.2", "0.1", "0.2"] 448 | 449 | state.uvs.push(parseFloat(result[1]), parseFloat(result[2])); 450 | } else { 451 | 452 | throw new Error("Unexpected vertex/normal/uv line: '" + line + "'"); 453 | } 454 | } else if (lineFirstChar === "f") { 455 | 456 | if ((result = this.regexp.face_vertex_uv_normal.exec(line)) !== null) { 457 | 458 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal 459 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 460 | // ["f 1/1/1 2/2/2 3/3/3", "1", "1", "1", "2", "2", "2", "3", "3", "3", undefined, undefined, undefined] 461 | 462 | state.addFace(result[1], result[4], result[7], result[10], result[2], result[5], result[8], result[11], result[3], result[6], result[9], result[12]); 463 | } else if ((result = this.regexp.face_vertex_uv.exec(line)) !== null) { 464 | 465 | // f vertex/uv vertex/uv vertex/uv 466 | // 0 1 2 3 4 5 6 7 8 467 | // ["f 1/1 2/2 3/3", "1", "1", "2", "2", "3", "3", undefined, undefined] 468 | 469 | state.addFace(result[1], result[3], result[5], result[7], result[2], result[4], result[6], result[8]); 470 | } else if ((result = this.regexp.face_vertex_normal.exec(line)) !== null) { 471 | 472 | // f vertex//normal vertex//normal vertex//normal 473 | // 0 1 2 3 4 5 6 7 8 474 | // ["f 1//1 2//2 3//3", "1", "1", "2", "2", "3", "3", undefined, undefined] 475 | 476 | state.addFace(result[1], result[3], result[5], result[7], undefined, undefined, undefined, undefined, result[2], result[4], result[6], result[8]); 477 | } else if ((result = this.regexp.face_vertex.exec(line)) !== null) { 478 | 479 | // f vertex vertex vertex 480 | // 0 1 2 3 4 481 | // ["f 1 2 3", "1", "2", "3", undefined] 482 | 483 | state.addFace(result[1], result[2], result[3], result[4]); 484 | } else { 485 | 486 | throw new Error("Unexpected face line: '" + line + "'"); 487 | } 488 | } else if (lineFirstChar === "l") { 489 | 490 | var lineParts = line.substring(1).trim().split(" "); 491 | var lineVertices = [], 492 | lineUVs = []; 493 | 494 | if (line.indexOf("/") === -1) { 495 | 496 | lineVertices = lineParts; 497 | } else { 498 | 499 | for (var li = 0, llen = lineParts.length; li < llen; li++) { 500 | 501 | var parts = lineParts[li].split("/"); 502 | 503 | if (parts[0] !== "") lineVertices.push(parts[0]); 504 | if (parts[1] !== "") lineUVs.push(parts[1]); 505 | } 506 | } 507 | state.addLineGeometry(lineVertices, lineUVs); 508 | } else if ((result = this.regexp.object_pattern.exec(line)) !== null) { 509 | 510 | // o object_name 511 | // or 512 | // g group_name 513 | 514 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869 515 | // var name = result[ 0 ].substr( 1 ).trim(); 516 | var name = (" " + result[0].substr(1).trim()).substr(1); 517 | 518 | state.startObject(name); 519 | } else if (this.regexp.material_use_pattern.test(line)) { 520 | 521 | // material 522 | 523 | state.object.startMaterial(line.substring(7).trim(), state.materialLibraries); 524 | } else if (this.regexp.material_library_pattern.test(line)) { 525 | 526 | // mtl file 527 | 528 | state.materialLibraries.push(line.substring(7).trim()); 529 | } else if ((result = this.regexp.smoothing_pattern.exec(line)) !== null) { 530 | 531 | // smooth shading 532 | 533 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry, 534 | // but does not define a usemtl for each face set. 535 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups). 536 | // This requires some care to not create extra material on each smooth value for "normal" obj files. 537 | // where explicit usemtl defines geometry groups. 538 | // Example asset: examples/models/obj/cerberus/Cerberus.obj 539 | 540 | var value = result[1].trim().toLowerCase(); 541 | state.object.smooth = value === '1' || value === 'on'; 542 | 543 | var material = state.object.currentMaterial(); 544 | if (material) { 545 | 546 | material.smooth = state.object.smooth; 547 | } 548 | } else { 549 | 550 | // Handle null terminated files without exception 551 | if (line === '\0') continue; 552 | 553 | throw new Error("Unexpected line: '" + line + "'"); 554 | } 555 | } 556 | 557 | state.finalize(); 558 | 559 | var container = new THREE.Group(); 560 | container.materialLibraries = [].concat(state.materialLibraries); 561 | 562 | for (var i = 0, l = state.objects.length; i < l; i++) { 563 | 564 | var object = state.objects[i]; 565 | var geometry = object.geometry; 566 | var materials = object.materials; 567 | var isLine = geometry.type === 'Line'; 568 | 569 | // Skip o/g line declarations that did not follow with any faces 570 | if (geometry.vertices.length === 0) continue; 571 | 572 | var buffergeometry = new THREE.BufferGeometry(); 573 | 574 | buffergeometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(geometry.vertices), 3)); 575 | 576 | if (geometry.normals.length > 0) { 577 | 578 | buffergeometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(geometry.normals), 3)); 579 | } else { 580 | 581 | buffergeometry.computeVertexNormals(); 582 | } 583 | 584 | if (geometry.uvs.length > 0) { 585 | 586 | buffergeometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(geometry.uvs), 2)); 587 | } 588 | 589 | // Create materials 590 | 591 | var createdMaterials = []; 592 | 593 | for (var mi = 0, miLen = materials.length; mi < miLen; mi++) { 594 | 595 | var sourceMaterial = materials[mi]; 596 | var material = undefined; 597 | 598 | if (this.materials !== null) { 599 | 600 | material = this.materials.create(sourceMaterial.name); 601 | 602 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. 603 | if (isLine && material && !(material instanceof THREE.LineBasicMaterial)) { 604 | 605 | var materialLine = new THREE.LineBasicMaterial(); 606 | materialLine.copy(material); 607 | material = materialLine; 608 | } 609 | } 610 | 611 | if (!material) { 612 | 613 | material = !isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial(); 614 | material.name = sourceMaterial.name; 615 | } 616 | 617 | material.flatShading = !sourceMaterial.smooth; 618 | 619 | createdMaterials.push(material); 620 | } 621 | 622 | // Create mesh 623 | 624 | var mesh; 625 | 626 | if (createdMaterials.length > 1) { 627 | 628 | for (var mi = 0, miLen = materials.length; mi < miLen; mi++) { 629 | 630 | var sourceMaterial = materials[mi]; 631 | buffergeometry.addGroup(sourceMaterial.groupStart, sourceMaterial.groupCount, mi); 632 | } 633 | 634 | var multiMaterial = new THREE.MultiMaterial(createdMaterials); 635 | mesh = !isLine ? new THREE.Mesh(buffergeometry, multiMaterial) : new THREE.LineSegments(buffergeometry, multiMaterial); 636 | } else { 637 | 638 | mesh = !isLine ? new THREE.Mesh(buffergeometry, createdMaterials[0]) : new THREE.LineSegments(buffergeometry, createdMaterials[0]); 639 | } 640 | 641 | mesh.name = object.name; 642 | 643 | container.add(mesh); 644 | } 645 | 646 | console.timeEnd('OBJLoader'); 647 | 648 | return container; 649 | } 650 | 651 | }; 652 | 653 | export default OBJLoader 654 | -------------------------------------------------------------------------------- /src/javascript/Loaders/TDSLoader.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as THREE from 'three' 3 | 4 | /* 5 | * Autodesk 3DS threee.js file loader, based on lib3ds. 6 | * 7 | * Loads geometry with uv and materials basic properties with texture support. 8 | * 9 | * @author @tentone 10 | * @author @timknip 11 | * @class TDSLoader 12 | * @constructor 13 | */ 14 | 15 | const TDSLoader = function ( manager ) { 16 | 17 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 18 | this.debug = false; 19 | 20 | this.group = null; 21 | this.position = 0; 22 | 23 | this.materials = []; 24 | this.meshes = []; 25 | 26 | this.path = ""; 27 | 28 | }; 29 | 30 | TDSLoader.prototype = { 31 | 32 | constructor: TDSLoader, 33 | 34 | /** 35 | * Load 3ds file from url. 36 | * 37 | * @method load 38 | * @param {[type]} url URL for the file. 39 | * @param {Function} onLoad onLoad callback, receives group Object3D as argument. 40 | * @param {Function} onProgress onProgress callback. 41 | * @param {Function} onError onError callback. 42 | */ 43 | load : function ( url, onLoad, onProgress, onError ) { 44 | 45 | var scope = this; 46 | 47 | var loader = new THREE.FileLoader( this.manager ); 48 | 49 | loader.setResponseType( 'arraybuffer' ); 50 | 51 | loader.load( url, function ( data ) { 52 | 53 | onLoad( scope.parse( data ) ); 54 | 55 | }, onProgress, onError ); 56 | 57 | }, 58 | 59 | /** 60 | * Parse arraybuffer data and load 3ds file. 61 | * 62 | * @method parse 63 | * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. 64 | * @param {String} path Path for external resources. 65 | * @return {Object3D} Group loaded from 3ds file. 66 | */ 67 | parse : function ( arraybuffer ) { 68 | 69 | this.group = new THREE.Group(); 70 | this.position = 0; 71 | this.materials = []; 72 | this.meshes = []; 73 | 74 | this.readFile( arraybuffer ); 75 | 76 | for ( var i = 0; i < this.meshes.length; i ++ ) { 77 | 78 | this.group.add( this.meshes[ i ] ); 79 | 80 | } 81 | 82 | return this.group; 83 | 84 | }, 85 | 86 | /** 87 | * Decode file content to read 3ds data. 88 | * 89 | * @method readFile 90 | * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. 91 | */ 92 | readFile : function ( arraybuffer ) { 93 | 94 | var data = new DataView( arraybuffer ); 95 | var chunk = this.readChunk( data ); 96 | 97 | if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) { 98 | 99 | var next = this.nextChunk( data, chunk ); 100 | 101 | while ( next !== 0 ) { 102 | 103 | if ( next === M3D_VERSION ) { 104 | 105 | var version = this.readDWord( data ); 106 | this.debugMessage( '3DS file version: ' + version ); 107 | 108 | } else if ( next === MDATA ) { 109 | 110 | this.resetPosition( data ); 111 | this.readMeshData( data ); 112 | 113 | } else { 114 | 115 | this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) ); 116 | 117 | } 118 | 119 | next = this.nextChunk( data, chunk ); 120 | 121 | } 122 | 123 | } 124 | 125 | this.debugMessage( 'Parsed ' + this.meshes.length + ' meshes' ); 126 | 127 | }, 128 | 129 | /** 130 | * Read mesh data chunk. 131 | * 132 | * @method readMeshData 133 | * @param {Dataview} data Dataview in use. 134 | */ 135 | readMeshData : function ( data ) { 136 | 137 | var chunk = this.readChunk( data ); 138 | var next = this.nextChunk( data, chunk ); 139 | 140 | while ( next !== 0 ) { 141 | 142 | if ( next === MESH_VERSION ) { 143 | 144 | var version = + this.readDWord( data ); 145 | this.debugMessage( 'Mesh Version: ' + version ); 146 | 147 | } else if ( next === MASTER_SCALE ) { 148 | 149 | var scale = this.readFloat( data ); 150 | this.debugMessage( 'Master scale: ' + scale ); 151 | this.group.scale.set( scale, scale, scale ); 152 | 153 | } else if ( next === NAMED_OBJECT ) { 154 | 155 | this.debugMessage( 'Named Object' ); 156 | this.resetPosition( data ); 157 | this.readNamedObject( data ); 158 | 159 | } else if ( next === MAT_ENTRY ) { 160 | 161 | this.debugMessage( 'Material' ); 162 | this.resetPosition( data ); 163 | this.readMaterialEntry( data ); 164 | 165 | } else { 166 | 167 | this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) ); 168 | 169 | } 170 | 171 | next = this.nextChunk( data, chunk ); 172 | 173 | } 174 | 175 | }, 176 | 177 | /** 178 | * Read named object chunk. 179 | * 180 | * @method readNamedObject 181 | * @param {Dataview} data Dataview in use. 182 | */ 183 | readNamedObject : function ( data ) { 184 | 185 | var chunk = this.readChunk( data ); 186 | var name = this.readString( data, 64 ); 187 | chunk.cur = this.position; 188 | 189 | var next = this.nextChunk( data, chunk ); 190 | while ( next !== 0 ) { 191 | 192 | if ( next === N_TRI_OBJECT ) { 193 | 194 | this.resetPosition( data ); 195 | var mesh = this.readMesh( data ); 196 | mesh.name = name; 197 | this.meshes.push( mesh ); 198 | 199 | } else { 200 | 201 | this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) ); 202 | 203 | } 204 | 205 | next = this.nextChunk( data, chunk ); 206 | 207 | } 208 | 209 | this.endChunk( chunk ); 210 | 211 | }, 212 | 213 | /** 214 | * Read material data chunk and add it to the material list. 215 | * 216 | * @method readMaterialEntry 217 | * @param {Dataview} data Dataview in use. 218 | */ 219 | readMaterialEntry : function ( data ) { 220 | 221 | var chunk = this.readChunk( data ); 222 | var next = this.nextChunk( data, chunk ); 223 | var material = new THREE.MeshPhongMaterial(); 224 | 225 | while ( next !== 0 ) { 226 | 227 | if ( next === MAT_NAME ) { 228 | 229 | material.name = this.readString( data, 64 ); 230 | this.debugMessage( ' Name: ' + material.name ); 231 | 232 | } else if ( next === MAT_WIRE ) { 233 | 234 | this.debugMessage( ' Wireframe' ); 235 | material.wireframe = true; 236 | 237 | } else if ( next === MAT_WIRE_SIZE ) { 238 | 239 | var value = this.readByte( data ); 240 | material.wireframeLinewidth = value; 241 | this.debugMessage( ' Wireframe Thickness: ' + value ); 242 | 243 | } else if ( next === MAT_TWO_SIDE ) { 244 | 245 | material.side = THREE.DoubleSide; 246 | this.debugMessage( ' DoubleSided' ); 247 | 248 | } else if ( next === MAT_ADDITIVE ) { 249 | 250 | this.debugMessage( ' Additive Blending' ); 251 | material.blending = THREE.AdditiveBlending; 252 | 253 | } else if ( next === MAT_DIFFUSE ) { 254 | 255 | this.debugMessage( ' Diffuse Color' ); 256 | material.color = this.readColor( data ); 257 | 258 | } else if ( next === MAT_SPECULAR ) { 259 | 260 | this.debugMessage( ' Specular Color' ); 261 | material.specular = this.readColor( data ); 262 | 263 | } else if ( next === MAT_AMBIENT ) { 264 | 265 | this.debugMessage( ' Ambient color' ); 266 | material.color = this.readColor( data ); 267 | 268 | } else if ( next === MAT_SHININESS ) { 269 | 270 | var shininess = this.readWord( data ); 271 | material.shininess = shininess; 272 | this.debugMessage( ' Shininess : ' + shininess ); 273 | 274 | } else if( next === MAT_TEXMAP ) { 275 | 276 | this.debugMessage( ' ColorMap' ); 277 | this.resetPosition( data ); 278 | material.map = this.readMap( data ); 279 | 280 | } else if( next === MAT_BUMPMAP ) { 281 | 282 | this.debugMessage( ' BumpMap' ); 283 | this.resetPosition( data ); 284 | material.bumpMap = this.readMap( data ); 285 | 286 | } else if( next == MAT_OPACMAP ) { 287 | 288 | this.debugMessage( ' OpacityMap' ); 289 | this.resetPosition( data ); 290 | material.alphaMap = this.readMap( data ); 291 | 292 | } else if( next == MAT_SPECMAP ) { 293 | 294 | this.debugMessage( ' SpecularMap' ); 295 | this.resetPosition( data ); 296 | material.specularMap = this.readMap( data ); 297 | 298 | } else { 299 | 300 | this.debugMessage( ' Unknown material chunk: ' + next.toString( 16 ) ); 301 | 302 | } 303 | 304 | next = this.nextChunk( data, chunk ); 305 | 306 | } 307 | 308 | this.endChunk( chunk ); 309 | 310 | this.materials[ material.name ] = material; 311 | 312 | }, 313 | 314 | /** 315 | * Read mesh data chunk. 316 | * 317 | * @method readMesh 318 | * @param {Dataview} data Dataview in use. 319 | */ 320 | readMesh : function ( data ) { 321 | 322 | var chunk = this.readChunk( data ); 323 | var next = this.nextChunk( data, chunk ); 324 | 325 | var useBufferGeometry = false; 326 | var geometry = null; 327 | var uvs = []; 328 | 329 | if ( useBufferGeometry ) { 330 | 331 | geometry = new THREE.BufferGeometry(); 332 | 333 | } else { 334 | 335 | geometry = new THREE.Geometry(); 336 | 337 | } 338 | 339 | var material = new THREE.MeshPhongMaterial(); 340 | var mesh = new THREE.Mesh( geometry, material ); 341 | mesh.name = 'mesh'; 342 | 343 | while ( next !== 0 ) { 344 | 345 | if ( next === POINT_ARRAY ) { 346 | 347 | var points = this.readWord( data ); 348 | 349 | this.debugMessage( ' Vertex: ' + points ); 350 | 351 | //BufferGeometry 352 | 353 | if ( useBufferGeometry ) { 354 | 355 | var vertices = []; 356 | for ( var i = 0; i < points; i ++ ) { 357 | 358 | vertices.push( this.readFloat( data ) ); 359 | vertices.push( this.readFloat( data ) ); 360 | vertices.push( this.readFloat( data ) ); 361 | 362 | } 363 | 364 | geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) ); 365 | 366 | } else { //Geometry 367 | 368 | for ( var i = 0; i < points; i ++ ) { 369 | 370 | geometry.vertices.push( new THREE.Vector3( this.readFloat( data ), this.readFloat( data ), this.readFloat( data ) ) ); 371 | 372 | } 373 | 374 | } 375 | 376 | } else if ( next === FACE_ARRAY ) { 377 | 378 | this.resetPosition( data ); 379 | this.readFaceArray( data, mesh ); 380 | 381 | } else if ( next === TEX_VERTS ) { 382 | 383 | var texels = this.readWord( data ); 384 | 385 | this.debugMessage( ' UV: ' + texels ); 386 | 387 | //BufferGeometry 388 | 389 | if ( useBufferGeometry ) { 390 | 391 | var uvs = []; 392 | for ( var i = 0; i < texels; i ++ ) { 393 | 394 | uvs.push( this.readFloat( data ) ); 395 | uvs.push( this.readFloat( data ) ); 396 | 397 | } 398 | geometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) ); 399 | 400 | } else { //Geometry 401 | 402 | uvs = []; 403 | for ( var i = 0; i < texels; i ++ ) { 404 | 405 | uvs.push( new THREE.Vector2( this.readFloat( data ), this.readFloat( data ) ) ); 406 | 407 | } 408 | 409 | } 410 | 411 | } else if ( next === MESH_MATRIX ) { 412 | 413 | this.debugMessage( ' Tranformation Matrix (TODO)' ); 414 | 415 | var values = []; 416 | for( var i = 0; i < 12; i++ ) { 417 | 418 | values[ i ] = this.readFloat( data ); 419 | 420 | } 421 | 422 | var matrix = new THREE.Matrix4(); 423 | 424 | //X Line 425 | matrix.elements[0] = values[0]; 426 | matrix.elements[1] = values[6]; 427 | matrix.elements[2] = values[3]; 428 | matrix.elements[3] = values[9]; 429 | 430 | //Y Line 431 | matrix.elements[4] = values[2]; 432 | matrix.elements[5] = values[8]; 433 | matrix.elements[6] = values[5]; 434 | matrix.elements[7] = values[11]; 435 | 436 | //Z Line 437 | matrix.elements[8] = values[1]; 438 | matrix.elements[9] = values[7]; 439 | matrix.elements[10] = values[4]; 440 | matrix.elements[11] = values[10]; 441 | 442 | //W Line 443 | matrix.elements[12] = 0; 444 | matrix.elements[13] = 0; 445 | matrix.elements[14] = 0; 446 | matrix.elements[15] = 1; 447 | 448 | matrix.transpose(); 449 | 450 | var inverse = new THREE.Matrix4(); 451 | inverse.getInverse( matrix, true ); 452 | geometry.applyMatrix( inverse ); 453 | 454 | matrix.decompose( mesh.position, mesh.quaternion, mesh.scale ); 455 | 456 | } else { 457 | 458 | this.debugMessage( ' Unknown mesh chunk: ' + next.toString( 16 ) ); 459 | 460 | } 461 | 462 | next = this.nextChunk( data, chunk ); 463 | 464 | } 465 | 466 | this.endChunk( chunk ); 467 | 468 | if ( ! useBufferGeometry ) { 469 | 470 | //geometry.faceVertexUvs[0][faceIndex][vertexIndex] 471 | 472 | if ( uvs.length > 0 ) { 473 | 474 | var faceUV = []; 475 | 476 | for ( var i = 0; i < geometry.faces.length; i ++ ) { 477 | 478 | faceUV.push( [ uvs[ geometry.faces[ i ].a ], uvs[ geometry.faces[ i ].b ], uvs[ geometry.faces[ i ].c ] ] ); 479 | 480 | } 481 | 482 | geometry.faceVertexUvs[ 0 ] = faceUV; 483 | 484 | } 485 | 486 | geometry.computeVertexNormals(); 487 | 488 | } 489 | 490 | return mesh; 491 | 492 | }, 493 | 494 | /** 495 | * Read face array data chunk. 496 | * 497 | * @method readFaceArray 498 | * @param {Dataview} data Dataview in use. 499 | * @param {Mesh} mesh Mesh to be filled with the data read. 500 | */ 501 | readFaceArray : function ( data, mesh ) { 502 | 503 | var chunk = this.readChunk( data ); 504 | var faces = this.readWord( data ); 505 | 506 | this.debugMessage( ' Faces: ' + faces ); 507 | 508 | for ( var i = 0; i < faces; ++ i ) { 509 | 510 | mesh.geometry.faces.push( new THREE.Face3( this.readWord( data ), this.readWord( data ), this.readWord( data ) ) ); 511 | 512 | var visibility = this.readWord( data ); 513 | 514 | } 515 | 516 | //The rest of the FACE_ARRAY chunk is subchunks 517 | 518 | while ( this.position < chunk.end ) { 519 | 520 | var chunk = this.readChunk( data ); 521 | 522 | if ( chunk.id === MSH_MAT_GROUP ) { 523 | 524 | this.debugMessage( ' Material Group' ); 525 | 526 | this.resetPosition( data ); 527 | 528 | var group = this.readMaterialGroup( data ); 529 | 530 | var material = this.materials[ group.name ]; 531 | 532 | if ( material !== undefined ) { 533 | 534 | mesh.material = material; 535 | 536 | if ( material.name === '' ) { 537 | 538 | material.name = mesh.name; 539 | 540 | } 541 | 542 | } 543 | 544 | } else { 545 | 546 | this.debugMessage( ' Unknown face array chunk: ' + chunk.toString( 16 ) ); 547 | 548 | } 549 | 550 | this.endChunk( chunk ); 551 | 552 | } 553 | 554 | this.endChunk( chunk ); 555 | 556 | }, 557 | 558 | /** 559 | * Read texture map data chunk. 560 | * 561 | * @method readMap 562 | * @param {Dataview} data Dataview in use. 563 | * @return {Texture} Texture read from this data chunk. 564 | */ 565 | readMap : function( data ) { 566 | 567 | var chunk = this.readChunk( data ); 568 | var next = this.nextChunk( data, chunk ); 569 | var texture = {}; 570 | 571 | var loader = new THREE.TextureLoader(); 572 | loader.setPath( this.path ); 573 | 574 | while( next !== 0 ) { 575 | 576 | if ( next === MAT_MAPNAME ) { 577 | 578 | var name = this.readString( data, 128 ); 579 | texture = loader.load( name ); 580 | 581 | this.debugMessage( ' File: ' + this.path + name ); 582 | 583 | } 584 | else if ( next === MAT_MAP_UOFFSET) { 585 | 586 | texture.offset.x = this.readFloat( data ); 587 | this.debugMessage( ' OffsetX: ' + texture.offset.x ); 588 | 589 | } 590 | else if ( next === MAT_MAP_VOFFSET) { 591 | 592 | texture.offset.y = this.readFloat( data ); 593 | this.debugMessage( ' OffsetY: ' + texture.offset.y ); 594 | 595 | } 596 | else if ( next === MAT_MAP_USCALE) { 597 | 598 | texture.repeat.x = this.readFloat( data ); 599 | this.debugMessage( ' RepeatX: ' + texture.repeat.x ); 600 | 601 | } 602 | else if ( next === MAT_MAP_VSCALE) { 603 | 604 | texture.repeat.y = this.readFloat( data ); 605 | this.debugMessage( ' RepeatY: ' + texture.repeat.y ); 606 | 607 | } 608 | else { 609 | 610 | this.debugMessage( ' Unknown map chunk: ' + next.toString( 16 ) ); 611 | 612 | } 613 | 614 | next = this.nextChunk( data, chunk ); 615 | 616 | } 617 | 618 | this.endChunk( chunk ); 619 | 620 | return texture; 621 | 622 | }, 623 | 624 | /** 625 | * Read material group data chunk. 626 | * 627 | * @method readMaterialGroup 628 | * @param {Dataview} data Dataview in use. 629 | * @return {Object} Object with name and index of the object. 630 | */ 631 | readMaterialGroup : function ( data ) { 632 | 633 | var chunk = this.readChunk( data ); 634 | var name = this.readString( data, 64 ); 635 | var numFaces = this.readWord( data ); 636 | 637 | this.debugMessage( ' Name: ' + name ); 638 | this.debugMessage( ' Faces: ' + numFaces ); 639 | 640 | var index = []; 641 | for ( var i = 0; i < numFaces; ++ i ) { 642 | 643 | index.push( this.readWord( data ) ); 644 | 645 | } 646 | 647 | return { name: name, index: index }; 648 | 649 | }, 650 | 651 | /** 652 | * Read a color value. 653 | * 654 | * @method readColor 655 | * @param {DataView} data Dataview. 656 | * @return {Color} Color value read.. 657 | */ 658 | readColor : function ( data ) { 659 | 660 | var chunk = this.readChunk( data ); 661 | var color = new THREE.Color(); 662 | 663 | if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) { 664 | 665 | var r = this.readByte( data ); 666 | var g = this.readByte( data ); 667 | var b = this.readByte( data ); 668 | 669 | color.setRGB( r / 255, g / 255, b / 255 ); 670 | 671 | this.debugMessage( ' Color: ' + color.r + ', ' + color.g + ', ' + color.b ); 672 | 673 | } else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) { 674 | 675 | var r = this.readFloat( data ); 676 | var g = this.readFloat( data ); 677 | var b = this.readFloat( data ); 678 | 679 | color.setRGB( r, g, b ); 680 | 681 | this.debugMessage( ' Color: ' + color.r + ', ' + color.g + ', ' + color.b ); 682 | 683 | } else { 684 | 685 | this.debugMessage( ' Unknown color chunk: ' + c.toString( 16 ) ); 686 | 687 | } 688 | 689 | this.endChunk( chunk ); 690 | return color; 691 | 692 | }, 693 | 694 | /** 695 | * Read next chunk of data. 696 | * 697 | * @method readChunk 698 | * @param {DataView} data Dataview. 699 | * @return {Object} Chunk of data read. 700 | */ 701 | readChunk : function ( data ) { 702 | 703 | var chunk = {}; 704 | 705 | chunk.cur = this.position; 706 | chunk.id = this.readWord( data ); 707 | chunk.size = this.readDWord( data ); 708 | chunk.end = chunk.cur + chunk.size; 709 | chunk.cur += 6; 710 | 711 | return chunk; 712 | 713 | }, 714 | 715 | /** 716 | * Set position to the end of the current chunk of data. 717 | * 718 | * @method endChunk 719 | * @param {Object} chunk Data chunk. 720 | */ 721 | endChunk : function ( chunk ) { 722 | 723 | this.position = chunk.end; 724 | 725 | }, 726 | 727 | /** 728 | * Move to the next data chunk. 729 | * 730 | * @method nextChunk 731 | * @param {DataView} data Dataview. 732 | * @param {Object} chunk Data chunk. 733 | */ 734 | nextChunk : function ( data, chunk ) { 735 | 736 | if ( chunk.cur >= chunk.end ) { 737 | 738 | return 0; 739 | 740 | } 741 | 742 | this.position = chunk.cur; 743 | 744 | try { 745 | 746 | var next = this.readChunk( data ); 747 | chunk.cur += next.size; 748 | return next.id; 749 | 750 | } catch ( e ) { 751 | 752 | this.debugMessage( 'Unable to read chunk at ' + this.position ); 753 | return 0; 754 | 755 | } 756 | 757 | }, 758 | 759 | /** 760 | * Reset dataview position. 761 | * 762 | * @method resetPosition 763 | * @param {DataView} data Dataview. 764 | */ 765 | resetPosition : function ( data, chunk ) { 766 | 767 | this.position -= 6; 768 | 769 | }, 770 | 771 | /** 772 | * Read byte value. 773 | * 774 | * @method readByte 775 | * @param {DataView} data Dataview to read data from. 776 | * @return {Number} Data read from the dataview. 777 | */ 778 | readByte : function ( data ) { 779 | 780 | var v = data.getUint8( this.position, true ); 781 | this.position += 1; 782 | return v; 783 | 784 | }, 785 | 786 | /** 787 | * Read 32 bit float value. 788 | * 789 | * @method readFloat 790 | * @param {DataView} data Dataview to read data from. 791 | * @return {Number} Data read from the dataview. 792 | */ 793 | readFloat : function ( data ) { 794 | 795 | try { 796 | 797 | var v = data.getFloat32( this.position, true ); 798 | this.position += 4; 799 | return v; 800 | 801 | } catch ( e ) { 802 | 803 | this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength ); 804 | 805 | } 806 | 807 | }, 808 | 809 | /** 810 | * Read 32 bit signed integer value. 811 | * 812 | * @method readInt 813 | * @param {DataView} data Dataview to read data from. 814 | * @return {Number} Data read from the dataview. 815 | */ 816 | readInt : function ( data ) { 817 | 818 | var v = data.getInt32( this.position, true ); 819 | this.position += 4; 820 | return v; 821 | 822 | }, 823 | 824 | /** 825 | * Read 16 bit signed integer value. 826 | * 827 | * @method readShort 828 | * @param {DataView} data Dataview to read data from. 829 | * @return {Number} Data read from the dataview. 830 | */ 831 | readShort : function ( data ) { 832 | 833 | var v = data.getInt16( this.position, true ); 834 | this.position += 2; 835 | return v; 836 | 837 | }, 838 | 839 | /** 840 | * Read 64 bit unsigned integer value. 841 | * 842 | * @method readDWord 843 | * @param {DataView} data Dataview to read data from. 844 | * @return {Number} Data read from the dataview. 845 | */ 846 | readDWord : function ( data ) { 847 | 848 | var v = data.getUint32( this.position, true ); 849 | this.position += 4; 850 | return v; 851 | 852 | }, 853 | 854 | /** 855 | * Read 32 bit unsigned integer value. 856 | * 857 | * @method readWord 858 | * @param {DataView} data Dataview to read data from. 859 | * @return {Number} Data read from the dataview. 860 | */ 861 | readWord : function ( data ) { 862 | 863 | var v = data.getUint16( this.position, true ); 864 | this.position += 2; 865 | return v; 866 | 867 | }, 868 | 869 | /** 870 | * Read string value. 871 | * 872 | * @method readString 873 | * @param {DataView} data Dataview to read data from. 874 | * @param {Number} maxLength Max size of the string to be read. 875 | * @return {String} Data read from the dataview. 876 | */ 877 | readString : function ( data, maxLength ) { 878 | 879 | var s = ''; 880 | 881 | for ( var i = 0; i < maxLength; i ++ ) { 882 | 883 | var c = this.readByte( data ); 884 | if ( ! c ) { 885 | 886 | break; 887 | 888 | } 889 | 890 | s += String.fromCharCode( c ); 891 | 892 | } 893 | 894 | return s; 895 | 896 | }, 897 | 898 | /** 899 | * Set resource path used to determine the file path to attached resources. 900 | * 901 | * @method setPath 902 | * @param {String} path Path to resources. 903 | * @return Self for chaining. 904 | */ 905 | setPath : function ( path ) { 906 | 907 | if(path !== undefined) 908 | { 909 | this.path = path; 910 | } 911 | 912 | return this; 913 | 914 | }, 915 | 916 | /** 917 | * Print debug message to the console. 918 | * 919 | * Is controlled by a flag to show or hide debug messages. 920 | * 921 | * @method debugMessage 922 | * @param {Object} message Debug message to print to the console. 923 | */ 924 | debugMessage : function ( message ) { 925 | 926 | if ( this.debug ) { 927 | 928 | console.log( message ); 929 | 930 | } 931 | 932 | } 933 | }; 934 | 935 | var NULL_CHUNK = 0x0000; 936 | var M3DMAGIC = 0x4D4D; 937 | var SMAGIC = 0x2D2D; 938 | var LMAGIC = 0x2D3D; 939 | var MLIBMAGIC = 0x3DAA; 940 | var MATMAGIC = 0x3DFF; 941 | var CMAGIC = 0xC23D; 942 | var M3D_VERSION = 0x0002; 943 | var M3D_KFVERSION = 0x0005; 944 | var COLOR_F = 0x0010; 945 | var COLOR_24 = 0x0011; 946 | var LIN_COLOR_24 = 0x0012; 947 | var LIN_COLOR_F = 0x0013; 948 | var INT_PERCENTAGE = 0x0030; 949 | var FLOAT_PERCENTAGE = 0x0031; 950 | var MDATA = 0x3D3D; 951 | var MESH_VERSION = 0x3D3E; 952 | var MASTER_SCALE = 0x0100; 953 | var LO_SHADOW_BIAS = 0x1400; 954 | var HI_SHADOW_BIAS = 0x1410; 955 | var SHADOW_MAP_SIZE = 0x1420; 956 | var SHADOW_SAMPLES = 0x1430; 957 | var SHADOW_RANGE = 0x1440; 958 | var SHADOW_FILTER = 0x1450; 959 | var RAY_BIAS = 0x1460; 960 | var O_CONSTS = 0x1500; 961 | var AMBIENT_LIGHT = 0x2100; 962 | var BIT_MAP = 0x1100; 963 | var SOLID_BGND = 0x1200; 964 | var V_GRADIENT = 0x1300; 965 | var USE_BIT_MAP = 0x1101; 966 | var USE_SOLID_BGND = 0x1201; 967 | var USE_V_GRADIENT = 0x1301; 968 | var FOG = 0x2200; 969 | var FOG_BGND = 0x2210; 970 | var LAYER_FOG = 0x2302; 971 | var DISTANCE_CUE = 0x2300; 972 | var DCUE_BGND = 0x2310; 973 | var USE_FOG = 0x2201; 974 | var USE_LAYER_FOG = 0x2303; 975 | var USE_DISTANCE_CUE = 0x2301; 976 | var MAT_ENTRY = 0xAFFF; 977 | var MAT_NAME = 0xA000; 978 | var MAT_AMBIENT = 0xA010; 979 | var MAT_DIFFUSE = 0xA020; 980 | var MAT_SPECULAR = 0xA030; 981 | var MAT_SHININESS = 0xA040; 982 | var MAT_SHIN2PCT = 0xA041; 983 | var MAT_TRANSPARENCY = 0xA050; 984 | var MAT_XPFALL = 0xA052; 985 | var MAT_USE_XPFALL = 0xA240; 986 | var MAT_REFBLUR = 0xA053; 987 | var MAT_SHADING = 0xA100; 988 | var MAT_USE_REFBLUR = 0xA250; 989 | var MAT_SELF_ILLUM = 0xA084; 990 | var MAT_TWO_SIDE = 0xA081; 991 | var MAT_DECAL = 0xA082; 992 | var MAT_ADDITIVE = 0xA083; 993 | var MAT_WIRE = 0xA085; 994 | var MAT_FACEMAP = 0xA088; 995 | var MAT_TRANSFALLOFF_IN = 0xA08A; 996 | var MAT_PHONGSOFT = 0xA08C; 997 | var MAT_WIREABS = 0xA08E; 998 | var MAT_WIRE_SIZE = 0xA087; 999 | var MAT_TEXMAP = 0xA200; 1000 | var MAT_SXP_TEXT_DATA = 0xA320; 1001 | var MAT_TEXMASK = 0xA33E; 1002 | var MAT_SXP_TEXTMASK_DATA = 0xA32A; 1003 | var MAT_TEX2MAP = 0xA33A; 1004 | var MAT_SXP_TEXT2_DATA = 0xA321; 1005 | var MAT_TEX2MASK = 0xA340; 1006 | var MAT_SXP_TEXT2MASK_DATA = 0xA32C; 1007 | var MAT_OPACMAP = 0xA210; 1008 | var MAT_SXP_OPAC_DATA = 0xA322; 1009 | var MAT_OPACMASK = 0xA342; 1010 | var MAT_SXP_OPACMASK_DATA = 0xA32E; 1011 | var MAT_BUMPMAP = 0xA230; 1012 | var MAT_SXP_BUMP_DATA = 0xA324; 1013 | var MAT_BUMPMASK = 0xA344; 1014 | var MAT_SXP_BUMPMASK_DATA = 0xA330; 1015 | var MAT_SPECMAP = 0xA204; 1016 | var MAT_SXP_SPEC_DATA = 0xA325; 1017 | var MAT_SPECMASK = 0xA348; 1018 | var MAT_SXP_SPECMASK_DATA = 0xA332; 1019 | var MAT_SHINMAP = 0xA33C; 1020 | var MAT_SXP_SHIN_DATA = 0xA326; 1021 | var MAT_SHINMASK = 0xA346; 1022 | var MAT_SXP_SHINMASK_DATA = 0xA334; 1023 | var MAT_SELFIMAP = 0xA33D; 1024 | var MAT_SXP_SELFI_DATA = 0xA328; 1025 | var MAT_SELFIMASK = 0xA34A; 1026 | var MAT_SXP_SELFIMASK_DATA = 0xA336; 1027 | var MAT_REFLMAP = 0xA220; 1028 | var MAT_REFLMASK = 0xA34C; 1029 | var MAT_SXP_REFLMASK_DATA = 0xA338; 1030 | var MAT_ACUBIC = 0xA310; 1031 | var MAT_MAPNAME = 0xA300; 1032 | var MAT_MAP_TILING = 0xA351; 1033 | var MAT_MAP_TEXBLUR = 0xA353; 1034 | var MAT_MAP_USCALE = 0xA354; 1035 | var MAT_MAP_VSCALE = 0xA356; 1036 | var MAT_MAP_UOFFSET = 0xA358; 1037 | var MAT_MAP_VOFFSET = 0xA35A; 1038 | var MAT_MAP_ANG = 0xA35C; 1039 | var MAT_MAP_COL1 = 0xA360; 1040 | var MAT_MAP_COL2 = 0xA362; 1041 | var MAT_MAP_RCOL = 0xA364; 1042 | var MAT_MAP_GCOL = 0xA366; 1043 | var MAT_MAP_BCOL = 0xA368; 1044 | var NAMED_OBJECT = 0x4000; 1045 | var N_DIRECT_LIGHT = 0x4600; 1046 | var DL_OFF = 0x4620; 1047 | var DL_OUTER_RANGE = 0x465A; 1048 | var DL_INNER_RANGE = 0x4659; 1049 | var DL_MULTIPLIER = 0x465B; 1050 | var DL_EXCLUDE = 0x4654; 1051 | var DL_ATTENUATE = 0x4625; 1052 | var DL_SPOTLIGHT = 0x4610; 1053 | var DL_SPOT_ROLL = 0x4656; 1054 | var DL_SHADOWED = 0x4630; 1055 | var DL_LOCAL_SHADOW2 = 0x4641; 1056 | var DL_SEE_CONE = 0x4650; 1057 | var DL_SPOT_RECTANGULAR = 0x4651; 1058 | var DL_SPOT_ASPECT = 0x4657; 1059 | var DL_SPOT_PROJECTOR = 0x4653; 1060 | var DL_SPOT_OVERSHOOT = 0x4652; 1061 | var DL_RAY_BIAS = 0x4658; 1062 | var DL_RAYSHAD = 0x4627; 1063 | var N_CAMERA = 0x4700; 1064 | var CAM_SEE_CONE = 0x4710; 1065 | var CAM_RANGES = 0x4720; 1066 | var OBJ_HIDDEN = 0x4010; 1067 | var OBJ_VIS_LOFTER = 0x4011; 1068 | var OBJ_DOESNT_CAST = 0x4012; 1069 | var OBJ_DONT_RECVSHADOW = 0x4017; 1070 | var OBJ_MATTE = 0x4013; 1071 | var OBJ_FAST = 0x4014; 1072 | var OBJ_PROCEDURAL = 0x4015; 1073 | var OBJ_FROZEN = 0x4016; 1074 | var N_TRI_OBJECT = 0x4100; 1075 | var POINT_ARRAY = 0x4110; 1076 | var POINT_FLAG_ARRAY = 0x4111; 1077 | var FACE_ARRAY = 0x4120; 1078 | var MSH_MAT_GROUP = 0x4130; 1079 | var SMOOTH_GROUP = 0x4150; 1080 | var MSH_BOXMAP = 0x4190; 1081 | var TEX_VERTS = 0x4140; 1082 | var MESH_MATRIX = 0x4160; 1083 | var MESH_COLOR = 0x4165; 1084 | var MESH_TEXTURE_INFO = 0x4170; 1085 | var KFDATA = 0xB000; 1086 | var KFHDR = 0xB00A; 1087 | var KFSEG = 0xB008; 1088 | var KFCURTIME = 0xB009; 1089 | var AMBIENT_NODE_TAG = 0xB001; 1090 | var OBJECT_NODE_TAG = 0xB002; 1091 | var CAMERA_NODE_TAG = 0xB003; 1092 | var TARGET_NODE_TAG = 0xB004; 1093 | var LIGHT_NODE_TAG = 0xB005; 1094 | var L_TARGET_NODE_TAG = 0xB006; 1095 | var SPOTLIGHT_NODE_TAG = 0xB007; 1096 | var NODE_ID = 0xB030; 1097 | var NODE_HDR = 0xB010; 1098 | var PIVOT = 0xB013; 1099 | var INSTANCE_NAME = 0xB011; 1100 | var MORPH_SMOOTH = 0xB015; 1101 | var BOUNDBOX = 0xB014; 1102 | var POS_TRACK_TAG = 0xB020; 1103 | var COL_TRACK_TAG = 0xB025; 1104 | var ROT_TRACK_TAG = 0xB021; 1105 | var SCL_TRACK_TAG = 0xB022; 1106 | var MORPH_TRACK_TAG = 0xB026; 1107 | var FOV_TRACK_TAG = 0xB023; 1108 | var ROLL_TRACK_TAG = 0xB024; 1109 | var HOT_TRACK_TAG = 0xB027; 1110 | var FALL_TRACK_TAG = 0xB028; 1111 | var HIDE_TRACK_TAG = 0xB029; 1112 | var POLY_2D = 0x5000; 1113 | var SHAPE_OK = 0x5010; 1114 | var SHAPE_NOT_OK = 0x5011; 1115 | var SHAPE_HOOK = 0x5020; 1116 | var PATH_3D = 0x6000; 1117 | var PATH_MATRIX = 0x6005; 1118 | var SHAPE_2D = 0x6010; 1119 | var M_SCALE = 0x6020; 1120 | var M_TWIST = 0x6030; 1121 | var M_TEETER = 0x6040; 1122 | var M_FIT = 0x6050; 1123 | var M_BEVEL = 0x6060; 1124 | var XZ_CURVE = 0x6070; 1125 | var YZ_CURVE = 0x6080; 1126 | var INTERPCT = 0x6090; 1127 | var DEFORM_LIMIT = 0x60A0; 1128 | var USE_CONTOUR = 0x6100; 1129 | var USE_TWEEN = 0x6110; 1130 | var USE_SCALE = 0x6120; 1131 | var USE_TWIST = 0x6130; 1132 | var USE_TEETER = 0x6140; 1133 | var USE_FIT = 0x6150; 1134 | var USE_BEVEL = 0x6160; 1135 | var DEFAULT_VIEW = 0x3000; 1136 | var VIEW_TOP = 0x3010; 1137 | var VIEW_BOTTOM = 0x3020; 1138 | var VIEW_LEFT = 0x3030; 1139 | var VIEW_RIGHT = 0x3040; 1140 | var VIEW_FRONT = 0x3050; 1141 | var VIEW_BACK = 0x3060; 1142 | var VIEW_USER = 0x3070; 1143 | var VIEW_CAMERA = 0x3080; 1144 | var VIEW_WINDOW = 0x3090; 1145 | var VIEWPORT_LAYOUT_OLD = 0x7000; 1146 | var VIEWPORT_DATA_OLD = 0x7010; 1147 | var VIEWPORT_LAYOUT = 0x7001; 1148 | var VIEWPORT_DATA = 0x7011; 1149 | var VIEWPORT_DATA_3 = 0x7012; 1150 | var VIEWPORT_SIZE = 0x7020; 1151 | var NETWORK_VIEW = 0x7030; 1152 | 1153 | export default TDSLoader 1154 | -------------------------------------------------------------------------------- /src/javascript/Loaders/AWDLoader.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as THREE from 'three' 3 | 4 | /** 5 | * Author: Pierre Lepers 6 | * Date: 09/12/2013 17:21 7 | */ 8 | var UNCOMPRESSED = 0, 9 | DEFLATE = 1, 10 | LZMA = 2, 11 | 12 | AWD_FIELD_INT8 = 1, 13 | AWD_FIELD_INT16 = 2, 14 | AWD_FIELD_INT32 = 3, 15 | AWD_FIELD_UINT8 = 4, 16 | AWD_FIELD_UINT16 = 5, 17 | AWD_FIELD_UINT32 = 6, 18 | AWD_FIELD_FLOAT32 = 7, 19 | AWD_FIELD_FLOAT64 = 8, 20 | AWD_FIELD_BOOL = 21, 21 | AWD_FIELD_COLOR = 22, 22 | AWD_FIELD_BADDR = 23, 23 | AWD_FIELD_STRING = 31, 24 | AWD_FIELD_BYTEARRAY = 32, 25 | AWD_FIELD_VECTOR2x1 = 41, 26 | AWD_FIELD_VECTOR3x1 = 42, 27 | AWD_FIELD_VECTOR4x1 = 43, 28 | AWD_FIELD_MTX3x2 = 44, 29 | AWD_FIELD_MTX3x3 = 45, 30 | AWD_FIELD_MTX4x3 = 46, 31 | AWD_FIELD_MTX4x4 = 47, 32 | 33 | BOOL = 21, 34 | COLOR = 22, 35 | BADDR = 23, 36 | 37 | INT8 = 1, 38 | INT16 = 2, 39 | INT32 = 3, 40 | UINT8 = 4, 41 | UINT16 = 5, 42 | UINT32 = 6, 43 | FLOAT32 = 7, 44 | FLOAT64 = 8; 45 | 46 | var littleEndian = true; 47 | 48 | function Block() { 49 | 50 | this.id = 0; 51 | this.data = null; 52 | 53 | } 54 | 55 | function AWDProperties() {} 56 | 57 | AWDProperties.prototype = { 58 | set: function( key, value ) { 59 | 60 | this[ key ] = value; 61 | 62 | }, 63 | 64 | get: function( key, fallback ) { 65 | 66 | if ( this.hasOwnProperty( key ) ) 67 | return this[ key ]; 68 | else return fallback; 69 | 70 | } 71 | }; 72 | 73 | const AWDLoader = function ( manager ) { 74 | 75 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 76 | 77 | this.trunk = new THREE.Object3D(); 78 | 79 | this.materialFactory = undefined; 80 | 81 | this._url = ''; 82 | this._baseDir = ''; 83 | 84 | this._data = undefined; 85 | this._ptr = 0; 86 | 87 | this._version = []; 88 | this._streaming = false; 89 | this._optimized_for_accuracy = false; 90 | this._compression = 0; 91 | this._bodylen = 0xFFFFFFFF; 92 | 93 | this._blocks = [ new Block() ]; 94 | 95 | this._accuracyMatrix = false; 96 | this._accuracyGeo = false; 97 | this._accuracyProps = false; 98 | 99 | }; 100 | 101 | AWDLoader.prototype = { 102 | 103 | constructor: AWDLoader, 104 | 105 | load: function ( url, onLoad, onProgress, onError ) { 106 | 107 | var scope = this; 108 | 109 | this._url = url; 110 | this._baseDir = url.substr( 0, url.lastIndexOf( '/' ) + 1 ); 111 | 112 | var loader = new THREE.FileLoader( this.manager ); 113 | loader.setResponseType( 'arraybuffer' ); 114 | loader.load( url, function ( text ) { 115 | 116 | onLoad( scope.parse( text ) ); 117 | 118 | }, onProgress, onError ); 119 | 120 | }, 121 | 122 | parse: function ( data ) { 123 | 124 | var blen = data.byteLength; 125 | 126 | this._ptr = 0; 127 | this._data = new DataView( data ); 128 | 129 | this._parseHeader( ); 130 | 131 | if ( this._compression != 0 ) { 132 | 133 | console.error( 'compressed AWD not supported' ); 134 | 135 | } 136 | 137 | if ( ! this._streaming && this._bodylen != data.byteLength - this._ptr ) { 138 | 139 | console.error( 'AWDLoader: body len does not match file length', this._bodylen, blen - this._ptr ); 140 | 141 | } 142 | 143 | while ( this._ptr < blen ) { 144 | 145 | this.parseNextBlock(); 146 | 147 | } 148 | 149 | return this.trunk; 150 | 151 | }, 152 | 153 | parseNextBlock: function() { 154 | 155 | var assetData, 156 | ns, type, len, block, 157 | blockId = this.readU32(), 158 | ns = this.readU8(), 159 | type = this.readU8(), 160 | flags = this.readU8(), 161 | len = this.readU32(); 162 | 163 | 164 | switch ( type ) { 165 | case 1: 166 | assetData = this.parseMeshData( len ); 167 | break; 168 | case 22: 169 | assetData = this.parseContainer( len ); 170 | break; 171 | case 23: 172 | assetData = this.parseMeshInstance( len ); 173 | break; 174 | case 81: 175 | assetData = this.parseMaterial( len ); 176 | break; 177 | case 82: 178 | assetData = this.parseTexture( len ); 179 | break; 180 | case 101: 181 | assetData = this.parseSkeleton( len ); 182 | break; 183 | 184 | // case 111: 185 | // assetData = this.parseMeshPoseAnimation(len, true); 186 | // break; 187 | case 112: 188 | assetData = this.parseMeshPoseAnimation( len, false ); 189 | break; 190 | case 113: 191 | assetData = this.parseVertexAnimationSet( len ); 192 | break; 193 | case 102: 194 | assetData = this.parseSkeletonPose( len ); 195 | break; 196 | case 103: 197 | assetData = this.parseSkeletonAnimation( len ); 198 | break; 199 | case 122: 200 | assetData = this.parseAnimatorSet( len ); 201 | break; 202 | // case 121: 203 | // assetData = parseUVAnimation(len); 204 | // break; 205 | default: 206 | //debug('Ignoring block!',type, len); 207 | this._ptr += len; 208 | break; 209 | } 210 | 211 | 212 | // Store block reference for later use 213 | this._blocks[ blockId ] = block = new Block(); 214 | block.data = assetData; 215 | block.id = blockId; 216 | 217 | 218 | }, 219 | 220 | _parseHeader: function () { 221 | 222 | var version = this._version, 223 | awdmagic = 224 | ( this.readU8() << 16 ) 225 | | ( this.readU8() << 8 ) 226 | | this.readU8(); 227 | 228 | if ( awdmagic != 4282180 ) 229 | throw new Error( "AWDLoader - bad magic" ); 230 | 231 | version[ 0 ] = this.readU8(); 232 | version[ 1 ] = this.readU8(); 233 | 234 | var flags = this.readU16(); 235 | 236 | this._streaming = ( flags & 0x1 ) == 0x1; 237 | 238 | if ( ( version[ 0 ] === 2 ) && ( version[ 1 ] === 1 ) ) { 239 | 240 | this._accuracyMatrix = ( flags & 0x2 ) === 0x2; 241 | this._accuracyGeo = ( flags & 0x4 ) === 0x4; 242 | this._accuracyProps = ( flags & 0x8 ) === 0x8; 243 | 244 | } 245 | 246 | this._geoNrType = this._accuracyGeo ? FLOAT64 : FLOAT32; 247 | this._matrixNrType = this._accuracyMatrix ? FLOAT64 : FLOAT32; 248 | this._propsNrType = this._accuracyProps ? FLOAT64 : FLOAT32; 249 | 250 | this._optimized_for_accuracy = ( flags & 0x2 ) === 0x2; 251 | 252 | this._compression = this.readU8(); 253 | this._bodylen = this.readU32(); 254 | 255 | }, 256 | 257 | parseContainer: function ( len ) { 258 | 259 | var parent, 260 | ctr = new THREE.Object3D(), 261 | par_id = this.readU32(), 262 | mtx = this.parseMatrix4(); 263 | 264 | ctr.name = this.readUTF(); 265 | ctr.applyMatrix( mtx ); 266 | 267 | parent = this._blocks[ par_id ].data || this.trunk; 268 | parent.add( ctr ); 269 | 270 | this.parseProperties( { 271 | 1: this._matrixNrType, 272 | 2: this._matrixNrType, 273 | 3: this._matrixNrType, 274 | 4: UINT8 275 | } ); 276 | 277 | ctr.extra = this.parseUserAttributes(); 278 | 279 | return ctr; 280 | 281 | }, 282 | 283 | parseMeshInstance: function ( len ) { 284 | 285 | var name, 286 | mesh, geometries, meshLen, meshes, 287 | par_id, data_id, 288 | mtx, 289 | materials, mat, mat_id, 290 | num_materials, 291 | parent, 292 | i; 293 | 294 | par_id = this.readU32(); 295 | mtx = this.parseMatrix4(); 296 | name = this.readUTF(); 297 | data_id = this.readU32(); 298 | num_materials = this.readU16(); 299 | 300 | geometries = this.getBlock( data_id ); 301 | 302 | materials = []; 303 | 304 | for ( i = 0; i < num_materials; i ++ ) { 305 | 306 | mat_id = this.readU32(); 307 | mat = this.getBlock( mat_id ); 308 | materials.push( mat ); 309 | 310 | } 311 | 312 | meshLen = geometries.length; 313 | meshes = []; 314 | 315 | // TODO : BufferGeometry don't support "geometryGroups" for now. 316 | // so we create sub meshes for each groups 317 | if ( meshLen > 1 ) { 318 | 319 | mesh = new THREE.Object3D(); 320 | for ( i = 0; i < meshLen; i ++ ) { 321 | 322 | var sm = new THREE.Mesh( geometries[ i ] ); 323 | meshes.push( sm ); 324 | mesh.add( sm ); 325 | 326 | } 327 | 328 | } else { 329 | 330 | mesh = new THREE.Mesh( geometries[ 0 ] ); 331 | meshes.push( mesh ); 332 | 333 | } 334 | 335 | mesh.applyMatrix( mtx ); 336 | mesh.name = name; 337 | 338 | 339 | parent = this.getBlock( par_id ) || this.trunk; 340 | parent.add( mesh ); 341 | 342 | 343 | var matLen = materials.length; 344 | var maxLen = Math.max( meshLen, matLen ); 345 | for ( i = 0; i < maxLen; i ++ ) 346 | meshes[ i % meshLen ].material = materials[ i % matLen ]; 347 | 348 | 349 | // Ignore for now 350 | this.parseProperties( null ); 351 | mesh.extra = this.parseUserAttributes(); 352 | 353 | return mesh; 354 | 355 | }, 356 | 357 | parseMaterial: function ( len ) { 358 | 359 | var name, 360 | type, 361 | props, 362 | mat, 363 | attributes, 364 | finalize, 365 | num_methods, 366 | methods_parsed; 367 | 368 | name = this.readUTF(); 369 | type = this.readU8(); 370 | num_methods = this.readU8(); 371 | 372 | //log( "AWDLoader parseMaterial ",name ) 373 | 374 | // Read material numerical properties 375 | // (1=color, 2=bitmap url, 11=alpha_blending, 12=alpha_threshold, 13=repeat) 376 | props = this.parseProperties( { 377 | 1: AWD_FIELD_INT32, 378 | 2: AWD_FIELD_BADDR, 379 | 11: AWD_FIELD_BOOL, 380 | 12: AWD_FIELD_FLOAT32, 381 | 13: AWD_FIELD_BOOL 382 | } ); 383 | 384 | methods_parsed = 0; 385 | 386 | while ( methods_parsed < num_methods ) { 387 | 388 | var method_type = this.readU16(); 389 | this.parseProperties( null ); 390 | this.parseUserAttributes(); 391 | 392 | } 393 | 394 | attributes = this.parseUserAttributes(); 395 | 396 | if ( this.materialFactory !== undefined ) { 397 | 398 | mat = this.materialFactory( name ); 399 | if ( mat ) return mat; 400 | 401 | } 402 | 403 | mat = new THREE.MeshPhongMaterial(); 404 | 405 | if ( type === 1 ) { 406 | 407 | // Color material 408 | mat.color.setHex( props.get( 1, 0xcccccc ) ); 409 | 410 | } else if ( type === 2 ) { 411 | 412 | // Bitmap material 413 | var tex_addr = props.get( 2, 0 ); 414 | mat.map = this.getBlock( tex_addr ); 415 | 416 | } 417 | 418 | mat.extra = attributes; 419 | mat.alphaThreshold = props.get( 12, 0.0 ); 420 | mat.repeat = props.get( 13, false ); 421 | 422 | 423 | return mat; 424 | 425 | }, 426 | 427 | parseTexture: function( len ) { 428 | 429 | var name = this.readUTF(), 430 | type = this.readU8(), 431 | asset, 432 | data_len; 433 | 434 | // External 435 | if ( type === 0 ) { 436 | 437 | data_len = this.readU32(); 438 | var url = this.readUTFBytes( data_len ); 439 | console.log( url ); 440 | 441 | asset = this.loadTexture( url ); 442 | 443 | } else { 444 | // embed texture not supported 445 | } 446 | // Ignore for now 447 | this.parseProperties( null ); 448 | 449 | this.parseUserAttributes(); 450 | return asset; 451 | 452 | }, 453 | 454 | loadTexture: function( url ) { 455 | 456 | var tex = new THREE.Texture(); 457 | 458 | var loader = new THREE.ImageLoader( this.manager ); 459 | 460 | loader.load( this._baseDir + url, function( image ) { 461 | 462 | tex.image = image; 463 | tex.needsUpdate = true; 464 | 465 | } ); 466 | 467 | return tex; 468 | 469 | }, 470 | 471 | parseSkeleton: function( len ) { 472 | 473 | // Array 474 | var name = this.readUTF(), 475 | num_joints = this.readU16(), 476 | skeleton = [], 477 | joints_parsed = 0; 478 | 479 | this.parseProperties( null ); 480 | 481 | while ( joints_parsed < num_joints ) { 482 | 483 | var joint, ibp; 484 | 485 | // Ignore joint id 486 | this.readU16(); 487 | 488 | joint = new THREE.Bone(); 489 | joint.parent = this.readU16() - 1; // 0=null in AWD 490 | joint.name = this.readUTF(); 491 | 492 | ibp = this.parseMatrix4(); 493 | joint.skinMatrix = ibp; 494 | 495 | // Ignore joint props/attributes for now 496 | this.parseProperties( null ); 497 | this.parseUserAttributes(); 498 | 499 | skeleton.push( joint ); 500 | joints_parsed ++; 501 | 502 | } 503 | 504 | // Discard attributes for now 505 | this.parseUserAttributes(); 506 | 507 | 508 | return skeleton; 509 | 510 | }, 511 | 512 | parseSkeletonPose: function( blockID ) { 513 | 514 | var name = this.readUTF(); 515 | 516 | var num_joints = this.readU16(); 517 | this.parseProperties( null ); 518 | 519 | // debug( 'parse Skeleton Pose. joints : ' + num_joints); 520 | 521 | var pose = []; 522 | 523 | var joints_parsed = 0; 524 | 525 | while ( joints_parsed < num_joints ) { 526 | 527 | var joint_pose; 528 | 529 | var has_transform; //:uint; 530 | var mtx_data; 531 | 532 | has_transform = this.readU8(); 533 | 534 | if ( has_transform === 1 ) { 535 | 536 | mtx_data = this.parseMatrix4(); 537 | 538 | } else { 539 | 540 | mtx_data = new THREE.Matrix4(); 541 | 542 | } 543 | pose[ joints_parsed ] = mtx_data; 544 | joints_parsed ++; 545 | 546 | } 547 | 548 | // Skip attributes for now 549 | this.parseUserAttributes(); 550 | 551 | return pose; 552 | 553 | }, 554 | 555 | parseSkeletonAnimation: function( blockID ) { 556 | 557 | var frame_dur; 558 | var pose_addr; 559 | var pose; 560 | 561 | var name = this.readUTF(); 562 | 563 | var clip = []; 564 | 565 | var num_frames = this.readU16(); 566 | this.parseProperties( null ); 567 | 568 | var frames_parsed = 0; 569 | var returnedArray; 570 | 571 | // debug( 'parse Skeleton Animation. frames : ' + num_frames); 572 | 573 | while ( frames_parsed < num_frames ) { 574 | 575 | pose_addr = this.readU32(); 576 | frame_dur = this.readU16(); 577 | 578 | pose = this._blocks[ pose_addr ].data; 579 | // debug( 'pose address ',pose[2].elements[12],pose[2].elements[13],pose[2].elements[14] ); 580 | clip.push( { 581 | pose : pose, 582 | duration : frame_dur 583 | } ); 584 | 585 | frames_parsed ++; 586 | 587 | } 588 | 589 | if ( clip.length === 0 ) { 590 | 591 | // debug("Could not this SkeletonClipNode, because no Frames where set."); 592 | return; 593 | 594 | } 595 | // Ignore attributes for now 596 | this.parseUserAttributes(); 597 | return clip; 598 | 599 | }, 600 | 601 | parseVertexAnimationSet: function( len ) { 602 | 603 | var poseBlockAdress, 604 | name = this.readUTF(), 605 | num_frames = this.readU16(), 606 | props = this.parseProperties( { 1: UINT16 } ), 607 | frames_parsed = 0, 608 | skeletonFrames = []; 609 | 610 | while ( frames_parsed < num_frames ) { 611 | 612 | poseBlockAdress = this.readU32(); 613 | skeletonFrames.push( this._blocks[ poseBlockAdress ].data ); 614 | frames_parsed ++; 615 | 616 | } 617 | 618 | this.parseUserAttributes(); 619 | 620 | 621 | return skeletonFrames; 622 | 623 | }, 624 | 625 | parseAnimatorSet: function( len ) { 626 | 627 | var targetMesh; 628 | 629 | var animSetBlockAdress; //:int 630 | 631 | var targetAnimationSet; //:AnimationSetBase; 632 | var outputString = ""; //:String = ""; 633 | var name = this.readUTF(); 634 | var type = this.readU16(); 635 | 636 | var props = this.parseProperties( { 1: BADDR } ); 637 | 638 | animSetBlockAdress = this.readU32(); 639 | var targetMeshLength = this.readU16(); 640 | 641 | var meshAdresses = []; //:Vector. = new Vector.; 642 | 643 | for ( var i = 0; i < targetMeshLength; i ++ ) 644 | meshAdresses.push( this.readU32() ); 645 | 646 | var activeState = this.readU16(); 647 | var autoplay = Boolean( this.readU8() ); 648 | this.parseUserAttributes(); 649 | this.parseUserAttributes(); 650 | 651 | var returnedArray; 652 | var targetMeshes = []; //:Vector. = new Vector.; 653 | 654 | for ( i = 0; i < meshAdresses.length; i ++ ) { 655 | 656 | // returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]); 657 | // if (returnedArray[0]) 658 | targetMeshes.push( this._blocks[ meshAdresses[ i ]].data ); 659 | 660 | } 661 | 662 | targetAnimationSet = this._blocks[ animSetBlockAdress ].data; 663 | var thisAnimator; 664 | 665 | if ( type == 1 ) { 666 | 667 | 668 | thisAnimator = { 669 | animationSet : targetAnimationSet, 670 | skeleton : this._blocks[ props.get( 1, 0 ) ].data 671 | }; 672 | 673 | } else if ( type == 2 ) { 674 | // debug( "vertex Anim???"); 675 | } 676 | 677 | 678 | for ( i = 0; i < targetMeshes.length; i ++ ) { 679 | 680 | targetMeshes[ i ].animator = thisAnimator; 681 | 682 | } 683 | // debug("Parsed a Animator: Name = " + name); 684 | 685 | return thisAnimator; 686 | 687 | }, 688 | 689 | parseMeshData: function ( len ) { 690 | 691 | var name = this.readUTF(), 692 | num_subs = this.readU16(), 693 | geom, 694 | subs_parsed = 0, 695 | props, 696 | buffer, 697 | skinW, skinI, 698 | geometries = []; 699 | 700 | props = this.parseProperties( { 701 | 1: this._geoNrType, 702 | 2: this._geoNrType 703 | } ); 704 | 705 | // Loop through sub meshes 706 | while ( subs_parsed < num_subs ) { 707 | 708 | var sm_len, sm_end, attrib; 709 | 710 | geom = new THREE.BufferGeometry(); 711 | geom.name = name; 712 | geometries.push( geom ); 713 | 714 | 715 | sm_len = this.readU32(); 716 | sm_end = this._ptr + sm_len; 717 | 718 | 719 | // Ignore for now 720 | this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } ); 721 | 722 | // Loop through data streams 723 | while ( this._ptr < sm_end ) { 724 | 725 | var idx = 0, 726 | str_type = this.readU8(), 727 | str_ftype = this.readU8(), 728 | str_len = this.readU32(), 729 | str_end = str_len + this._ptr; 730 | 731 | // VERTICES 732 | // ------------------ 733 | if ( str_type === 1 ) { 734 | 735 | buffer = new Float32Array( ( str_len / 12 ) * 3 ); 736 | attrib = new THREE.BufferAttribute( buffer, 3 ); 737 | 738 | geom.addAttribute( 'position', attrib ); 739 | idx = 0; 740 | 741 | while ( this._ptr < str_end ) { 742 | 743 | buffer[ idx ] = - this.readF32(); 744 | buffer[ idx + 1 ] = this.readF32(); 745 | buffer[ idx + 2 ] = this.readF32(); 746 | idx += 3; 747 | 748 | } 749 | 750 | } 751 | 752 | // INDICES 753 | // ----------------- 754 | else if ( str_type === 2 ) { 755 | 756 | buffer = new Uint16Array( str_len / 2 ); 757 | attrib = new THREE.BufferAttribute( buffer, 1 ); 758 | geom.setIndex( attrib ); 759 | 760 | idx = 0; 761 | 762 | while ( this._ptr < str_end ) { 763 | 764 | buffer[ idx + 1 ] = this.readU16(); 765 | buffer[ idx ] = this.readU16(); 766 | buffer[ idx + 2 ] = this.readU16(); 767 | idx += 3; 768 | 769 | } 770 | 771 | } 772 | 773 | // UVS 774 | // ------------------- 775 | else if ( str_type === 3 ) { 776 | 777 | buffer = new Float32Array( ( str_len / 8 ) * 2 ); 778 | attrib = new THREE.BufferAttribute( buffer, 2 ); 779 | 780 | geom.addAttribute( 'uv', attrib ); 781 | idx = 0; 782 | 783 | while ( this._ptr < str_end ) { 784 | 785 | buffer[ idx ] = this.readF32(); 786 | buffer[ idx + 1 ] = 1.0 - this.readF32(); 787 | idx += 2; 788 | 789 | } 790 | 791 | } 792 | 793 | // NORMALS 794 | else if ( str_type === 4 ) { 795 | 796 | buffer = new Float32Array( ( str_len / 12 ) * 3 ); 797 | attrib = new THREE.BufferAttribute( buffer, 3 ); 798 | geom.addAttribute( 'normal', attrib ); 799 | idx = 0; 800 | 801 | while ( this._ptr < str_end ) { 802 | 803 | buffer[ idx ] = - this.readF32(); 804 | buffer[ idx + 1 ] = this.readF32(); 805 | buffer[ idx + 2 ] = this.readF32(); 806 | idx += 3; 807 | 808 | } 809 | 810 | } 811 | 812 | // else if (str_type == 6) { 813 | // skinI = new Float32Array( str_len>>1 ); 814 | // idx = 0 815 | 816 | // while (this._ptr < str_end) { 817 | // skinI[idx] = this.readU16(); 818 | // idx++; 819 | // } 820 | 821 | // } 822 | // else if (str_type == 7) { 823 | // skinW = new Float32Array( str_len>>2 ); 824 | // idx = 0; 825 | 826 | // while (this._ptr < str_end) { 827 | // skinW[idx] = this.readF32(); 828 | // idx++; 829 | // } 830 | // } 831 | else { 832 | 833 | this._ptr = str_end; 834 | 835 | } 836 | 837 | } 838 | 839 | this.parseUserAttributes(); 840 | 841 | geom.computeBoundingSphere(); 842 | subs_parsed ++; 843 | 844 | } 845 | 846 | //geom.computeFaceNormals(); 847 | 848 | this.parseUserAttributes(); 849 | //finalizeAsset(geom, name); 850 | 851 | return geometries; 852 | 853 | }, 854 | 855 | parseMeshPoseAnimation: function( len, poseOnly ) { 856 | 857 | var num_frames = 1, 858 | num_submeshes, 859 | frames_parsed, 860 | subMeshParsed, 861 | frame_dur, 862 | x, y, z, 863 | 864 | str_len, 865 | str_end, 866 | geom, 867 | subGeom, 868 | idx = 0, 869 | clip = {}, 870 | indices, 871 | verts, 872 | num_Streams, 873 | streamsParsed, 874 | streamtypes = [], 875 | 876 | props, 877 | thisGeo, 878 | name = this.readUTF(), 879 | geoAdress = this.readU32(); 880 | 881 | var mesh = this.getBlock( geoAdress ); 882 | 883 | if ( mesh === null ) { 884 | 885 | console.log( "parseMeshPoseAnimation target mesh not found at:", geoAdress ); 886 | return; 887 | 888 | } 889 | 890 | geom = mesh.geometry; 891 | geom.morphTargets = []; 892 | 893 | if ( ! poseOnly ) 894 | num_frames = this.readU16(); 895 | 896 | num_submeshes = this.readU16(); 897 | num_Streams = this.readU16(); 898 | 899 | // debug("VA num_frames : ", num_frames ); 900 | // debug("VA num_submeshes : ", num_submeshes ); 901 | // debug("VA numstreams : ", num_Streams ); 902 | 903 | streamsParsed = 0; 904 | while ( streamsParsed < num_Streams ) { 905 | 906 | streamtypes.push( this.readU16() ); 907 | streamsParsed ++; 908 | 909 | } 910 | props = this.parseProperties( { 1: BOOL, 2: BOOL } ); 911 | 912 | clip.looping = props.get( 1, true ); 913 | clip.stitchFinalFrame = props.get( 2, false ); 914 | 915 | frames_parsed = 0; 916 | 917 | while ( frames_parsed < num_frames ) { 918 | 919 | frame_dur = this.readU16(); 920 | subMeshParsed = 0; 921 | 922 | while ( subMeshParsed < num_submeshes ) { 923 | 924 | streamsParsed = 0; 925 | str_len = this.readU32(); 926 | str_end = this._ptr + str_len; 927 | 928 | while ( streamsParsed < num_Streams ) { 929 | 930 | if ( streamtypes[ streamsParsed ] === 1 ) { 931 | 932 | //geom.addAttribute( 'morphTarget'+frames_parsed, Float32Array, str_len/12, 3 ); 933 | var buffer = new Float32Array( str_len / 4 ); 934 | geom.morphTargets.push( { 935 | array : buffer 936 | } ); 937 | 938 | //buffer = geom.attributes['morphTarget'+frames_parsed].array 939 | idx = 0; 940 | 941 | while ( this._ptr < str_end ) { 942 | 943 | buffer[ idx ] = this.readF32(); 944 | buffer[ idx + 1 ] = this.readF32(); 945 | buffer[ idx + 2 ] = this.readF32(); 946 | idx += 3; 947 | 948 | } 949 | 950 | 951 | subMeshParsed ++; 952 | 953 | } else 954 | this._ptr = str_end; 955 | streamsParsed ++; 956 | 957 | } 958 | 959 | } 960 | 961 | 962 | frames_parsed ++; 963 | 964 | } 965 | 966 | this.parseUserAttributes(); 967 | 968 | return null; 969 | 970 | }, 971 | 972 | getBlock: function ( id ) { 973 | 974 | return this._blocks[ id ].data; 975 | 976 | }, 977 | 978 | parseMatrix4: function () { 979 | 980 | var mtx = new THREE.Matrix4(); 981 | var e = mtx.elements; 982 | 983 | e[ 0 ] = this.readF32(); 984 | e[ 1 ] = this.readF32(); 985 | e[ 2 ] = this.readF32(); 986 | e[ 3 ] = 0.0; 987 | //e[3] = 0.0; 988 | 989 | e[ 4 ] = this.readF32(); 990 | e[ 5 ] = this.readF32(); 991 | e[ 6 ] = this.readF32(); 992 | //e[7] = this.readF32(); 993 | e[ 7 ] = 0.0; 994 | 995 | e[ 8 ] = this.readF32(); 996 | e[ 9 ] = this.readF32(); 997 | e[ 10 ] = this.readF32(); 998 | //e[11] = this.readF32(); 999 | e[ 11 ] = 0.0; 1000 | 1001 | e[ 12 ] = - this.readF32(); 1002 | e[ 13 ] = this.readF32(); 1003 | e[ 14 ] = this.readF32(); 1004 | //e[15] = this.readF32(); 1005 | e[ 15 ] = 1.0; 1006 | return mtx; 1007 | 1008 | }, 1009 | 1010 | parseProperties: function ( expected ) { 1011 | 1012 | var list_len = this.readU32(); 1013 | var list_end = this._ptr + list_len; 1014 | 1015 | var props = new AWDProperties(); 1016 | 1017 | if ( expected ) { 1018 | 1019 | while ( this._ptr < list_end ) { 1020 | 1021 | var key = this.readU16(); 1022 | var len = this.readU32(); 1023 | var type; 1024 | 1025 | if ( expected.hasOwnProperty( key ) ) { 1026 | 1027 | type = expected[ key ]; 1028 | props.set( key, this.parseAttrValue( type, len ) ); 1029 | 1030 | } else { 1031 | 1032 | this._ptr += len; 1033 | 1034 | } 1035 | 1036 | } 1037 | 1038 | } 1039 | 1040 | return props; 1041 | 1042 | }, 1043 | 1044 | parseUserAttributes: function () { 1045 | 1046 | // skip for now 1047 | this._ptr = this.readU32() + this._ptr; 1048 | return null; 1049 | 1050 | }, 1051 | 1052 | parseAttrValue: function ( type, len ) { 1053 | 1054 | var elem_len; 1055 | var read_func; 1056 | 1057 | switch ( type ) { 1058 | case AWD_FIELD_INT8: 1059 | elem_len = 1; 1060 | read_func = this.readI8; 1061 | break; 1062 | case AWD_FIELD_INT16: 1063 | elem_len = 2; 1064 | read_func = this.readI16; 1065 | break; 1066 | case AWD_FIELD_INT32: 1067 | elem_len = 4; 1068 | read_func = this.readI32; 1069 | break; 1070 | case AWD_FIELD_BOOL: 1071 | case AWD_FIELD_UINT8: 1072 | elem_len = 1; 1073 | read_func = this.readU8; 1074 | break; 1075 | case AWD_FIELD_UINT16: 1076 | elem_len = 2; 1077 | read_func = this.readU16; 1078 | break; 1079 | case AWD_FIELD_UINT32: 1080 | case AWD_FIELD_BADDR: 1081 | elem_len = 4; 1082 | read_func = this.readU32; 1083 | break; 1084 | case AWD_FIELD_FLOAT32: 1085 | elem_len = 4; 1086 | read_func = this.readF32; 1087 | break; 1088 | case AWD_FIELD_FLOAT64: 1089 | elem_len = 8; 1090 | read_func = this.readF64; 1091 | break; 1092 | case AWD_FIELD_VECTOR2x1: 1093 | case AWD_FIELD_VECTOR3x1: 1094 | case AWD_FIELD_VECTOR4x1: 1095 | case AWD_FIELD_MTX3x2: 1096 | case AWD_FIELD_MTX3x3: 1097 | case AWD_FIELD_MTX4x3: 1098 | case AWD_FIELD_MTX4x4: 1099 | elem_len = 8; 1100 | read_func = this.readF64; 1101 | break; 1102 | } 1103 | 1104 | if ( elem_len < len ) { 1105 | 1106 | var list; 1107 | var num_read; 1108 | var num_elems; 1109 | 1110 | list = []; 1111 | num_read = 0; 1112 | num_elems = len / elem_len; 1113 | 1114 | while ( num_read < num_elems ) { 1115 | 1116 | list.push( read_func.call( this ) ); 1117 | num_read ++; 1118 | 1119 | } 1120 | 1121 | return list; 1122 | 1123 | } else { 1124 | 1125 | return read_func.call( this ); 1126 | 1127 | } 1128 | 1129 | }, 1130 | 1131 | readU8: function () { 1132 | 1133 | return this._data.getUint8( this._ptr ++ ); 1134 | 1135 | }, 1136 | readI8: function () { 1137 | 1138 | return this._data.getInt8( this._ptr ++ ); 1139 | 1140 | }, 1141 | readU16: function () { 1142 | 1143 | var a = this._data.getUint16( this._ptr, littleEndian ); 1144 | this._ptr += 2; 1145 | return a; 1146 | 1147 | }, 1148 | readI16: function () { 1149 | 1150 | var a = this._data.getInt16( this._ptr, littleEndian ); 1151 | this._ptr += 2; 1152 | return a; 1153 | 1154 | }, 1155 | readU32: function () { 1156 | 1157 | var a = this._data.getUint32( this._ptr, littleEndian ); 1158 | this._ptr += 4; 1159 | return a; 1160 | 1161 | }, 1162 | readI32: function () { 1163 | 1164 | var a = this._data.getInt32( this._ptr, littleEndian ); 1165 | this._ptr += 4; 1166 | return a; 1167 | 1168 | }, 1169 | readF32: function () { 1170 | 1171 | var a = this._data.getFloat32( this._ptr, littleEndian ); 1172 | this._ptr += 4; 1173 | return a; 1174 | 1175 | }, 1176 | readF64: function () { 1177 | 1178 | var a = this._data.getFloat64( this._ptr, littleEndian ); 1179 | this._ptr += 8; 1180 | return a; 1181 | 1182 | }, 1183 | 1184 | /** 1185 | * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. 1186 | * @param {Array.} bytes UTF-8 byte array. 1187 | * @return {string} 16-bit Unicode string. 1188 | */ 1189 | readUTF: function () { 1190 | 1191 | var len = this.readU16(); 1192 | return this.readUTFBytes( len ); 1193 | 1194 | }, 1195 | 1196 | /** 1197 | * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. 1198 | * @param {Array.} bytes UTF-8 byte array. 1199 | * @return {string} 16-bit Unicode string. 1200 | */ 1201 | readUTFBytes: function ( len ) { 1202 | 1203 | // TODO(user): Use native implementations if/when available 1204 | var out = [], c = 0; 1205 | 1206 | while ( out.length < len ) { 1207 | 1208 | var c1 = this._data.getUint8( this._ptr ++, littleEndian ); 1209 | if ( c1 < 128 ) { 1210 | 1211 | out[ c ++ ] = String.fromCharCode( c1 ); 1212 | 1213 | } else if ( c1 > 191 && c1 < 224 ) { 1214 | 1215 | var c2 = this._data.getUint8( this._ptr ++, littleEndian ); 1216 | out[ c ++ ] = String.fromCharCode( ( c1 & 31 ) << 6 | c2 & 63 ); 1217 | 1218 | } else { 1219 | 1220 | var c2 = this._data.getUint8( this._ptr ++, littleEndian ); 1221 | var c3 = this._data.getUint8( this._ptr ++, littleEndian ); 1222 | out[ c ++ ] = String.fromCharCode( 1223 | ( c1 & 15 ) << 12 | ( c2 & 63 ) << 6 | c3 & 63 1224 | ); 1225 | 1226 | } 1227 | 1228 | } 1229 | return out.join( '' ); 1230 | 1231 | } 1232 | 1233 | }; 1234 | 1235 | export default AWDLoader 1236 | -------------------------------------------------------------------------------- /src/javascript/Loaders/GLTFLoader.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as THREE from 'three' 3 | 4 | /** 5 | * @author Rich Tibbett / https://github.com/richtr 6 | * @author mrdoob / http://mrdoob.com/ 7 | * @author Tony Parisi / http://www.tonyparisi.com/ 8 | * @author Takahiro / https://github.com/takahirox 9 | * @author Don McCurdy / https://www.donmccurdy.com 10 | */ 11 | 12 | function GLTFLoader( manager ) { 13 | 14 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 15 | 16 | } 17 | 18 | GLTFLoader.prototype = { 19 | 20 | constructor: GLTFLoader, 21 | 22 | crossOrigin: 'Anonymous', 23 | 24 | load: function ( url, onLoad, onProgress, onError ) { 25 | 26 | var scope = this; 27 | 28 | var path = this.path && ( typeof this.path === 'string' ) ? this.path : THREE.Loader.prototype.extractUrlBase( url ); 29 | 30 | var loader = new THREE.FileLoader( scope.manager ); 31 | 32 | loader.setResponseType( 'arraybuffer' ); 33 | 34 | loader.load( url, function ( data ) { 35 | 36 | try { 37 | 38 | scope.parse( data, path, onLoad, onError ); 39 | 40 | } catch ( e ) { 41 | 42 | // For SyntaxError or TypeError, return a generic failure message. 43 | onError( e.constructor === Error ? e : new Error( 'THREE.GLTFLoader: Unable to parse model.' ) ); 44 | 45 | } 46 | 47 | }, onProgress, onError ); 48 | 49 | }, 50 | 51 | setCrossOrigin: function ( value ) { 52 | 53 | this.crossOrigin = value; 54 | 55 | }, 56 | 57 | setPath: function ( value ) { 58 | 59 | this.path = value; 60 | 61 | }, 62 | 63 | parse: function ( data, path, onLoad, onError ) { 64 | 65 | var content; 66 | var extensions = {}; 67 | 68 | var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) ); 69 | 70 | if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { 71 | 72 | extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data ); 73 | content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content; 74 | 75 | } else { 76 | 77 | content = convertUint8ArrayToString( new Uint8Array( data ) ); 78 | 79 | } 80 | 81 | var json = JSON.parse( content ); 82 | 83 | if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { 84 | 85 | onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); 86 | return; 87 | 88 | } 89 | 90 | if ( json.extensionsUsed ) { 91 | 92 | if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) { 93 | 94 | extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json ); 95 | 96 | } 97 | 98 | if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) { 99 | 100 | extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json ); 101 | 102 | } 103 | 104 | if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ) >= 0 ) { 105 | 106 | extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); 107 | 108 | } 109 | 110 | } 111 | 112 | console.time( 'GLTFLoader' ); 113 | 114 | var parser = new GLTFParser( json, extensions, { 115 | 116 | path: path || this.path, 117 | crossOrigin: this.crossOrigin 118 | 119 | } ); 120 | 121 | parser.parse( function ( scene, scenes, cameras, animations ) { 122 | 123 | console.timeEnd( 'GLTFLoader' ); 124 | 125 | var glTF = { 126 | scene: scene, 127 | scenes: scenes, 128 | cameras: cameras, 129 | animations: animations 130 | }; 131 | 132 | onLoad( glTF ); 133 | 134 | }, onError ); 135 | 136 | } 137 | 138 | }; 139 | 140 | /* GLTFREGISTRY */ 141 | 142 | function GLTFRegistry() { 143 | 144 | var objects = {}; 145 | 146 | return { 147 | 148 | get: function ( key ) { 149 | 150 | return objects[ key ]; 151 | 152 | }, 153 | 154 | add: function ( key, object ) { 155 | 156 | objects[ key ] = object; 157 | 158 | }, 159 | 160 | remove: function ( key ) { 161 | 162 | delete objects[ key ]; 163 | 164 | }, 165 | 166 | removeAll: function () { 167 | 168 | objects = {}; 169 | 170 | }, 171 | 172 | update: function ( scene, camera ) { 173 | 174 | for ( var name in objects ) { 175 | 176 | var object = objects[ name ]; 177 | 178 | if ( object.update ) { 179 | 180 | object.update( scene, camera ); 181 | 182 | } 183 | 184 | } 185 | 186 | } 187 | 188 | }; 189 | 190 | } 191 | 192 | /*********************************/ 193 | /********** EXTENSIONS ***********/ 194 | /*********************************/ 195 | 196 | var EXTENSIONS = { 197 | KHR_BINARY_GLTF: 'KHR_binary_glTF', 198 | KHR_LIGHTS: 'KHR_lights', 199 | KHR_MATERIALS_COMMON: 'KHR_materials_common', 200 | KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness' 201 | }; 202 | 203 | /** 204 | * Lights Extension 205 | * 206 | * Specification: PENDING 207 | */ 208 | function GLTFLightsExtension( json ) { 209 | 210 | this.name = EXTENSIONS.KHR_LIGHTS; 211 | 212 | this.lights = {}; 213 | 214 | var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS ] ) || {}; 215 | var lights = extension.lights || {}; 216 | 217 | for ( var lightId in lights ) { 218 | 219 | var light = lights[ lightId ]; 220 | var lightNode; 221 | 222 | var color = new THREE.Color().fromArray( light.color ); 223 | 224 | switch ( light.type ) { 225 | 226 | case 'directional': 227 | lightNode = new THREE.DirectionalLight( color ); 228 | lightNode.position.set( 0, 0, 1 ); 229 | break; 230 | 231 | case 'point': 232 | lightNode = new THREE.PointLight( color ); 233 | break; 234 | 235 | case 'spot': 236 | lightNode = new THREE.SpotLight( color ); 237 | lightNode.position.set( 0, 0, 1 ); 238 | break; 239 | 240 | case 'ambient': 241 | lightNode = new THREE.AmbientLight( color ); 242 | break; 243 | 244 | } 245 | 246 | if ( lightNode ) { 247 | 248 | if ( light.constantAttenuation !== undefined ) { 249 | 250 | lightNode.intensity = light.constantAttenuation; 251 | 252 | } 253 | 254 | if ( light.linearAttenuation !== undefined ) { 255 | 256 | lightNode.distance = 1 / light.linearAttenuation; 257 | 258 | } 259 | 260 | if ( light.quadraticAttenuation !== undefined ) { 261 | 262 | lightNode.decay = light.quadraticAttenuation; 263 | 264 | } 265 | 266 | if ( light.fallOffAngle !== undefined ) { 267 | 268 | lightNode.angle = light.fallOffAngle; 269 | 270 | } 271 | 272 | if ( light.fallOffExponent !== undefined ) { 273 | 274 | console.warn( 'THREE.GLTFLoader:: light.fallOffExponent not currently supported.' ); 275 | 276 | } 277 | 278 | lightNode.name = light.name || ( 'light_' + lightId ); 279 | this.lights[ lightId ] = lightNode; 280 | 281 | } 282 | 283 | } 284 | 285 | } 286 | 287 | /** 288 | * Common Materials Extension 289 | * 290 | * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common 291 | */ 292 | function GLTFMaterialsCommonExtension( json ) { 293 | 294 | this.name = EXTENSIONS.KHR_MATERIALS_COMMON; 295 | 296 | } 297 | 298 | GLTFMaterialsCommonExtension.prototype.getMaterialType = function ( material ) { 299 | 300 | var khrMaterial = material.extensions[ this.name ]; 301 | 302 | switch ( khrMaterial.type ) { 303 | 304 | case 'commonBlinn' : 305 | case 'commonPhong' : 306 | return THREE.MeshPhongMaterial; 307 | 308 | case 'commonLambert' : 309 | return THREE.MeshLambertMaterial; 310 | 311 | case 'commonConstant' : 312 | default : 313 | return THREE.MeshBasicMaterial; 314 | 315 | } 316 | 317 | }; 318 | 319 | GLTFMaterialsCommonExtension.prototype.extendParams = function ( materialParams, material, parser ) { 320 | 321 | var khrMaterial = material.extensions[ this.name ]; 322 | 323 | var pending = []; 324 | 325 | var keys = []; 326 | 327 | // TODO: Currently ignored: 'ambientFactor', 'ambientTexture' 328 | switch ( khrMaterial.type ) { 329 | 330 | case 'commonBlinn' : 331 | case 'commonPhong' : 332 | keys.push( 'diffuseFactor', 'diffuseTexture', 'specularFactor', 'specularTexture', 'shininessFactor' ); 333 | break; 334 | 335 | case 'commonLambert' : 336 | keys.push( 'diffuseFactor', 'diffuseTexture' ); 337 | break; 338 | 339 | case 'commonConstant' : 340 | default : 341 | break; 342 | 343 | } 344 | 345 | var materialValues = {}; 346 | 347 | keys.forEach( function( v ) { 348 | 349 | if ( khrMaterial[ v ] !== undefined ) materialValues[ v ] = khrMaterial[ v ]; 350 | 351 | } ); 352 | 353 | if ( materialValues.diffuseFactor !== undefined ) { 354 | 355 | materialParams.color = new THREE.Color().fromArray( materialValues.diffuseFactor ); 356 | materialParams.opacity = materialValues.diffuseFactor[ 3 ]; 357 | 358 | } 359 | 360 | if ( materialValues.diffuseTexture !== undefined ) { 361 | 362 | pending.push( parser.assignTexture( materialParams, 'map', materialValues.diffuseTexture.index ) ); 363 | 364 | } 365 | 366 | if ( materialValues.specularFactor !== undefined ) { 367 | 368 | materialParams.specular = new THREE.Color().fromArray( materialValues.specularFactor ); 369 | 370 | } 371 | 372 | if ( materialValues.specularTexture !== undefined ) { 373 | 374 | pending.push( parser.assignTexture( materialParams, 'specularMap', materialValues.specularTexture.index ) ); 375 | 376 | } 377 | 378 | if ( materialValues.shininessFactor !== undefined ) { 379 | 380 | materialParams.shininess = materialValues.shininessFactor; 381 | 382 | } 383 | 384 | return Promise.all( pending ); 385 | 386 | }; 387 | 388 | /* BINARY EXTENSION */ 389 | 390 | var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF'; 391 | var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; 392 | var BINARY_EXTENSION_HEADER_LENGTH = 12; 393 | var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; 394 | 395 | function GLTFBinaryExtension( data ) { 396 | 397 | this.name = EXTENSIONS.KHR_BINARY_GLTF; 398 | this.content = null; 399 | this.body = null; 400 | 401 | var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH ); 402 | 403 | this.header = { 404 | magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ), 405 | version: headerView.getUint32( 4, true ), 406 | length: headerView.getUint32( 8, true ) 407 | }; 408 | 409 | if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { 410 | 411 | throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); 412 | 413 | } else if ( this.header.version < 2.0 ) { 414 | 415 | throw new Error( 'THREE.GLTFLoader: Legacy binary file detected. Use GLTFLoader instead.' ); 416 | 417 | } 418 | 419 | var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH ); 420 | var chunkIndex = 0; 421 | 422 | while ( chunkIndex < chunkView.byteLength ) { 423 | 424 | var chunkLength = chunkView.getUint32( chunkIndex, true ); 425 | chunkIndex += 4; 426 | 427 | var chunkType = chunkView.getUint32( chunkIndex, true ); 428 | chunkIndex += 4; 429 | 430 | if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { 431 | 432 | var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength ); 433 | this.content = convertUint8ArrayToString( contentArray ); 434 | 435 | } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { 436 | 437 | var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex; 438 | this.body = data.slice( byteOffset, byteOffset + chunkLength ); 439 | 440 | } 441 | 442 | // Clients must ignore chunks with unknown types. 443 | 444 | chunkIndex += chunkLength; 445 | 446 | } 447 | 448 | if ( this.content === null ) { 449 | 450 | throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); 451 | 452 | } 453 | 454 | } 455 | 456 | /** 457 | * Specular-Glossiness Extension 458 | * 459 | * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness 460 | */ 461 | function GLTFMaterialsPbrSpecularGlossinessExtension() { 462 | 463 | return { 464 | 465 | name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS, 466 | 467 | getMaterialType: function () { 468 | 469 | return THREE.ShaderMaterial; 470 | 471 | }, 472 | 473 | extendParams: function ( params, material, parser ) { 474 | 475 | var pbrSpecularGlossiness = material.extensions[ this.name ]; 476 | 477 | var shader = THREE.ShaderLib[ 'standard' ]; 478 | 479 | var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); 480 | 481 | var specularMapParsFragmentChunk = [ 482 | '#ifdef USE_SPECULARMAP', 483 | ' uniform sampler2D specularMap;', 484 | '#endif' 485 | ].join( '\n' ); 486 | 487 | var glossinessMapParsFragmentChunk = [ 488 | '#ifdef USE_GLOSSINESSMAP', 489 | ' uniform sampler2D glossinessMap;', 490 | '#endif' 491 | ].join( '\n' ); 492 | 493 | var specularMapFragmentChunk = [ 494 | 'vec3 specularFactor = specular;', 495 | '#ifdef USE_SPECULARMAP', 496 | ' vec4 texelSpecular = texture2D( specularMap, vUv );', 497 | ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', 498 | ' specularFactor *= texelSpecular.rgb;', 499 | '#endif' 500 | ].join( '\n' ); 501 | 502 | var glossinessMapFragmentChunk = [ 503 | 'float glossinessFactor = glossiness;', 504 | '#ifdef USE_GLOSSINESSMAP', 505 | ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', 506 | ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', 507 | ' glossinessFactor *= texelGlossiness.a;', 508 | '#endif' 509 | ].join( '\n' ); 510 | 511 | var lightPhysicalFragmentChunk = [ 512 | 'PhysicalMaterial material;', 513 | 'material.diffuseColor = diffuseColor.rgb;', 514 | 'material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );', 515 | 'material.specularColor = specularFactor.rgb;', 516 | ].join( '\n' ); 517 | 518 | var fragmentShader = shader.fragmentShader 519 | .replace( '#include ', '' ) 520 | .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) 521 | .replace( 'uniform float metalness;', 'uniform float glossiness;' ) 522 | .replace( '#include ', specularMapParsFragmentChunk ) 523 | .replace( '#include ', glossinessMapParsFragmentChunk ) 524 | .replace( '#include ', specularMapFragmentChunk ) 525 | .replace( '#include ', glossinessMapFragmentChunk ) 526 | .replace( '#include ', lightPhysicalFragmentChunk ); 527 | 528 | delete uniforms.roughness; 529 | delete uniforms.metalness; 530 | delete uniforms.roughnessMap; 531 | delete uniforms.metalnessMap; 532 | 533 | uniforms.specular = { value: new THREE.Color().setHex( 0x111111 ) }; 534 | uniforms.glossiness = { value: 0.5 }; 535 | uniforms.specularMap = { value: null }; 536 | uniforms.glossinessMap = { value: null }; 537 | 538 | params.vertexShader = shader.vertexShader; 539 | params.fragmentShader = fragmentShader; 540 | params.uniforms = uniforms; 541 | params.defines = { 'STANDARD': '' }; 542 | 543 | params.color = new THREE.Color( 1.0, 1.0, 1.0 ); 544 | params.opacity = 1.0; 545 | 546 | var pending = []; 547 | 548 | if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { 549 | 550 | var array = pbrSpecularGlossiness.diffuseFactor; 551 | 552 | params.color.fromArray( array ); 553 | params.opacity = array[ 3 ]; 554 | 555 | } 556 | 557 | if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { 558 | 559 | pending.push( parser.assignTexture( params, 'map', pbrSpecularGlossiness.diffuseTexture.index ) ); 560 | 561 | } 562 | 563 | params.emissive = new THREE.Color( 0.0, 0.0, 0.0 ); 564 | params.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; 565 | params.specular = new THREE.Color( 1.0, 1.0, 1.0 ); 566 | 567 | if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { 568 | 569 | params.specular.fromArray( pbrSpecularGlossiness.specularFactor ); 570 | 571 | } 572 | 573 | if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { 574 | 575 | var specGlossIndex = pbrSpecularGlossiness.specularGlossinessTexture.index; 576 | pending.push( parser.assignTexture( params, 'glossinessMap', specGlossIndex ) ); 577 | pending.push( parser.assignTexture( params, 'specularMap', specGlossIndex ) ); 578 | 579 | } 580 | 581 | return Promise.all( pending ); 582 | 583 | }, 584 | 585 | createMaterial: function ( params ) { 586 | 587 | // setup material properties based on MeshStandardMaterial for Specular-Glossiness 588 | 589 | var material = new THREE.ShaderMaterial( { 590 | defines: params.defines, 591 | vertexShader: params.vertexShader, 592 | fragmentShader: params.fragmentShader, 593 | uniforms: params.uniforms, 594 | fog: true, 595 | lights: true, 596 | opacity: params.opacity, 597 | transparent: params.transparent 598 | } ); 599 | 600 | material.isGLTFSpecularGlossinessMaterial = true; 601 | 602 | material.color = params.color; 603 | 604 | material.map = params.map === undefined ? null : params.map; 605 | 606 | material.lightMap = null; 607 | material.lightMapIntensity = 1.0; 608 | 609 | material.aoMap = params.aoMap === undefined ? null : params.aoMap; 610 | material.aoMapIntensity = 1.0; 611 | 612 | material.emissive = params.emissive; 613 | material.emissiveIntensity = 1.0; 614 | material.emissiveMap = params.emissiveMap === undefined ? null : params.emissiveMap; 615 | 616 | material.bumpMap = params.bumpMap === undefined ? null : params.bumpMap; 617 | material.bumpScale = 1; 618 | 619 | material.normalMap = params.normalMap === undefined ? null : params.normalMap; 620 | material.normalScale = new THREE.Vector2( 1, 1 ); 621 | 622 | material.displacementMap = null; 623 | material.displacementScale = 1; 624 | material.displacementBias = 0; 625 | 626 | material.specularMap = params.specularMap === undefined ? null : params.specularMap; 627 | material.specular = params.specular; 628 | 629 | material.glossinessMap = params.glossinessMap === undefined ? null : params.glossinessMap; 630 | material.glossiness = params.glossiness; 631 | 632 | material.alphaMap = null; 633 | 634 | material.envMap = params.envMap === undefined ? null : params.envMap; 635 | material.envMapIntensity = 1.0; 636 | 637 | material.refractionRatio = 0.98; 638 | 639 | material.extensions.derivatives = true; 640 | 641 | return material; 642 | 643 | }, 644 | 645 | // Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer. 646 | refreshUniforms: function ( renderer, scene, camera, geometry, material, group ) { 647 | 648 | var uniforms = material.uniforms; 649 | var defines = material.defines; 650 | 651 | uniforms.opacity.value = material.opacity; 652 | 653 | uniforms.diffuse.value.copy( material.color ); 654 | uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); 655 | 656 | uniforms.map.value = material.map; 657 | uniforms.specularMap.value = material.specularMap; 658 | uniforms.alphaMap.value = material.alphaMap; 659 | 660 | uniforms.lightMap.value = material.lightMap; 661 | uniforms.lightMapIntensity.value = material.lightMapIntensity; 662 | 663 | uniforms.aoMap.value = material.aoMap; 664 | uniforms.aoMapIntensity.value = material.aoMapIntensity; 665 | 666 | // uv repeat and offset setting priorities 667 | // 1. color map 668 | // 2. specular map 669 | // 3. normal map 670 | // 4. bump map 671 | // 5. alpha map 672 | // 6. emissive map 673 | 674 | var uvScaleMap; 675 | 676 | if ( material.map ) { 677 | 678 | uvScaleMap = material.map; 679 | 680 | } else if ( material.specularMap ) { 681 | 682 | uvScaleMap = material.specularMap; 683 | 684 | } else if ( material.displacementMap ) { 685 | 686 | uvScaleMap = material.displacementMap; 687 | 688 | } else if ( material.normalMap ) { 689 | 690 | uvScaleMap = material.normalMap; 691 | 692 | } else if ( material.bumpMap ) { 693 | 694 | uvScaleMap = material.bumpMap; 695 | 696 | } else if ( material.glossinessMap ) { 697 | 698 | uvScaleMap = material.glossinessMap; 699 | 700 | } else if ( material.alphaMap ) { 701 | 702 | uvScaleMap = material.alphaMap; 703 | 704 | } else if ( material.emissiveMap ) { 705 | 706 | uvScaleMap = material.emissiveMap; 707 | 708 | } 709 | 710 | if ( uvScaleMap !== undefined ) { 711 | 712 | // backwards compatibility 713 | if ( uvScaleMap.isWebGLRenderTarget ) { 714 | 715 | uvScaleMap = uvScaleMap.texture; 716 | 717 | } 718 | 719 | var offset = uvScaleMap.offset; 720 | var repeat = uvScaleMap.repeat; 721 | 722 | uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); 723 | 724 | } 725 | 726 | uniforms.envMap.value = material.envMap; 727 | uniforms.envMapIntensity.value = material.envMapIntensity; 728 | uniforms.flipEnvMap.value = ( material.envMap && material.envMap.isCubeTexture ) ? -1 : 1; 729 | 730 | uniforms.refractionRatio.value = material.refractionRatio; 731 | 732 | uniforms.specular.value.copy( material.specular ); 733 | uniforms.glossiness.value = material.glossiness; 734 | 735 | uniforms.glossinessMap.value = material.glossinessMap; 736 | 737 | uniforms.emissiveMap.value = material.emissiveMap; 738 | uniforms.bumpMap.value = material.bumpMap; 739 | uniforms.normalMap.value = material.normalMap; 740 | 741 | uniforms.displacementMap.value = material.displacementMap; 742 | uniforms.displacementScale.value = material.displacementScale; 743 | uniforms.displacementBias.value = material.displacementBias; 744 | 745 | if ( uniforms.glossinessMap.value !== null && defines.USE_GLOSSINESSMAP === undefined ) { 746 | 747 | defines.USE_GLOSSINESSMAP = ''; 748 | // set USE_ROUGHNESSMAP to enable vUv 749 | defines.USE_ROUGHNESSMAP = ''; 750 | 751 | } 752 | 753 | if ( uniforms.glossinessMap.value === null && defines.USE_GLOSSINESSMAP !== undefined ) { 754 | 755 | delete defines.USE_GLOSSINESSMAP; 756 | delete defines.USE_ROUGHNESSMAP; 757 | 758 | } 759 | 760 | } 761 | 762 | }; 763 | 764 | } 765 | 766 | /*********************************/ 767 | /********** INTERNALS ************/ 768 | /*********************************/ 769 | 770 | /* CONSTANTS */ 771 | 772 | var WEBGL_CONSTANTS = { 773 | FLOAT: 5126, 774 | //FLOAT_MAT2: 35674, 775 | FLOAT_MAT3: 35675, 776 | FLOAT_MAT4: 35676, 777 | FLOAT_VEC2: 35664, 778 | FLOAT_VEC3: 35665, 779 | FLOAT_VEC4: 35666, 780 | LINEAR: 9729, 781 | REPEAT: 10497, 782 | SAMPLER_2D: 35678, 783 | POINTS: 0, 784 | LINES: 1, 785 | LINE_LOOP: 2, 786 | LINE_STRIP: 3, 787 | TRIANGLES: 4, 788 | TRIANGLE_STRIP: 5, 789 | TRIANGLE_FAN: 6, 790 | UNSIGNED_BYTE: 5121, 791 | UNSIGNED_SHORT: 5123 792 | }; 793 | 794 | var WEBGL_TYPE = { 795 | 5126: Number, 796 | //35674: THREE.Matrix2, 797 | 35675: THREE.Matrix3, 798 | 35676: THREE.Matrix4, 799 | 35664: THREE.Vector2, 800 | 35665: THREE.Vector3, 801 | 35666: THREE.Vector4, 802 | 35678: THREE.Texture 803 | }; 804 | 805 | var WEBGL_COMPONENT_TYPES = { 806 | 5120: Int8Array, 807 | 5121: Uint8Array, 808 | 5122: Int16Array, 809 | 5123: Uint16Array, 810 | 5125: Uint32Array, 811 | 5126: Float32Array 812 | }; 813 | 814 | var WEBGL_FILTERS = { 815 | 9728: THREE.NearestFilter, 816 | 9729: THREE.LinearFilter, 817 | 9984: THREE.NearestMipMapNearestFilter, 818 | 9985: THREE.LinearMipMapNearestFilter, 819 | 9986: THREE.NearestMipMapLinearFilter, 820 | 9987: THREE.LinearMipMapLinearFilter 821 | }; 822 | 823 | var WEBGL_WRAPPINGS = { 824 | 33071: THREE.ClampToEdgeWrapping, 825 | 33648: THREE.MirroredRepeatWrapping, 826 | 10497: THREE.RepeatWrapping 827 | }; 828 | 829 | var WEBGL_TEXTURE_FORMATS = { 830 | 6406: THREE.AlphaFormat, 831 | 6407: THREE.RGBFormat, 832 | 6408: THREE.RGBAFormat, 833 | 6409: THREE.LuminanceFormat, 834 | 6410: THREE.LuminanceAlphaFormat 835 | }; 836 | 837 | var WEBGL_TEXTURE_DATATYPES = { 838 | 5121: THREE.UnsignedByteType, 839 | 32819: THREE.UnsignedShort4444Type, 840 | 32820: THREE.UnsignedShort5551Type, 841 | 33635: THREE.UnsignedShort565Type 842 | }; 843 | 844 | var WEBGL_SIDES = { 845 | 1028: THREE.BackSide, // Culling front 846 | 1029: THREE.FrontSide // Culling back 847 | //1032: THREE.NoSide // Culling front and back, what to do? 848 | }; 849 | 850 | var WEBGL_DEPTH_FUNCS = { 851 | 512: THREE.NeverDepth, 852 | 513: THREE.LessDepth, 853 | 514: THREE.EqualDepth, 854 | 515: THREE.LessEqualDepth, 855 | 516: THREE.GreaterEqualDepth, 856 | 517: THREE.NotEqualDepth, 857 | 518: THREE.GreaterEqualDepth, 858 | 519: THREE.AlwaysDepth 859 | }; 860 | 861 | var WEBGL_BLEND_EQUATIONS = { 862 | 32774: THREE.AddEquation, 863 | 32778: THREE.SubtractEquation, 864 | 32779: THREE.ReverseSubtractEquation 865 | }; 866 | 867 | var WEBGL_BLEND_FUNCS = { 868 | 0: THREE.ZeroFactor, 869 | 1: THREE.OneFactor, 870 | 768: THREE.SrcColorFactor, 871 | 769: THREE.OneMinusSrcColorFactor, 872 | 770: THREE.SrcAlphaFactor, 873 | 771: THREE.OneMinusSrcAlphaFactor, 874 | 772: THREE.DstAlphaFactor, 875 | 773: THREE.OneMinusDstAlphaFactor, 876 | 774: THREE.DstColorFactor, 877 | 775: THREE.OneMinusDstColorFactor, 878 | 776: THREE.SrcAlphaSaturateFactor 879 | // The followings are not supported by Three.js yet 880 | //32769: CONSTANT_COLOR, 881 | //32770: ONE_MINUS_CONSTANT_COLOR, 882 | //32771: CONSTANT_ALPHA, 883 | //32772: ONE_MINUS_CONSTANT_COLOR 884 | }; 885 | 886 | var WEBGL_TYPE_SIZES = { 887 | 'SCALAR': 1, 888 | 'VEC2': 2, 889 | 'VEC3': 3, 890 | 'VEC4': 4, 891 | 'MAT2': 4, 892 | 'MAT3': 9, 893 | 'MAT4': 16 894 | }; 895 | 896 | var PATH_PROPERTIES = { 897 | scale: 'scale', 898 | translation: 'position', 899 | rotation: 'quaternion', 900 | weights: 'morphTargetInfluences' 901 | }; 902 | 903 | var INTERPOLATION = { 904 | CATMULLROMSPLINE: THREE.InterpolateSmooth, 905 | CUBICSPLINE: THREE.InterpolateSmooth, 906 | LINEAR: THREE.InterpolateLinear, 907 | STEP: THREE.InterpolateDiscrete 908 | }; 909 | 910 | var STATES_ENABLES = { 911 | 2884: 'CULL_FACE', 912 | 2929: 'DEPTH_TEST', 913 | 3042: 'BLEND', 914 | 3089: 'SCISSOR_TEST', 915 | 32823: 'POLYGON_OFFSET_FILL', 916 | 32926: 'SAMPLE_ALPHA_TO_COVERAGE' 917 | }; 918 | 919 | var ALPHA_MODES = { 920 | OPAQUE: 'OPAQUE', 921 | MASK: 'MASK', 922 | BLEND: 'BLEND' 923 | }; 924 | 925 | /* UTILITY FUNCTIONS */ 926 | 927 | function _each( object, callback, thisObj ) { 928 | 929 | if ( !object ) { 930 | return Promise.resolve(); 931 | } 932 | 933 | var results; 934 | var fns = []; 935 | 936 | if ( Object.prototype.toString.call( object ) === '[object Array]' ) { 937 | 938 | results = []; 939 | 940 | var length = object.length; 941 | 942 | for ( var idx = 0; idx < length; idx ++ ) { 943 | 944 | var value = callback.call( thisObj || this, object[ idx ], idx ); 945 | 946 | if ( value ) { 947 | 948 | fns.push( value ); 949 | 950 | if ( value instanceof Promise ) { 951 | 952 | value.then( function( key, value ) { 953 | 954 | results[ key ] = value; 955 | 956 | }.bind( this, idx )); 957 | 958 | } else { 959 | 960 | results[ idx ] = value; 961 | 962 | } 963 | 964 | } 965 | 966 | } 967 | 968 | } else { 969 | 970 | results = {}; 971 | 972 | for ( var key in object ) { 973 | 974 | if ( object.hasOwnProperty( key ) ) { 975 | 976 | var value = callback.call( thisObj || this, object[ key ], key ); 977 | 978 | if ( value ) { 979 | 980 | fns.push( value ); 981 | 982 | if ( value instanceof Promise ) { 983 | 984 | value.then( function( key, value ) { 985 | 986 | results[ key ] = value; 987 | 988 | }.bind( this, key )); 989 | 990 | } else { 991 | 992 | results[ key ] = value; 993 | 994 | } 995 | 996 | } 997 | 998 | } 999 | 1000 | } 1001 | 1002 | } 1003 | 1004 | return Promise.all( fns ).then( function() { 1005 | 1006 | return results; 1007 | 1008 | }); 1009 | 1010 | } 1011 | 1012 | function resolveURL( url, path ) { 1013 | 1014 | // Invalid URL 1015 | if ( typeof url !== 'string' || url === '' ) 1016 | return ''; 1017 | 1018 | // Absolute URL http://,https://,// 1019 | if ( /^(https?:)?\/\//i.test( url ) ) { 1020 | 1021 | return url; 1022 | 1023 | } 1024 | 1025 | // Data URI 1026 | if ( /^data:.*,.*$/i.test( url ) ) { 1027 | 1028 | return url; 1029 | 1030 | } 1031 | 1032 | // Blob URL 1033 | if ( /^blob:.*$/i.test( url ) ) { 1034 | 1035 | return url; 1036 | 1037 | } 1038 | 1039 | // Relative URL 1040 | return ( path || '' ) + url; 1041 | 1042 | } 1043 | 1044 | function convertUint8ArrayToString( array ) { 1045 | 1046 | if ( window.TextDecoder !== undefined ) { 1047 | 1048 | return new TextDecoder().decode( array ); 1049 | 1050 | } 1051 | 1052 | // Avoid the String.fromCharCode.apply(null, array) shortcut, which 1053 | // throws a "maximum call stack size exceeded" error for large arrays. 1054 | 1055 | var s = ''; 1056 | 1057 | for ( var i = 0, il = array.length; i < il; i ++ ) { 1058 | 1059 | s += String.fromCharCode( array[ i ] ); 1060 | 1061 | } 1062 | 1063 | return s; 1064 | 1065 | } 1066 | 1067 | /** 1068 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material 1069 | */ 1070 | function createDefaultMaterial() { 1071 | 1072 | return new THREE.MeshStandardMaterial( { 1073 | color: 0xFFFFFF, 1074 | emissive: 0x000000, 1075 | metalness: 1, 1076 | roughness: 1, 1077 | transparent: false, 1078 | depthTest: true, 1079 | side: THREE.FrontSide 1080 | } ); 1081 | 1082 | } 1083 | 1084 | /** 1085 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets 1086 | * @param {THREE.Mesh} mesh 1087 | * @param {GLTF.Mesh} meshDef 1088 | * @param {GLTF.Primitive} primitiveDef 1089 | * @param {Object} dependencies 1090 | */ 1091 | function addMorphTargets ( mesh, meshDef, primitiveDef, dependencies ) { 1092 | 1093 | var geometry = mesh.geometry; 1094 | var material = mesh.material; 1095 | 1096 | var targets = primitiveDef.targets; 1097 | var morphAttributes = geometry.morphAttributes; 1098 | 1099 | morphAttributes.position = []; 1100 | morphAttributes.normal = []; 1101 | 1102 | material.morphTargets = true; 1103 | 1104 | for ( var i = 0, il = targets.length; i < il; i ++ ) { 1105 | 1106 | var target = targets[ i ]; 1107 | var attributeName = 'morphTarget' + i; 1108 | 1109 | var positionAttribute, normalAttribute; 1110 | 1111 | if ( target.POSITION !== undefined ) { 1112 | 1113 | // Three.js morph formula is 1114 | // position 1115 | // + weight0 * ( morphTarget0 - position ) 1116 | // + weight1 * ( morphTarget1 - position ) 1117 | // ... 1118 | // while the glTF one is 1119 | // position 1120 | // + weight0 * morphTarget0 1121 | // + weight1 * morphTarget1 1122 | // ... 1123 | // then adding position to morphTarget. 1124 | // So morphTarget value will depend on mesh's position, then cloning attribute 1125 | // for the case if attribute is shared among two or more meshes. 1126 | 1127 | positionAttribute = dependencies.accessors[ target.POSITION ].clone(); 1128 | var position = geometry.attributes.position; 1129 | 1130 | for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) { 1131 | 1132 | positionAttribute.setXYZ( 1133 | j, 1134 | positionAttribute.getX( j ) + position.getX( j ), 1135 | positionAttribute.getY( j ) + position.getY( j ), 1136 | positionAttribute.getZ( j ) + position.getZ( j ) 1137 | ); 1138 | 1139 | } 1140 | 1141 | } else { 1142 | 1143 | // Copying the original position not to affect the final position. 1144 | // See the formula above. 1145 | positionAttribute = geometry.attributes.position.clone(); 1146 | 1147 | } 1148 | 1149 | if ( target.NORMAL !== undefined ) { 1150 | 1151 | material.morphNormals = true; 1152 | 1153 | // see target.POSITION's comment 1154 | 1155 | normalAttribute = dependencies.accessors[ target.NORMAL ].clone(); 1156 | var normal = geometry.attributes.normal; 1157 | 1158 | for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) { 1159 | 1160 | normalAttribute.setXYZ( 1161 | j, 1162 | normalAttribute.getX( j ) + normal.getX( j ), 1163 | normalAttribute.getY( j ) + normal.getY( j ), 1164 | normalAttribute.getZ( j ) + normal.getZ( j ) 1165 | ); 1166 | 1167 | } 1168 | 1169 | } else { 1170 | 1171 | normalAttribute = geometry.attributes.normal.clone(); 1172 | 1173 | } 1174 | 1175 | if ( target.TANGENT !== undefined ) { 1176 | 1177 | // TODO: implement 1178 | 1179 | } 1180 | 1181 | positionAttribute.name = attributeName; 1182 | normalAttribute.name = attributeName; 1183 | 1184 | morphAttributes.position.push( positionAttribute ); 1185 | morphAttributes.normal.push( normalAttribute ); 1186 | 1187 | } 1188 | 1189 | mesh.updateMorphTargets(); 1190 | 1191 | if ( meshDef.weights !== undefined ) { 1192 | 1193 | for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) { 1194 | 1195 | mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; 1196 | 1197 | } 1198 | 1199 | } 1200 | 1201 | } 1202 | 1203 | /* GLTF PARSER */ 1204 | 1205 | function GLTFParser( json, extensions, options ) { 1206 | 1207 | this.json = json || {}; 1208 | this.extensions = extensions || {}; 1209 | this.options = options || {}; 1210 | 1211 | // loader object cache 1212 | this.cache = new GLTFRegistry(); 1213 | 1214 | } 1215 | 1216 | GLTFParser.prototype._withDependencies = function ( dependencies ) { 1217 | 1218 | var _dependencies = {}; 1219 | 1220 | for ( var i = 0; i < dependencies.length; i ++ ) { 1221 | 1222 | var dependency = dependencies[ i ]; 1223 | var fnName = 'load' + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 ); 1224 | 1225 | var cached = this.cache.get( dependency ); 1226 | 1227 | if ( cached !== undefined ) { 1228 | 1229 | _dependencies[ dependency ] = cached; 1230 | 1231 | } else if ( this[ fnName ] ) { 1232 | 1233 | var fn = this[ fnName ](); 1234 | this.cache.add( dependency, fn ); 1235 | 1236 | _dependencies[ dependency ] = fn; 1237 | 1238 | } 1239 | 1240 | } 1241 | 1242 | return _each( _dependencies, function ( dependency ) { 1243 | 1244 | return dependency; 1245 | 1246 | } ); 1247 | 1248 | }; 1249 | 1250 | GLTFParser.prototype.parse = function ( onLoad, onError ) { 1251 | 1252 | var json = this.json; 1253 | 1254 | // Clear the loader cache 1255 | this.cache.removeAll(); 1256 | 1257 | // Fire the callback on complete 1258 | this._withDependencies( [ 1259 | 1260 | 'scenes', 1261 | 'cameras', 1262 | 'animations' 1263 | 1264 | ] ).then( function ( dependencies ) { 1265 | 1266 | var scenes = []; 1267 | 1268 | for ( var name in dependencies.scenes ) { 1269 | 1270 | scenes.push( dependencies.scenes[ name ] ); 1271 | 1272 | } 1273 | 1274 | var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ]; 1275 | 1276 | var cameras = []; 1277 | 1278 | for ( var name in dependencies.cameras ) { 1279 | 1280 | var camera = dependencies.cameras[ name ]; 1281 | cameras.push( camera ); 1282 | 1283 | } 1284 | 1285 | var animations = []; 1286 | 1287 | for ( var name in dependencies.animations ) { 1288 | 1289 | animations.push( dependencies.animations[ name ] ); 1290 | 1291 | } 1292 | 1293 | onLoad( scene, scenes, cameras, animations ); 1294 | 1295 | } ).catch( onError ); 1296 | 1297 | }; 1298 | 1299 | /** 1300 | * Requests the specified dependency asynchronously, with caching. 1301 | * @param {string} type 1302 | * @param {number} index 1303 | * @return {Promise} 1304 | */ 1305 | GLTFParser.prototype.getDependency = function ( type, index ) { 1306 | 1307 | var cacheKey = type + ':' + index; 1308 | var dependency = this.cache.get( cacheKey ); 1309 | 1310 | if ( !dependency ) { 1311 | 1312 | var fnName = 'load' + type.charAt( 0 ).toUpperCase() + type.slice( 1 ); 1313 | dependency = this[ fnName ]( index ); 1314 | this.cache.add( cacheKey, dependency ); 1315 | 1316 | } 1317 | 1318 | return dependency; 1319 | 1320 | }; 1321 | 1322 | /** 1323 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views 1324 | * @param {number} bufferIndex 1325 | * @return {Promise} 1326 | */ 1327 | GLTFParser.prototype.loadBuffer = function ( bufferIndex ) { 1328 | 1329 | var bufferDef = this.json.buffers[ bufferIndex ]; 1330 | 1331 | if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { 1332 | 1333 | throw new Error( 'THREE.GLTFLoader: %s buffer type is not supported.', bufferDef.type ); 1334 | 1335 | } 1336 | 1337 | // If present, GLB container is required to be the first buffer. 1338 | if ( bufferDef.uri === undefined && bufferIndex === 0 ) { 1339 | 1340 | return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body ); 1341 | 1342 | } 1343 | 1344 | var options = this.options; 1345 | 1346 | return new Promise( function ( resolve ) { 1347 | 1348 | var loader = new THREE.FileLoader(); 1349 | loader.setResponseType( 'arraybuffer' ); 1350 | loader.load( resolveURL( bufferDef.uri, options.path ), resolve); 1351 | 1352 | } ); 1353 | 1354 | }; 1355 | 1356 | /** 1357 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views 1358 | * @param {number} bufferViewIndex 1359 | * @return {Promise} 1360 | */ 1361 | GLTFParser.prototype.loadBufferView = function ( bufferViewIndex ) { 1362 | 1363 | var bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; 1364 | 1365 | return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { 1366 | 1367 | var byteLength = bufferViewDef.byteLength || 0; 1368 | var byteOffset = bufferViewDef.byteOffset || 0; 1369 | return buffer.slice( byteOffset, byteOffset + byteLength ); 1370 | 1371 | } ); 1372 | 1373 | }; 1374 | 1375 | GLTFParser.prototype.loadAccessors = function () { 1376 | 1377 | var parser = this; 1378 | var json = this.json; 1379 | 1380 | return _each( json.accessors, function ( accessor ) { 1381 | 1382 | return parser.getDependency( 'bufferView', accessor.bufferView ).then( function ( bufferView ) { 1383 | 1384 | var itemSize = WEBGL_TYPE_SIZES[ accessor.type ]; 1385 | var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ]; 1386 | 1387 | // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. 1388 | var elementBytes = TypedArray.BYTES_PER_ELEMENT; 1389 | var itemBytes = elementBytes * itemSize; 1390 | var byteStride = json.bufferViews[ accessor.bufferView ].byteStride; 1391 | var array; 1392 | 1393 | // The buffer is not interleaved if the stride is the item size in bytes. 1394 | if ( byteStride && byteStride !== itemBytes ) { 1395 | 1396 | // Use the full buffer if it's interleaved. 1397 | array = new TypedArray( bufferView ); 1398 | 1399 | // Integer parameters to IB/IBA are in array elements, not bytes. 1400 | var ib = new THREE.InterleavedBuffer( array, byteStride / elementBytes ); 1401 | 1402 | return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes ); 1403 | 1404 | } else { 1405 | 1406 | array = new TypedArray( bufferView, accessor.byteOffset, accessor.count * itemSize ); 1407 | 1408 | return new THREE.BufferAttribute( array, itemSize ); 1409 | 1410 | } 1411 | 1412 | } ); 1413 | 1414 | } ); 1415 | 1416 | }; 1417 | 1418 | /** 1419 | * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures 1420 | * @param {number} textureIndex 1421 | * @return {Promise} 1422 | */ 1423 | GLTFParser.prototype.loadTexture = function ( textureIndex ) { 1424 | 1425 | var parser = this; 1426 | var json = this.json; 1427 | var options = this.options; 1428 | 1429 | var URL = window.URL || window.webkitURL; 1430 | 1431 | var textureDef = json.textures[ textureIndex ]; 1432 | var source = json.images[ textureDef.source ]; 1433 | var sourceURI = source.uri; 1434 | var isObjectURL = false; 1435 | 1436 | if ( source.bufferView !== undefined ) { 1437 | 1438 | // Load binary image data from bufferView, if provided. 1439 | 1440 | sourceURI = parser.getDependency( 'bufferView', source.bufferView ) 1441 | .then( function ( bufferView ) { 1442 | 1443 | isObjectURL = true; 1444 | var blob = new Blob( [ bufferView ], { type: source.mimeType } ); 1445 | sourceURI = URL.createObjectURL( blob ); 1446 | return sourceURI; 1447 | 1448 | } ); 1449 | 1450 | } 1451 | 1452 | return Promise.resolve( sourceURI ).then( function ( sourceURI ) { 1453 | 1454 | // Load Texture resource. 1455 | 1456 | var textureLoader = THREE.Loader.Handlers.get( sourceURI ) || new THREE.TextureLoader(); 1457 | textureLoader.setCrossOrigin( options.crossOrigin ); 1458 | 1459 | return new Promise( function ( resolve, reject ) { 1460 | 1461 | textureLoader.load( resolveURL( sourceURI, options.path ), resolve, undefined, reject ); 1462 | 1463 | } ); 1464 | 1465 | } ).then( function ( texture ) { 1466 | 1467 | // Clean up resources and configure Texture. 1468 | 1469 | if ( isObjectURL !== undefined ) { 1470 | 1471 | URL.revokeObjectURL( sourceURI ); 1472 | 1473 | } 1474 | 1475 | texture.flipY = false; 1476 | 1477 | if ( textureDef.name !== undefined ) texture.name = textureDef.name; 1478 | 1479 | texture.format = textureDef.format !== undefined ? WEBGL_TEXTURE_FORMATS[ textureDef.format ] : THREE.RGBAFormat; 1480 | 1481 | if ( textureDef.internalFormat !== undefined && texture.format !== WEBGL_TEXTURE_FORMATS[ textureDef.internalFormat ] ) { 1482 | 1483 | console.warn( 'THREE.GLTFLoader: Three.js does not support texture internalFormat which is different from texture format. ' + 1484 | 'internalFormat will be forced to be the same value as format.' ); 1485 | 1486 | } 1487 | 1488 | texture.type = textureDef.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ textureDef.type ] : THREE.UnsignedByteType; 1489 | 1490 | var samplers = json.samplers || {}; 1491 | var sampler = samplers[ textureDef.sampler ] || {}; 1492 | 1493 | texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter; 1494 | texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipMapLinearFilter; 1495 | texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping; 1496 | texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping; 1497 | 1498 | return texture; 1499 | 1500 | } ); 1501 | 1502 | }; 1503 | 1504 | /** 1505 | * Asynchronously assigns a texture to the given material parameters. 1506 | * @param {Object} materialParams 1507 | * @param {string} textureName 1508 | * @param {number} textureIndex 1509 | * @return {Promise} 1510 | */ 1511 | GLTFParser.prototype.assignTexture = function ( materialParams, textureName, textureIndex ) { 1512 | 1513 | return this.getDependency( 'texture', textureIndex ).then( function ( texture ) { 1514 | 1515 | materialParams[ textureName ] = texture; 1516 | 1517 | } ); 1518 | 1519 | }; 1520 | 1521 | /** 1522 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials 1523 | * @return {Promise>} 1524 | */ 1525 | GLTFParser.prototype.loadMaterials = function () { 1526 | 1527 | var parser = this; 1528 | var json = this.json; 1529 | var extensions = this.extensions; 1530 | 1531 | return _each( json.materials, function ( material ) { 1532 | 1533 | var materialType; 1534 | var materialParams = {}; 1535 | var materialExtensions = material.extensions || {}; 1536 | 1537 | var pending = []; 1538 | 1539 | if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) { 1540 | 1541 | var khcExtension = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ]; 1542 | materialType = khcExtension.getMaterialType( material ); 1543 | pending.push( khcExtension.extendParams( materialParams, material, parser ) ); 1544 | 1545 | } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { 1546 | 1547 | var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; 1548 | materialType = sgExtension.getMaterialType( material ); 1549 | pending.push( sgExtension.extendParams( materialParams, material, parser ) ); 1550 | 1551 | } else if ( material.pbrMetallicRoughness !== undefined ) { 1552 | 1553 | // Specification: 1554 | // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material 1555 | 1556 | materialType = THREE.MeshStandardMaterial; 1557 | 1558 | var metallicRoughness = material.pbrMetallicRoughness; 1559 | 1560 | materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 ); 1561 | materialParams.opacity = 1.0; 1562 | 1563 | if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { 1564 | 1565 | var array = metallicRoughness.baseColorFactor; 1566 | 1567 | materialParams.color.fromArray( array ); 1568 | materialParams.opacity = array[ 3 ]; 1569 | 1570 | } 1571 | 1572 | if ( metallicRoughness.baseColorTexture !== undefined ) { 1573 | 1574 | pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture.index ) ); 1575 | 1576 | } 1577 | 1578 | materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; 1579 | materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; 1580 | 1581 | if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { 1582 | 1583 | var textureIndex = metallicRoughness.metallicRoughnessTexture.index; 1584 | pending.push( parser.assignTexture( materialParams, 'metalnessMap', textureIndex ) ); 1585 | pending.push( parser.assignTexture( materialParams, 'roughnessMap', textureIndex ) ); 1586 | 1587 | } 1588 | 1589 | } else { 1590 | 1591 | materialType = THREE.MeshPhongMaterial; 1592 | 1593 | } 1594 | 1595 | if ( material.doubleSided === true ) { 1596 | 1597 | materialParams.side = THREE.DoubleSide; 1598 | 1599 | } 1600 | 1601 | var alphaMode = material.alphaMode || ALPHA_MODES.OPAQUE; 1602 | 1603 | if ( alphaMode !== ALPHA_MODES.OPAQUE ) { 1604 | 1605 | materialParams.transparent = true; 1606 | 1607 | } else { 1608 | 1609 | materialParams.transparent = false; 1610 | 1611 | } 1612 | 1613 | if ( material.normalTexture !== undefined ) { 1614 | 1615 | pending.push( parser.assignTexture( materialParams, 'normalMap', material.normalTexture.index ) ); 1616 | 1617 | } 1618 | 1619 | if ( material.occlusionTexture !== undefined ) { 1620 | 1621 | pending.push( parser.assignTexture( materialParams, 'aoMap', material.occlusionTexture.index ) ); 1622 | 1623 | } 1624 | 1625 | if ( material.emissiveFactor !== undefined ) { 1626 | 1627 | if ( materialType === THREE.MeshBasicMaterial ) { 1628 | 1629 | materialParams.color = new THREE.Color().fromArray( material.emissiveFactor ); 1630 | 1631 | } else { 1632 | 1633 | materialParams.emissive = new THREE.Color().fromArray( material.emissiveFactor ); 1634 | 1635 | } 1636 | 1637 | } 1638 | 1639 | if ( material.emissiveTexture !== undefined ) { 1640 | 1641 | if ( materialType === THREE.MeshBasicMaterial ) { 1642 | 1643 | pending.push( parser.assignTexture( materialParams, 'map', material.emissiveTexture.index ) ); 1644 | 1645 | } else { 1646 | 1647 | pending.push( parser.assignTexture( materialParams, 'emissiveMap', material.emissiveTexture.index ) ); 1648 | 1649 | } 1650 | 1651 | } 1652 | 1653 | return Promise.all( pending ).then( function () { 1654 | 1655 | var _material; 1656 | 1657 | if ( materialType === THREE.ShaderMaterial ) { 1658 | 1659 | _material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); 1660 | 1661 | } else { 1662 | 1663 | _material = new materialType( materialParams ); 1664 | 1665 | } 1666 | 1667 | if ( material.name !== undefined ) _material.name = material.name; 1668 | 1669 | // Normal map textures use OpenGL conventions: 1670 | // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture 1671 | _material.normalScale.x = -1; 1672 | 1673 | _material.userData = material.extras; 1674 | 1675 | return _material; 1676 | 1677 | } ); 1678 | 1679 | } ); 1680 | 1681 | }; 1682 | 1683 | GLTFParser.prototype.loadGeometries = function ( primitives ) { 1684 | 1685 | return this._withDependencies( [ 1686 | 1687 | 'accessors', 1688 | 1689 | ] ).then( function ( dependencies ) { 1690 | 1691 | return _each( primitives, function ( primitive ) { 1692 | 1693 | var geometry = new THREE.BufferGeometry(); 1694 | 1695 | var attributes = primitive.attributes; 1696 | 1697 | for ( var attributeId in attributes ) { 1698 | 1699 | var attributeEntry = attributes[ attributeId ]; 1700 | 1701 | if ( attributeEntry === undefined ) return; 1702 | 1703 | var bufferAttribute = dependencies.accessors[ attributeEntry ]; 1704 | 1705 | switch ( attributeId ) { 1706 | 1707 | case 'POSITION': 1708 | 1709 | geometry.addAttribute( 'position', bufferAttribute ); 1710 | break; 1711 | 1712 | case 'NORMAL': 1713 | 1714 | geometry.addAttribute( 'normal', bufferAttribute ); 1715 | break; 1716 | 1717 | case 'TEXCOORD_0': 1718 | case 'TEXCOORD0': 1719 | case 'TEXCOORD': 1720 | 1721 | geometry.addAttribute( 'uv', bufferAttribute ); 1722 | break; 1723 | 1724 | case 'TEXCOORD_1': 1725 | 1726 | geometry.addAttribute( 'uv2', bufferAttribute ); 1727 | break; 1728 | 1729 | case 'COLOR_0': 1730 | case 'COLOR0': 1731 | case 'COLOR': 1732 | 1733 | geometry.addAttribute( 'color', bufferAttribute ); 1734 | break; 1735 | 1736 | case 'WEIGHTS_0': 1737 | case 'WEIGHT': // WEIGHT semantic deprecated. 1738 | 1739 | geometry.addAttribute( 'skinWeight', bufferAttribute ); 1740 | break; 1741 | 1742 | case 'JOINTS_0': 1743 | case 'JOINT': // JOINT semantic deprecated. 1744 | 1745 | geometry.addAttribute( 'skinIndex', bufferAttribute ); 1746 | break; 1747 | 1748 | } 1749 | 1750 | } 1751 | 1752 | if ( primitive.indices !== undefined ) { 1753 | 1754 | geometry.setIndex( dependencies.accessors[ primitive.indices ] ); 1755 | 1756 | } 1757 | 1758 | return geometry; 1759 | 1760 | } ); 1761 | 1762 | } ); 1763 | 1764 | }; 1765 | 1766 | /** 1767 | * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes 1768 | */ 1769 | GLTFParser.prototype.loadMeshes = function () { 1770 | 1771 | var scope = this; 1772 | var json = this.json; 1773 | 1774 | return this._withDependencies( [ 1775 | 1776 | 'accessors', 1777 | 'materials' 1778 | 1779 | ] ).then( function ( dependencies ) { 1780 | 1781 | return _each( json.meshes, function ( meshDef ) { 1782 | 1783 | var group = new THREE.Group(); 1784 | 1785 | if ( meshDef.name !== undefined ) group.name = meshDef.name; 1786 | if ( meshDef.extras ) group.userData = meshDef.extras; 1787 | 1788 | var primitives = meshDef.primitives || []; 1789 | 1790 | return scope.loadGeometries( primitives ).then( function ( geometries ) { 1791 | 1792 | for ( var name in primitives ) { 1793 | 1794 | var primitive = primitives[ name ]; 1795 | var geometry = geometries[ name ]; 1796 | 1797 | var material = primitive.material === undefined 1798 | ? createDefaultMaterial() 1799 | : dependencies.materials[ primitive.material ]; 1800 | 1801 | if ( material.aoMap 1802 | && geometry.attributes.uv2 === undefined 1803 | && geometry.attributes.uv !== undefined ) { 1804 | 1805 | console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' ); 1806 | geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) ); 1807 | 1808 | } 1809 | 1810 | if ( geometry.attributes.color !== undefined ) { 1811 | 1812 | material.vertexColors = THREE.VertexColors; 1813 | material.needsUpdate = true; 1814 | 1815 | } 1816 | 1817 | if ( geometry.attributes.normal === undefined ) { 1818 | 1819 | if ( material.flatShading !== undefined ) { 1820 | 1821 | material.flatShading = true; 1822 | 1823 | } else { 1824 | 1825 | // TODO: Remove this backwards-compatibility fix after r87 release. 1826 | material.shading = THREE.FlatShading; 1827 | 1828 | } 1829 | 1830 | } 1831 | 1832 | var mesh; 1833 | 1834 | if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) { 1835 | 1836 | mesh = new THREE.Mesh( geometry, material ); 1837 | 1838 | } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) { 1839 | 1840 | mesh = new THREE.Mesh( geometry, material ); 1841 | mesh.drawMode = THREE.TriangleStripDrawMode; 1842 | 1843 | } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) { 1844 | 1845 | mesh = new THREE.Mesh( geometry, material ); 1846 | mesh.drawMode = THREE.TriangleFanDrawMode; 1847 | 1848 | } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) { 1849 | 1850 | mesh = new THREE.LineSegments( geometry, material ); 1851 | 1852 | } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) { 1853 | 1854 | mesh = new THREE.Line( geometry, material ); 1855 | 1856 | } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) { 1857 | 1858 | mesh = new THREE.LineLoop( geometry, material ); 1859 | 1860 | } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) { 1861 | 1862 | mesh = new THREE.Points( geometry, material ); 1863 | 1864 | } else { 1865 | 1866 | throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ', primitive.mode ); 1867 | 1868 | } 1869 | 1870 | mesh.name = group.name + '_' + name; 1871 | 1872 | if ( primitive.targets !== undefined ) { 1873 | 1874 | addMorphTargets( mesh, meshDef, primitive, dependencies ); 1875 | 1876 | } 1877 | 1878 | if ( primitive.extras ) mesh.userData = primitive.extras; 1879 | 1880 | group.add( mesh ); 1881 | 1882 | } 1883 | 1884 | return group; 1885 | 1886 | } ); 1887 | 1888 | } ); 1889 | 1890 | } ); 1891 | 1892 | }; 1893 | 1894 | /** 1895 | * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras 1896 | */ 1897 | GLTFParser.prototype.loadCameras = function () { 1898 | 1899 | var json = this.json; 1900 | 1901 | return _each( json.cameras, function ( camera ) { 1902 | 1903 | var _camera; 1904 | 1905 | var params = camera[ camera.type ]; 1906 | 1907 | if ( !params ) { 1908 | 1909 | console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); 1910 | return; 1911 | 1912 | } 1913 | 1914 | if ( camera.type === 'perspective' ) { 1915 | 1916 | var aspectRatio = params.aspectRatio || 1; 1917 | var xfov = params.yfov * aspectRatio; 1918 | 1919 | _camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, params.znear || 1, params.zfar || 2e6 ); 1920 | 1921 | } else if ( camera.type === 'orthographic' ) { 1922 | 1923 | _camera = new THREE.OrthographicCamera( params.xmag / -2, params.xmag / 2, params.ymag / 2, params.ymag / -2, params.znear, params.zfar ); 1924 | 1925 | } 1926 | 1927 | if ( camera.name !== undefined ) _camera.name = camera.name; 1928 | if ( camera.extras ) _camera.userData = camera.extras; 1929 | 1930 | return _camera; 1931 | 1932 | } ); 1933 | 1934 | }; 1935 | 1936 | GLTFParser.prototype.loadSkins = function () { 1937 | 1938 | var json = this.json; 1939 | 1940 | return this._withDependencies( [ 1941 | 1942 | 'accessors' 1943 | 1944 | ] ).then( function ( dependencies ) { 1945 | 1946 | return _each( json.skins, function ( skin ) { 1947 | 1948 | var _skin = { 1949 | joints: skin.joints, 1950 | inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ] 1951 | }; 1952 | 1953 | return _skin; 1954 | 1955 | } ); 1956 | 1957 | } ); 1958 | 1959 | }; 1960 | 1961 | GLTFParser.prototype.loadAnimations = function () { 1962 | 1963 | var json = this.json; 1964 | 1965 | return this._withDependencies( [ 1966 | 1967 | 'accessors', 1968 | 'nodes' 1969 | 1970 | ] ).then( function ( dependencies ) { 1971 | 1972 | return _each( json.animations, function ( animation, animationId ) { 1973 | 1974 | var tracks = []; 1975 | 1976 | for ( var channelId in animation.channels ) { 1977 | 1978 | var channel = animation.channels[ channelId ]; 1979 | var sampler = animation.samplers[ channel.sampler ]; 1980 | 1981 | if ( sampler ) { 1982 | 1983 | var target = channel.target; 1984 | var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. 1985 | var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input; 1986 | var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output; 1987 | 1988 | var inputAccessor = dependencies.accessors[ input ]; 1989 | var outputAccessor = dependencies.accessors[ output ]; 1990 | 1991 | var node = dependencies.nodes[ name ]; 1992 | 1993 | if ( node ) { 1994 | 1995 | node.updateMatrix(); 1996 | node.matrixAutoUpdate = true; 1997 | 1998 | var TypedKeyframeTrack; 1999 | 2000 | switch ( PATH_PROPERTIES[ target.path ] ) { 2001 | 2002 | case PATH_PROPERTIES.weights: 2003 | 2004 | TypedKeyframeTrack = THREE.NumberKeyframeTrack; 2005 | break; 2006 | 2007 | case PATH_PROPERTIES.rotation: 2008 | 2009 | TypedKeyframeTrack = THREE.QuaternionKeyframeTrack; 2010 | break; 2011 | 2012 | case PATH_PROPERTIES.position: 2013 | case PATH_PROPERTIES.scale: 2014 | default: 2015 | 2016 | TypedKeyframeTrack = THREE.VectorKeyframeTrack; 2017 | break; 2018 | 2019 | } 2020 | 2021 | var targetName = node.name ? node.name : node.uuid; 2022 | 2023 | if ( sampler.interpolation === 'CATMULLROMSPLINE' ) { 2024 | 2025 | console.warn( 'THREE.GLTFLoader: CATMULLROMSPLINE interpolation is not supported. Using CUBICSPLINE instead.' ); 2026 | 2027 | } 2028 | 2029 | var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear; 2030 | 2031 | var targetNames = []; 2032 | 2033 | if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) { 2034 | 2035 | // node should be THREE.Group here but 2036 | // PATH_PROPERTIES.weights(morphTargetInfluences) should be 2037 | // the property of a mesh object under node. 2038 | // So finding targets here. 2039 | 2040 | node.traverse( function ( object ) { 2041 | 2042 | if ( object.isMesh === true && object.material.morphTargets === true ) { 2043 | 2044 | targetNames.push( object.name ? object.name : object.uuid ); 2045 | 2046 | } 2047 | 2048 | } ); 2049 | 2050 | } else { 2051 | 2052 | targetNames.push( targetName ); 2053 | 2054 | } 2055 | 2056 | // KeyframeTrack.optimize() will modify given 'times' and 'values' 2057 | // buffers before creating a truncated copy to keep. Because buffers may 2058 | // be reused by other tracks, make copies here. 2059 | for ( var i = 0, il = targetNames.length; i < il; i ++ ) { 2060 | 2061 | tracks.push( new TypedKeyframeTrack( 2062 | targetNames[ i ] + '.' + PATH_PROPERTIES[ target.path ], 2063 | THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ), 2064 | THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ), 2065 | interpolation 2066 | ) ); 2067 | 2068 | } 2069 | 2070 | } 2071 | 2072 | } 2073 | 2074 | } 2075 | 2076 | var name = animation.name !== undefined ? animation.name : 'animation_' + animationId; 2077 | 2078 | return new THREE.AnimationClip( name, undefined, tracks ); 2079 | 2080 | } ); 2081 | 2082 | } ); 2083 | 2084 | }; 2085 | 2086 | GLTFParser.prototype.loadNodes = function () { 2087 | 2088 | var json = this.json; 2089 | var extensions = this.extensions; 2090 | var scope = this; 2091 | 2092 | var nodes = json.nodes || []; 2093 | var skins = json.skins || []; 2094 | 2095 | // Nothing in the node definition indicates whether it is a Bone or an 2096 | // Object3D. Use the skins' joint references to mark bones. 2097 | skins.forEach( function ( skin ) { 2098 | 2099 | skin.joints.forEach( function ( id ) { 2100 | 2101 | nodes[ id ].isBone = true; 2102 | 2103 | } ); 2104 | 2105 | } ); 2106 | 2107 | return _each( json.nodes, function ( node ) { 2108 | 2109 | var matrix = new THREE.Matrix4(); 2110 | 2111 | var _node = node.isBone === true ? new THREE.Bone() : new THREE.Object3D(); 2112 | 2113 | if ( node.name !== undefined ) { 2114 | 2115 | _node.name = THREE.PropertyBinding.sanitizeNodeName( node.name ); 2116 | 2117 | } 2118 | 2119 | if ( node.extras ) _node.userData = node.extras; 2120 | 2121 | if ( node.matrix !== undefined ) { 2122 | 2123 | matrix.fromArray( node.matrix ); 2124 | _node.applyMatrix( matrix ); 2125 | 2126 | } else { 2127 | 2128 | if ( node.translation !== undefined ) { 2129 | 2130 | _node.position.fromArray( node.translation ); 2131 | 2132 | } 2133 | 2134 | if ( node.rotation !== undefined ) { 2135 | 2136 | _node.quaternion.fromArray( node.rotation ); 2137 | 2138 | } 2139 | 2140 | if ( node.scale !== undefined ) { 2141 | 2142 | _node.scale.fromArray( node.scale ); 2143 | 2144 | } 2145 | 2146 | } 2147 | 2148 | return _node; 2149 | 2150 | } ).then( function ( __nodes ) { 2151 | 2152 | return scope._withDependencies( [ 2153 | 2154 | 'meshes', 2155 | 'skins', 2156 | 'cameras' 2157 | 2158 | ] ).then( function ( dependencies ) { 2159 | 2160 | return _each( __nodes, function ( _node, nodeId ) { 2161 | 2162 | var node = json.nodes[ nodeId ]; 2163 | 2164 | var meshes; 2165 | 2166 | if ( node.mesh !== undefined) { 2167 | 2168 | meshes = [ node.mesh ]; 2169 | 2170 | } else if ( node.meshes !== undefined ) { 2171 | 2172 | console.warn( 'THREE.GLTFLoader: Legacy glTF file detected. Nodes may have no more than one mesh.' ); 2173 | 2174 | meshes = node.meshes; 2175 | 2176 | } 2177 | 2178 | if ( meshes !== undefined ) { 2179 | 2180 | for ( var meshId in meshes ) { 2181 | 2182 | var mesh = meshes[ meshId ]; 2183 | var group = dependencies.meshes[ mesh ]; 2184 | 2185 | if ( group === undefined ) { 2186 | 2187 | console.warn( 'THREE.GLTFLoader: Could not find node "' + mesh + '".' ); 2188 | continue; 2189 | 2190 | } 2191 | 2192 | // do not clone children as they will be replaced anyway 2193 | var clonedgroup = group.clone( false ); 2194 | 2195 | for ( var childrenId in group.children ) { 2196 | 2197 | var child = group.children[ childrenId ]; 2198 | var originalChild = child; 2199 | 2200 | // clone Mesh to add to _node 2201 | 2202 | var originalMaterial = child.material; 2203 | var originalGeometry = child.geometry; 2204 | var originalInfluences = child.morphTargetInfluences; 2205 | var originalUserData = child.userData; 2206 | var originalName = child.name; 2207 | 2208 | var material = originalMaterial; 2209 | 2210 | switch ( child.type ) { 2211 | 2212 | case 'LineSegments': 2213 | child = new THREE.LineSegments( originalGeometry, material ); 2214 | break; 2215 | 2216 | case 'LineLoop': 2217 | child = new THREE.LineLoop( originalGeometry, material ); 2218 | break; 2219 | 2220 | case 'Line': 2221 | child = new THREE.Line( originalGeometry, material ); 2222 | break; 2223 | 2224 | case 'Points': 2225 | child = new THREE.Points( originalGeometry, material ); 2226 | break; 2227 | 2228 | default: 2229 | child = new THREE.Mesh( originalGeometry, material ); 2230 | child.drawMode = originalChild.drawMode; 2231 | 2232 | } 2233 | 2234 | child.castShadow = true; 2235 | child.morphTargetInfluences = originalInfluences; 2236 | child.userData = originalUserData; 2237 | child.name = originalName; 2238 | 2239 | var skinEntry; 2240 | 2241 | if ( node.skin !== undefined ) { 2242 | 2243 | skinEntry = dependencies.skins[ node.skin ]; 2244 | 2245 | } 2246 | 2247 | // Replace Mesh with SkinnedMesh in library 2248 | if ( skinEntry ) { 2249 | 2250 | var geometry = originalGeometry; 2251 | material = originalMaterial; 2252 | material.skinning = true; 2253 | 2254 | child = new THREE.SkinnedMesh( geometry, material ); 2255 | child.castShadow = true; 2256 | child.userData = originalUserData; 2257 | child.name = originalName; 2258 | 2259 | var bones = []; 2260 | var boneInverses = []; 2261 | 2262 | for ( var i = 0, l = skinEntry.joints.length; i < l; i ++ ) { 2263 | 2264 | var jointId = skinEntry.joints[ i ]; 2265 | var jointNode = __nodes[ jointId ]; 2266 | 2267 | if ( jointNode ) { 2268 | 2269 | bones.push( jointNode ); 2270 | 2271 | var m = skinEntry.inverseBindMatrices.array; 2272 | var mat = new THREE.Matrix4().fromArray( m, i * 16 ); 2273 | boneInverses.push( mat ); 2274 | 2275 | } else { 2276 | 2277 | console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', jointId ); 2278 | 2279 | } 2280 | 2281 | } 2282 | 2283 | child.bind( new THREE.Skeleton( bones, boneInverses ), child.matrixWorld ); 2284 | 2285 | } 2286 | 2287 | clonedgroup.add( child ); 2288 | 2289 | } 2290 | 2291 | _node.add( clonedgroup ); 2292 | 2293 | } 2294 | 2295 | } 2296 | 2297 | if ( node.camera !== undefined ) { 2298 | 2299 | var camera = dependencies.cameras[ node.camera ]; 2300 | 2301 | _node.add( camera ); 2302 | 2303 | } 2304 | 2305 | if ( node.extensions 2306 | && node.extensions[ EXTENSIONS.KHR_LIGHTS ] 2307 | && node.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) { 2308 | 2309 | var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights; 2310 | _node.add( lights[ node.extensions[ EXTENSIONS.KHR_LIGHTS ].light ] ); 2311 | 2312 | } 2313 | 2314 | return _node; 2315 | 2316 | } ); 2317 | 2318 | } ); 2319 | 2320 | } ); 2321 | 2322 | }; 2323 | 2324 | GLTFParser.prototype.loadScenes = function () { 2325 | 2326 | var json = this.json; 2327 | var extensions = this.extensions; 2328 | 2329 | // scene node hierachy builder 2330 | 2331 | function buildNodeHierachy( nodeId, parentObject, allNodes ) { 2332 | 2333 | var _node = allNodes[ nodeId ]; 2334 | parentObject.add( _node ); 2335 | 2336 | var node = json.nodes[ nodeId ]; 2337 | 2338 | if ( node.children ) { 2339 | 2340 | var children = node.children; 2341 | 2342 | for ( var i = 0, l = children.length; i < l; i ++ ) { 2343 | 2344 | var child = children[ i ]; 2345 | buildNodeHierachy( child, _node, allNodes ); 2346 | 2347 | } 2348 | 2349 | } 2350 | 2351 | } 2352 | 2353 | return this._withDependencies( [ 2354 | 2355 | 'nodes' 2356 | 2357 | ] ).then( function ( dependencies ) { 2358 | 2359 | return _each( json.scenes, function ( scene ) { 2360 | 2361 | var _scene = new THREE.Scene(); 2362 | if ( scene.name !== undefined ) _scene.name = scene.name; 2363 | 2364 | if ( scene.extras ) _scene.userData = scene.extras; 2365 | 2366 | var nodes = scene.nodes || []; 2367 | 2368 | for ( var i = 0, l = nodes.length; i < l; i ++ ) { 2369 | 2370 | var nodeId = nodes[ i ]; 2371 | buildNodeHierachy( nodeId, _scene, dependencies.nodes ); 2372 | 2373 | } 2374 | 2375 | _scene.traverse( function ( child ) { 2376 | 2377 | // for Specular-Glossiness. 2378 | if ( child.material && child.material.isGLTFSpecularGlossinessMaterial ) { 2379 | 2380 | child.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms; 2381 | 2382 | } 2383 | 2384 | } ); 2385 | 2386 | // Ambient lighting, if present, is always attached to the scene root. 2387 | if ( scene.extensions 2388 | && scene.extensions[ EXTENSIONS.KHR_LIGHTS ] 2389 | && scene.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) { 2390 | 2391 | var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights; 2392 | _scene.add( lights[ scene.extensions[ EXTENSIONS.KHR_LIGHTS ].light ] ); 2393 | 2394 | } 2395 | 2396 | return _scene; 2397 | 2398 | } ); 2399 | 2400 | } ); 2401 | 2402 | }; 2403 | 2404 | export default GLTFLoader 2405 | --------------------------------------------------------------------------------