├── 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