@threlte/gltf 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/cli.js +85 -0
- package/logo.svg +145 -0
- package/package.json +71 -0
- package/readme.md +246 -0
- package/src/bin/DRACOLoader.js +208 -0
- package/src/bin/GLTFLoader.js +2734 -0
- package/src/index.js +76 -0
- package/src/utils/exports.js +5 -0
- package/src/utils/glftLoader.js +4 -0
- package/src/utils/isVarName.js +10 -0
- package/src/utils/parser.js +516 -0
- package/src/utils/transform.js +48 -0
|
@@ -0,0 +1,2734 @@
|
|
|
1
|
+
import THREE from 'three'
|
|
2
|
+
|
|
3
|
+
const GLTFLoader = (THREE.GLTFLoader = (function () {
|
|
4
|
+
function GLTFLoader(manager) {
|
|
5
|
+
THREE.Loader.call(this, manager)
|
|
6
|
+
|
|
7
|
+
this.dracoLoader = null
|
|
8
|
+
this.ddsLoader = null
|
|
9
|
+
this.ktx2Loader = null
|
|
10
|
+
|
|
11
|
+
this.pluginCallbacks = []
|
|
12
|
+
|
|
13
|
+
this.register(function (parser) {
|
|
14
|
+
return new GLTFMaterialsClearcoatExtension(parser)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
this.register(function (parser) {
|
|
18
|
+
return new GLTFTextureBasisUExtension(parser)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
this.register(function (parser) {
|
|
22
|
+
return new GLTFMaterialsTransmissionExtension(parser)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
this.register(function (parser) {
|
|
26
|
+
return new GLTFLightsExtension(parser)
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
GLTFLoader.prototype = Object.assign(Object.create(THREE.Loader.prototype), {
|
|
31
|
+
constructor: GLTFLoader,
|
|
32
|
+
|
|
33
|
+
load: function (url, onLoad, onProgress, onError) {
|
|
34
|
+
var scope = this
|
|
35
|
+
|
|
36
|
+
var resourcePath
|
|
37
|
+
|
|
38
|
+
if (this.resourcePath !== '') {
|
|
39
|
+
resourcePath = this.resourcePath
|
|
40
|
+
} else if (this.path !== '') {
|
|
41
|
+
resourcePath = this.path
|
|
42
|
+
} else {
|
|
43
|
+
resourcePath = THREE.LoaderUtils.extractUrlBase(url)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Tells the LoadingManager to track an extra item, which resolves after
|
|
47
|
+
// the model is fully loaded. This means the count of items loaded will
|
|
48
|
+
// be incorrect, but ensures manager.onLoad() does not fire early.
|
|
49
|
+
this.manager.itemStart(url)
|
|
50
|
+
|
|
51
|
+
var _onError = function (e) {
|
|
52
|
+
if (onError) {
|
|
53
|
+
onError(e)
|
|
54
|
+
} else {
|
|
55
|
+
console.error(e)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
scope.manager.itemError(url)
|
|
59
|
+
scope.manager.itemEnd(url)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var loader = new THREE.FileLoader(this.manager)
|
|
63
|
+
|
|
64
|
+
loader.setPath(this.path)
|
|
65
|
+
loader.setResponseType('arraybuffer')
|
|
66
|
+
loader.setRequestHeader(this.requestHeader)
|
|
67
|
+
loader.setWithCredentials(this.withCredentials)
|
|
68
|
+
|
|
69
|
+
loader.load(
|
|
70
|
+
url,
|
|
71
|
+
function (data) {
|
|
72
|
+
try {
|
|
73
|
+
scope.parse(
|
|
74
|
+
data,
|
|
75
|
+
resourcePath,
|
|
76
|
+
function (gltf) {
|
|
77
|
+
onLoad(gltf)
|
|
78
|
+
|
|
79
|
+
scope.manager.itemEnd(url)
|
|
80
|
+
},
|
|
81
|
+
_onError
|
|
82
|
+
)
|
|
83
|
+
} catch (e) {
|
|
84
|
+
_onError(e)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
onProgress,
|
|
88
|
+
_onError
|
|
89
|
+
)
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
setDRACOLoader: function (dracoLoader) {
|
|
93
|
+
this.dracoLoader = dracoLoader
|
|
94
|
+
return this
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
setDDSLoader: function (ddsLoader) {
|
|
98
|
+
this.ddsLoader = ddsLoader
|
|
99
|
+
return this
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
setKTX2Loader: function (ktx2Loader) {
|
|
103
|
+
this.ktx2Loader = ktx2Loader
|
|
104
|
+
return this
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
register: function (callback) {
|
|
108
|
+
if (this.pluginCallbacks.indexOf(callback) === -1) {
|
|
109
|
+
this.pluginCallbacks.push(callback)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
unregister: function (callback) {
|
|
116
|
+
if (this.pluginCallbacks.indexOf(callback) !== -1) {
|
|
117
|
+
this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
parse: function (data, path, onLoad, onError) {
|
|
124
|
+
var content
|
|
125
|
+
var extensions = {}
|
|
126
|
+
var plugins = {}
|
|
127
|
+
|
|
128
|
+
if (typeof data === 'string') {
|
|
129
|
+
content = data
|
|
130
|
+
} else {
|
|
131
|
+
var magic = THREE.LoaderUtils.decodeText(new Uint8Array(data, 0, 4))
|
|
132
|
+
|
|
133
|
+
if (magic === BINARY_EXTENSION_HEADER_MAGIC) {
|
|
134
|
+
try {
|
|
135
|
+
extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data)
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (onError) onError(error)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content
|
|
142
|
+
} else {
|
|
143
|
+
content = THREE.LoaderUtils.decodeText(new Uint8Array(data))
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var json = JSON.parse(content)
|
|
148
|
+
|
|
149
|
+
if (json.asset === undefined || json.asset.version[0] < 2) {
|
|
150
|
+
if (onError) onError(new Error('THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.'))
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
var parser = new GLTFParser(json, {
|
|
155
|
+
path: path || this.resourcePath || '',
|
|
156
|
+
crossOrigin: this.crossOrigin,
|
|
157
|
+
manager: this.manager,
|
|
158
|
+
ktx2Loader: this.ktx2Loader,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
parser.fileLoader.setRequestHeader(this.requestHeader)
|
|
162
|
+
|
|
163
|
+
for (var i = 0; i < this.pluginCallbacks.length; i++) {
|
|
164
|
+
var plugin = this.pluginCallbacks[i](parser)
|
|
165
|
+
plugins[plugin.name] = plugin
|
|
166
|
+
|
|
167
|
+
// Workaround to avoid determining as unknown extension
|
|
168
|
+
// in addUnknownExtensionsToUserData().
|
|
169
|
+
// Remove this workaround if we move all the existing
|
|
170
|
+
// extension handlers to plugin system
|
|
171
|
+
extensions[plugin.name] = true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (json.extensionsUsed) {
|
|
175
|
+
for (var i = 0; i < json.extensionsUsed.length; ++i) {
|
|
176
|
+
var extensionName = json.extensionsUsed[i]
|
|
177
|
+
var extensionsRequired = json.extensionsRequired || []
|
|
178
|
+
|
|
179
|
+
switch (extensionName) {
|
|
180
|
+
case EXTENSIONS.KHR_MATERIALS_UNLIT:
|
|
181
|
+
extensions[extensionName] = new GLTFMaterialsUnlitExtension()
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
|
|
185
|
+
extensions[extensionName] = new GLTFMaterialsPbrSpecularGlossinessExtension()
|
|
186
|
+
break
|
|
187
|
+
|
|
188
|
+
case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
|
|
189
|
+
extensions[extensionName] = new GLTFDracoMeshCompressionExtension(json, this.dracoLoader)
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
case EXTENSIONS.MSFT_TEXTURE_DDS:
|
|
193
|
+
extensions[extensionName] = new GLTFTextureDDSExtension(this.ddsLoader)
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
|
|
197
|
+
extensions[extensionName] = new GLTFTextureTransformExtension()
|
|
198
|
+
break
|
|
199
|
+
|
|
200
|
+
case EXTENSIONS.KHR_MESH_QUANTIZATION:
|
|
201
|
+
extensions[extensionName] = new GLTFMeshQuantizationExtension()
|
|
202
|
+
break
|
|
203
|
+
|
|
204
|
+
default:
|
|
205
|
+
if (extensionsRequired.indexOf(extensionName) >= 0 && plugins[extensionName] === undefined) {
|
|
206
|
+
// console.warn('THREE.GLTFLoader: Unknown extension "' + extensionName + '".')
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
parser.setExtensions(extensions)
|
|
213
|
+
parser.setPlugins(plugins)
|
|
214
|
+
parser.parse(onLoad, onError)
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
/* GLTFREGISTRY */
|
|
219
|
+
|
|
220
|
+
function GLTFRegistry() {
|
|
221
|
+
var objects = {}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
get: function (key) {
|
|
225
|
+
return objects[key]
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
add: function (key, object) {
|
|
229
|
+
objects[key] = object
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
remove: function (key) {
|
|
233
|
+
delete objects[key]
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
removeAll: function () {
|
|
237
|
+
objects = {}
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/*********************************/
|
|
243
|
+
/********** EXTENSIONS ***********/
|
|
244
|
+
/*********************************/
|
|
245
|
+
|
|
246
|
+
var EXTENSIONS = {
|
|
247
|
+
KHR_BINARY_GLTF: 'KHR_binary_glTF',
|
|
248
|
+
KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
|
|
249
|
+
KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
|
|
250
|
+
KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
|
|
251
|
+
KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
|
|
252
|
+
KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
|
|
253
|
+
KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
|
|
254
|
+
KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
|
|
255
|
+
KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
|
|
256
|
+
KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
|
|
257
|
+
MSFT_TEXTURE_DDS: 'MSFT_texture_dds',
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* DDS Texture Extension
|
|
262
|
+
*
|
|
263
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
|
|
264
|
+
*
|
|
265
|
+
*/
|
|
266
|
+
function GLTFTextureDDSExtension(ddsLoader) {
|
|
267
|
+
if (!ddsLoader) {
|
|
268
|
+
throw new Error('THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader')
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.name = EXTENSIONS.MSFT_TEXTURE_DDS
|
|
272
|
+
this.ddsLoader = ddsLoader
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Punctual Lights Extension
|
|
277
|
+
*
|
|
278
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
|
279
|
+
*/
|
|
280
|
+
function GLTFLightsExtension(parser) {
|
|
281
|
+
this.parser = parser
|
|
282
|
+
this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL
|
|
283
|
+
|
|
284
|
+
// Object3D instance caches
|
|
285
|
+
this.cache = { refs: {}, uses: {} }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
GLTFLightsExtension.prototype._markDefs = function () {
|
|
289
|
+
var parser = this.parser
|
|
290
|
+
var nodeDefs = this.parser.json.nodes || []
|
|
291
|
+
|
|
292
|
+
for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {
|
|
293
|
+
var nodeDef = nodeDefs[nodeIndex]
|
|
294
|
+
|
|
295
|
+
if (nodeDef.extensions && nodeDef.extensions[this.name] && nodeDef.extensions[this.name].light !== undefined) {
|
|
296
|
+
parser._addNodeRef(this.cache, nodeDef.extensions[this.name].light)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
GLTFLightsExtension.prototype._loadLight = function (lightIndex) {
|
|
302
|
+
var parser = this.parser
|
|
303
|
+
var cacheKey = 'light:' + lightIndex
|
|
304
|
+
var dependency = parser.cache.get(cacheKey)
|
|
305
|
+
|
|
306
|
+
if (dependency) return dependency
|
|
307
|
+
|
|
308
|
+
var json = parser.json
|
|
309
|
+
var extensions = (json.extensions && json.extensions[this.name]) || {}
|
|
310
|
+
var lightDefs = extensions.lights || []
|
|
311
|
+
var lightDef = lightDefs[lightIndex]
|
|
312
|
+
var lightNode
|
|
313
|
+
|
|
314
|
+
var color = new THREE.Color(0xffffff)
|
|
315
|
+
|
|
316
|
+
if (lightDef.color !== undefined) color.fromArray(lightDef.color)
|
|
317
|
+
|
|
318
|
+
var range = lightDef.range !== undefined ? lightDef.range : 0
|
|
319
|
+
|
|
320
|
+
switch (lightDef.type) {
|
|
321
|
+
case 'directional':
|
|
322
|
+
lightNode = new THREE.DirectionalLight(color)
|
|
323
|
+
lightNode.target.position.set(0, 0, -1)
|
|
324
|
+
lightNode.add(lightNode.target)
|
|
325
|
+
break
|
|
326
|
+
|
|
327
|
+
case 'point':
|
|
328
|
+
lightNode = new THREE.PointLight(color)
|
|
329
|
+
lightNode.distance = range
|
|
330
|
+
break
|
|
331
|
+
|
|
332
|
+
case 'spot':
|
|
333
|
+
lightNode = new THREE.SpotLight(color)
|
|
334
|
+
lightNode.distance = range
|
|
335
|
+
// Handle spotlight properties.
|
|
336
|
+
lightDef.spot = lightDef.spot || {}
|
|
337
|
+
lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0
|
|
338
|
+
lightDef.spot.outerConeAngle =
|
|
339
|
+
lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0
|
|
340
|
+
lightNode.angle = lightDef.spot.outerConeAngle
|
|
341
|
+
lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle
|
|
342
|
+
lightNode.target.position.set(0, 0, -1)
|
|
343
|
+
lightNode.add(lightNode.target)
|
|
344
|
+
break
|
|
345
|
+
|
|
346
|
+
default:
|
|
347
|
+
throw new Error('THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Some lights (e.g. spot) default to a position other than the origin. Reset the position
|
|
351
|
+
// here, because node-level parsing will only override position if explicitly specified.
|
|
352
|
+
lightNode.position.set(0, 0, 0)
|
|
353
|
+
|
|
354
|
+
lightNode.decay = 2
|
|
355
|
+
|
|
356
|
+
if (lightDef.intensity !== undefined) lightNode.intensity = lightDef.intensity
|
|
357
|
+
|
|
358
|
+
lightNode.name = parser.createUniqueName(lightDef.name || 'light_' + lightIndex)
|
|
359
|
+
|
|
360
|
+
dependency = Promise.resolve(lightNode)
|
|
361
|
+
|
|
362
|
+
parser.cache.add(cacheKey, dependency)
|
|
363
|
+
|
|
364
|
+
return dependency
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
GLTFLightsExtension.prototype.createNodeAttachment = function (nodeIndex) {
|
|
368
|
+
var self = this
|
|
369
|
+
var parser = this.parser
|
|
370
|
+
var json = parser.json
|
|
371
|
+
var nodeDef = json.nodes[nodeIndex]
|
|
372
|
+
var lightDef = (nodeDef.extensions && nodeDef.extensions[this.name]) || {}
|
|
373
|
+
var lightIndex = lightDef.light
|
|
374
|
+
|
|
375
|
+
if (lightIndex === undefined) return null
|
|
376
|
+
|
|
377
|
+
return this._loadLight(lightIndex).then(function (light) {
|
|
378
|
+
return parser._getNodeRef(self.cache, lightIndex, light)
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Unlit Materials Extension
|
|
384
|
+
*
|
|
385
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
|
|
386
|
+
*/
|
|
387
|
+
function GLTFMaterialsUnlitExtension() {
|
|
388
|
+
this.name = EXTENSIONS.KHR_MATERIALS_UNLIT
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () {
|
|
392
|
+
return THREE.MeshBasicMaterial
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
GLTFMaterialsUnlitExtension.prototype.extendParams = function (materialParams, materialDef, parser) {
|
|
396
|
+
var pending = []
|
|
397
|
+
|
|
398
|
+
materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
|
|
399
|
+
materialParams.opacity = 1.0
|
|
400
|
+
|
|
401
|
+
var metallicRoughness = materialDef.pbrMetallicRoughness
|
|
402
|
+
|
|
403
|
+
if (metallicRoughness) {
|
|
404
|
+
if (Array.isArray(metallicRoughness.baseColorFactor)) {
|
|
405
|
+
var array = metallicRoughness.baseColorFactor
|
|
406
|
+
|
|
407
|
+
materialParams.color.fromArray(array)
|
|
408
|
+
materialParams.opacity = array[3]
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return Promise.all(pending)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Clearcoat Materials Extension
|
|
417
|
+
*
|
|
418
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
|
|
419
|
+
*/
|
|
420
|
+
function GLTFMaterialsClearcoatExtension(parser) {
|
|
421
|
+
this.parser = parser
|
|
422
|
+
this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function (materialIndex) {
|
|
426
|
+
var parser = this.parser
|
|
427
|
+
var materialDef = parser.json.materials[materialIndex]
|
|
428
|
+
|
|
429
|
+
if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
|
|
430
|
+
|
|
431
|
+
return THREE.MeshPhysicalMaterial
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) {
|
|
435
|
+
var parser = this.parser
|
|
436
|
+
var materialDef = parser.json.materials[materialIndex]
|
|
437
|
+
|
|
438
|
+
if (!materialDef.extensions || !materialDef.extensions[this.name]) {
|
|
439
|
+
return Promise.resolve()
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
var pending = []
|
|
443
|
+
|
|
444
|
+
var extension = materialDef.extensions[this.name]
|
|
445
|
+
|
|
446
|
+
if (extension.clearcoatFactor !== undefined) {
|
|
447
|
+
materialParams.clearcoat = extension.clearcoatFactor
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (extension.clearcoatRoughnessFactor !== undefined) {
|
|
451
|
+
materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (extension.clearcoatNormalTexture !== undefined) {
|
|
455
|
+
if (extension.clearcoatNormalTexture.scale !== undefined) {
|
|
456
|
+
var scale = extension.clearcoatNormalTexture.scale
|
|
457
|
+
materialParams.clearcoatNormalScale = new THREE.Vector2(scale, scale)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return Promise.all(pending)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Transmission Materials Extension
|
|
466
|
+
*
|
|
467
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
|
|
468
|
+
* Draft: https://github.com/KhronosGroup/glTF/pull/1698
|
|
469
|
+
*/
|
|
470
|
+
function GLTFMaterialsTransmissionExtension(parser) {
|
|
471
|
+
this.parser = parser
|
|
472
|
+
this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function (materialIndex) {
|
|
476
|
+
var parser = this.parser
|
|
477
|
+
var materialDef = parser.json.materials[materialIndex]
|
|
478
|
+
|
|
479
|
+
if (!materialDef.extensions || !materialDef.extensions[this.name]) return null
|
|
480
|
+
|
|
481
|
+
return THREE.MeshPhysicalMaterial
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function (materialIndex, materialParams) {
|
|
485
|
+
var parser = this.parser
|
|
486
|
+
var materialDef = parser.json.materials[materialIndex]
|
|
487
|
+
|
|
488
|
+
if (!materialDef.extensions || !materialDef.extensions[this.name]) {
|
|
489
|
+
return Promise.resolve()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
var pending = []
|
|
493
|
+
|
|
494
|
+
var extension = materialDef.extensions[this.name]
|
|
495
|
+
|
|
496
|
+
if (extension.transmissionFactor !== undefined) {
|
|
497
|
+
materialParams.transmission = extension.transmissionFactor
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return Promise.all(pending)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* BasisU Texture Extension
|
|
505
|
+
*
|
|
506
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
|
|
507
|
+
* (draft PR https://github.com/KhronosGroup/glTF/pull/1751)
|
|
508
|
+
*/
|
|
509
|
+
function GLTFTextureBasisUExtension(parser) {
|
|
510
|
+
this.parser = parser
|
|
511
|
+
this.name = EXTENSIONS.KHR_TEXTURE_BASISU
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
GLTFTextureBasisUExtension.prototype.loadTexture = function (textureIndex) {
|
|
515
|
+
return Promise.resolve(new THREE.Texture())
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* BINARY EXTENSION */
|
|
519
|
+
var BINARY_EXTENSION_HEADER_MAGIC = 'glTF'
|
|
520
|
+
var BINARY_EXTENSION_HEADER_LENGTH = 12
|
|
521
|
+
var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4942 }
|
|
522
|
+
|
|
523
|
+
function GLTFBinaryExtension(data) {
|
|
524
|
+
this.name = EXTENSIONS.KHR_BINARY_GLTF
|
|
525
|
+
this.content = null
|
|
526
|
+
this.body = null
|
|
527
|
+
|
|
528
|
+
var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH)
|
|
529
|
+
|
|
530
|
+
this.header = {
|
|
531
|
+
magic: THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))),
|
|
532
|
+
version: headerView.getUint32(4, true),
|
|
533
|
+
length: headerView.getUint32(8, true),
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
|
|
537
|
+
throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.')
|
|
538
|
+
} else if (this.header.version < 2.0) {
|
|
539
|
+
throw new Error('THREE.GLTFLoader: Legacy binary file detected.')
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
var chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH)
|
|
543
|
+
var chunkIndex = 0
|
|
544
|
+
|
|
545
|
+
while (chunkIndex < chunkView.byteLength) {
|
|
546
|
+
var chunkLength = chunkView.getUint32(chunkIndex, true)
|
|
547
|
+
chunkIndex += 4
|
|
548
|
+
|
|
549
|
+
var chunkType = chunkView.getUint32(chunkIndex, true)
|
|
550
|
+
chunkIndex += 4
|
|
551
|
+
|
|
552
|
+
if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
|
|
553
|
+
var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength)
|
|
554
|
+
this.content = THREE.LoaderUtils.decodeText(contentArray)
|
|
555
|
+
} else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
|
|
556
|
+
var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex
|
|
557
|
+
this.body = data.slice(byteOffset, byteOffset + chunkLength)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Clients must ignore chunks with unknown types.
|
|
561
|
+
|
|
562
|
+
chunkIndex += chunkLength
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (this.content === null) {
|
|
566
|
+
throw new Error('THREE.GLTFLoader: JSON content not found.')
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* DRACO Mesh Compression Extension
|
|
572
|
+
*
|
|
573
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
|
|
574
|
+
*/
|
|
575
|
+
function GLTFDracoMeshCompressionExtension(json, dracoLoader) {
|
|
576
|
+
if (!dracoLoader) {
|
|
577
|
+
throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.')
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION
|
|
581
|
+
this.json = json
|
|
582
|
+
this.dracoLoader = dracoLoader
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function (primitive, parser) {
|
|
586
|
+
var json = this.json
|
|
587
|
+
var dracoLoader = this.dracoLoader
|
|
588
|
+
var bufferViewIndex = primitive.extensions[this.name].bufferView
|
|
589
|
+
var gltfAttributeMap = primitive.extensions[this.name].attributes
|
|
590
|
+
var threeAttributeMap = {}
|
|
591
|
+
var attributeNormalizedMap = {}
|
|
592
|
+
var attributeTypeMap = {}
|
|
593
|
+
|
|
594
|
+
for (var attributeName in gltfAttributeMap) {
|
|
595
|
+
var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase()
|
|
596
|
+
|
|
597
|
+
threeAttributeMap[threeAttributeName] = gltfAttributeMap[attributeName]
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
for (attributeName in primitive.attributes) {
|
|
601
|
+
var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase()
|
|
602
|
+
|
|
603
|
+
if (gltfAttributeMap[attributeName] !== undefined) {
|
|
604
|
+
var accessorDef = json.accessors[primitive.attributes[attributeName]]
|
|
605
|
+
var componentType = WEBGL_COMPONENT_TYPES[accessorDef.componentType]
|
|
606
|
+
|
|
607
|
+
attributeTypeMap[threeAttributeName] = componentType
|
|
608
|
+
attributeNormalizedMap[threeAttributeName] = accessorDef.normalized === true
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return parser.getDependency('bufferView', bufferViewIndex).then(function (bufferView) {
|
|
613
|
+
return new Promise(function (resolve) {
|
|
614
|
+
dracoLoader.decodeDracoFile(
|
|
615
|
+
bufferView,
|
|
616
|
+
function (geometry) {
|
|
617
|
+
for (var attributeName in geometry.attributes) {
|
|
618
|
+
var attribute = geometry.attributes[attributeName]
|
|
619
|
+
var normalized = attributeNormalizedMap[attributeName]
|
|
620
|
+
|
|
621
|
+
if (normalized !== undefined) attribute.normalized = normalized
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
resolve(geometry)
|
|
625
|
+
},
|
|
626
|
+
threeAttributeMap,
|
|
627
|
+
attributeTypeMap
|
|
628
|
+
)
|
|
629
|
+
})
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Texture Transform Extension
|
|
635
|
+
*
|
|
636
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
|
|
637
|
+
*/
|
|
638
|
+
function GLTFTextureTransformExtension() {
|
|
639
|
+
this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
GLTFTextureTransformExtension.prototype.extendTexture = function (texture, transform) {
|
|
643
|
+
texture = texture.clone()
|
|
644
|
+
|
|
645
|
+
if (transform.offset !== undefined) {
|
|
646
|
+
texture.offset.fromArray(transform.offset)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (transform.rotation !== undefined) {
|
|
650
|
+
texture.rotation = transform.rotation
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (transform.scale !== undefined) {
|
|
654
|
+
texture.repeat.fromArray(transform.scale)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (transform.texCoord !== undefined) {
|
|
658
|
+
console.warn('THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.')
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
texture.needsUpdate = true
|
|
662
|
+
|
|
663
|
+
return texture
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Specular-Glossiness Extension
|
|
668
|
+
*
|
|
669
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
|
|
670
|
+
*/
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* A sub class of THREE.StandardMaterial with some of the functionality
|
|
674
|
+
* changed via the `onBeforeCompile` callback
|
|
675
|
+
* @pailhead
|
|
676
|
+
*/
|
|
677
|
+
|
|
678
|
+
function GLTFMeshStandardSGMaterial(params) {
|
|
679
|
+
THREE.MeshStandardMaterial.call(this)
|
|
680
|
+
|
|
681
|
+
this.isGLTFSpecularGlossinessMaterial = true
|
|
682
|
+
|
|
683
|
+
//various chunks that need replacing
|
|
684
|
+
var specularMapParsFragmentChunk = ['#ifdef USE_SPECULARMAP', ' uniform sampler2D specularMap;', '#endif'].join('\n')
|
|
685
|
+
|
|
686
|
+
var glossinessMapParsFragmentChunk = [
|
|
687
|
+
'#ifdef USE_GLOSSINESSMAP',
|
|
688
|
+
' uniform sampler2D glossinessMap;',
|
|
689
|
+
'#endif',
|
|
690
|
+
].join('\n')
|
|
691
|
+
|
|
692
|
+
var specularMapFragmentChunk = [
|
|
693
|
+
'vec3 specularFactor = specular;',
|
|
694
|
+
'#ifdef USE_SPECULARMAP',
|
|
695
|
+
' vec4 texelSpecular = texture2D( specularMap, vUv );',
|
|
696
|
+
' texelSpecular = sRGBToLinear( texelSpecular );',
|
|
697
|
+
' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
|
|
698
|
+
' specularFactor *= texelSpecular.rgb;',
|
|
699
|
+
'#endif',
|
|
700
|
+
].join('\n')
|
|
701
|
+
|
|
702
|
+
var glossinessMapFragmentChunk = [
|
|
703
|
+
'float glossinessFactor = glossiness;',
|
|
704
|
+
'#ifdef USE_GLOSSINESSMAP',
|
|
705
|
+
' vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
|
|
706
|
+
' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
|
|
707
|
+
' glossinessFactor *= texelGlossiness.a;',
|
|
708
|
+
'#endif',
|
|
709
|
+
].join('\n')
|
|
710
|
+
|
|
711
|
+
var lightPhysicalFragmentChunk = [
|
|
712
|
+
'PhysicalMaterial material;',
|
|
713
|
+
'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
|
|
714
|
+
'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
|
|
715
|
+
'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',
|
|
716
|
+
'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
|
|
717
|
+
'material.specularRoughness += geometryRoughness;',
|
|
718
|
+
'material.specularRoughness = min( material.specularRoughness, 1.0 );',
|
|
719
|
+
'material.specularColor = specularFactor;',
|
|
720
|
+
].join('\n')
|
|
721
|
+
|
|
722
|
+
var uniforms = {
|
|
723
|
+
specular: { value: new THREE.Color().setHex(0xffffff) },
|
|
724
|
+
glossiness: { value: 1 },
|
|
725
|
+
specularMap: { value: null },
|
|
726
|
+
glossinessMap: { value: null },
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
this._extraUniforms = uniforms
|
|
730
|
+
|
|
731
|
+
this.onBeforeCompile = function (shader) {
|
|
732
|
+
for (var uniformName in uniforms) {
|
|
733
|
+
shader.uniforms[uniformName] = uniforms[uniformName]
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
shader.fragmentShader = shader.fragmentShader
|
|
737
|
+
.replace('uniform float roughness;', 'uniform vec3 specular;')
|
|
738
|
+
.replace('uniform float metalness;', 'uniform float glossiness;')
|
|
739
|
+
.replace('#include <roughnessmap_pars_fragment>', specularMapParsFragmentChunk)
|
|
740
|
+
.replace('#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk)
|
|
741
|
+
.replace('#include <roughnessmap_fragment>', specularMapFragmentChunk)
|
|
742
|
+
.replace('#include <metalnessmap_fragment>', glossinessMapFragmentChunk)
|
|
743
|
+
.replace('#include <lights_physical_fragment>', lightPhysicalFragmentChunk)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
Object.defineProperties(this, {
|
|
747
|
+
specular: {
|
|
748
|
+
get: function () {
|
|
749
|
+
return uniforms.specular.value
|
|
750
|
+
},
|
|
751
|
+
set: function (v) {
|
|
752
|
+
uniforms.specular.value = v
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
specularMap: {
|
|
757
|
+
get: function () {
|
|
758
|
+
return uniforms.specularMap.value
|
|
759
|
+
},
|
|
760
|
+
set: function (v) {
|
|
761
|
+
uniforms.specularMap.value = v
|
|
762
|
+
|
|
763
|
+
if (v) {
|
|
764
|
+
this.defines.USE_SPECULARMAP = '' // USE_UV is set by the renderer for specular maps
|
|
765
|
+
} else {
|
|
766
|
+
delete this.defines.USE_SPECULARMAP
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
glossiness: {
|
|
772
|
+
get: function () {
|
|
773
|
+
return uniforms.glossiness.value
|
|
774
|
+
},
|
|
775
|
+
set: function (v) {
|
|
776
|
+
uniforms.glossiness.value = v
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
glossinessMap: {
|
|
781
|
+
get: function () {
|
|
782
|
+
return uniforms.glossinessMap.value
|
|
783
|
+
},
|
|
784
|
+
set: function (v) {
|
|
785
|
+
uniforms.glossinessMap.value = v
|
|
786
|
+
|
|
787
|
+
if (v) {
|
|
788
|
+
this.defines.USE_GLOSSINESSMAP = ''
|
|
789
|
+
this.defines.USE_UV = ''
|
|
790
|
+
} else {
|
|
791
|
+
delete this.defines.USE_GLOSSINESSMAP
|
|
792
|
+
delete this.defines.USE_UV
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
delete this.metalness
|
|
799
|
+
delete this.roughness
|
|
800
|
+
delete this.metalnessMap
|
|
801
|
+
delete this.roughnessMap
|
|
802
|
+
|
|
803
|
+
this.setValues(params)
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
GLTFMeshStandardSGMaterial.prototype = Object.create(THREE.MeshStandardMaterial.prototype)
|
|
807
|
+
GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial
|
|
808
|
+
|
|
809
|
+
GLTFMeshStandardSGMaterial.prototype.copy = function (source) {
|
|
810
|
+
THREE.MeshStandardMaterial.prototype.copy.call(this, source)
|
|
811
|
+
this.specularMap = source.specularMap
|
|
812
|
+
this.specular.copy(source.specular)
|
|
813
|
+
this.glossinessMap = source.glossinessMap
|
|
814
|
+
this.glossiness = source.glossiness
|
|
815
|
+
delete this.metalness
|
|
816
|
+
delete this.roughness
|
|
817
|
+
delete this.metalnessMap
|
|
818
|
+
delete this.roughnessMap
|
|
819
|
+
return this
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function GLTFMaterialsPbrSpecularGlossinessExtension() {
|
|
823
|
+
return {
|
|
824
|
+
name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
|
|
825
|
+
|
|
826
|
+
specularGlossinessParams: [
|
|
827
|
+
'color',
|
|
828
|
+
'map',
|
|
829
|
+
'lightMap',
|
|
830
|
+
'lightMapIntensity',
|
|
831
|
+
'aoMap',
|
|
832
|
+
'aoMapIntensity',
|
|
833
|
+
'emissive',
|
|
834
|
+
'emissiveIntensity',
|
|
835
|
+
'emissiveMap',
|
|
836
|
+
'bumpMap',
|
|
837
|
+
'bumpScale',
|
|
838
|
+
'normalMap',
|
|
839
|
+
'normalMapType',
|
|
840
|
+
'displacementMap',
|
|
841
|
+
'displacementScale',
|
|
842
|
+
'displacementBias',
|
|
843
|
+
'specularMap',
|
|
844
|
+
'specular',
|
|
845
|
+
'glossinessMap',
|
|
846
|
+
'glossiness',
|
|
847
|
+
'alphaMap',
|
|
848
|
+
'envMap',
|
|
849
|
+
'envMapIntensity',
|
|
850
|
+
'refractionRatio',
|
|
851
|
+
],
|
|
852
|
+
|
|
853
|
+
getMaterialType: function () {
|
|
854
|
+
return GLTFMeshStandardSGMaterial
|
|
855
|
+
},
|
|
856
|
+
|
|
857
|
+
extendParams: function (materialParams, materialDef, parser) {
|
|
858
|
+
var pbrSpecularGlossiness = materialDef.extensions[this.name]
|
|
859
|
+
|
|
860
|
+
materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
|
|
861
|
+
materialParams.opacity = 1.0
|
|
862
|
+
|
|
863
|
+
var pending = []
|
|
864
|
+
|
|
865
|
+
if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) {
|
|
866
|
+
var array = pbrSpecularGlossiness.diffuseFactor
|
|
867
|
+
|
|
868
|
+
materialParams.color.fromArray(array)
|
|
869
|
+
materialParams.opacity = array[3]
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
materialParams.emissive = new THREE.Color(0.0, 0.0, 0.0)
|
|
873
|
+
materialParams.glossiness =
|
|
874
|
+
pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0
|
|
875
|
+
materialParams.specular = new THREE.Color(1.0, 1.0, 1.0)
|
|
876
|
+
|
|
877
|
+
if (Array.isArray(pbrSpecularGlossiness.specularFactor)) {
|
|
878
|
+
materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor)
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return Promise.all(pending)
|
|
882
|
+
},
|
|
883
|
+
|
|
884
|
+
createMaterial: function (materialParams) {
|
|
885
|
+
var material = new GLTFMeshStandardSGMaterial(materialParams)
|
|
886
|
+
material.fog = true
|
|
887
|
+
|
|
888
|
+
material.color = materialParams.color
|
|
889
|
+
|
|
890
|
+
material.map = materialParams.map === undefined ? null : materialParams.map
|
|
891
|
+
|
|
892
|
+
material.lightMap = null
|
|
893
|
+
material.lightMapIntensity = 1.0
|
|
894
|
+
|
|
895
|
+
material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap
|
|
896
|
+
material.aoMapIntensity = 1.0
|
|
897
|
+
|
|
898
|
+
material.emissive = materialParams.emissive
|
|
899
|
+
material.emissiveIntensity = 1.0
|
|
900
|
+
material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap
|
|
901
|
+
|
|
902
|
+
material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap
|
|
903
|
+
material.bumpScale = 1
|
|
904
|
+
|
|
905
|
+
material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap
|
|
906
|
+
material.normalMapType = THREE.TangentSpaceNormalMap
|
|
907
|
+
|
|
908
|
+
if (materialParams.normalScale) material.normalScale = materialParams.normalScale
|
|
909
|
+
|
|
910
|
+
material.displacementMap = null
|
|
911
|
+
material.displacementScale = 1
|
|
912
|
+
material.displacementBias = 0
|
|
913
|
+
|
|
914
|
+
material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap
|
|
915
|
+
material.specular = materialParams.specular
|
|
916
|
+
|
|
917
|
+
material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap
|
|
918
|
+
material.glossiness = materialParams.glossiness
|
|
919
|
+
|
|
920
|
+
material.alphaMap = null
|
|
921
|
+
|
|
922
|
+
material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap
|
|
923
|
+
material.envMapIntensity = 1.0
|
|
924
|
+
|
|
925
|
+
material.refractionRatio = 0.98
|
|
926
|
+
|
|
927
|
+
return material
|
|
928
|
+
},
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Mesh Quantization Extension
|
|
934
|
+
*
|
|
935
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization
|
|
936
|
+
*/
|
|
937
|
+
function GLTFMeshQuantizationExtension() {
|
|
938
|
+
this.name = EXTENSIONS.KHR_MESH_QUANTIZATION
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/*********************************/
|
|
942
|
+
/********** INTERPOLATION ********/
|
|
943
|
+
/*********************************/
|
|
944
|
+
|
|
945
|
+
// Spline Interpolation
|
|
946
|
+
// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
|
|
947
|
+
function GLTFCubicSplineInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) {
|
|
948
|
+
THREE.Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer)
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
GLTFCubicSplineInterpolant.prototype = Object.create(THREE.Interpolant.prototype)
|
|
952
|
+
GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant
|
|
953
|
+
|
|
954
|
+
GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function (index) {
|
|
955
|
+
// Copies a sample value to the result buffer. See description of glTF
|
|
956
|
+
// CUBICSPLINE values layout in interpolate_() function below.
|
|
957
|
+
|
|
958
|
+
var result = this.resultBuffer,
|
|
959
|
+
values = this.sampleValues,
|
|
960
|
+
valueSize = this.valueSize,
|
|
961
|
+
offset = index * valueSize * 3 + valueSize
|
|
962
|
+
|
|
963
|
+
for (var i = 0; i !== valueSize; i++) {
|
|
964
|
+
result[i] = values[offset + i]
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return result
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_
|
|
971
|
+
|
|
972
|
+
GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_
|
|
973
|
+
|
|
974
|
+
GLTFCubicSplineInterpolant.prototype.interpolate_ = function (i1, t0, t, t1) {
|
|
975
|
+
var result = this.resultBuffer
|
|
976
|
+
var values = this.sampleValues
|
|
977
|
+
var stride = this.valueSize
|
|
978
|
+
|
|
979
|
+
var stride2 = stride * 2
|
|
980
|
+
var stride3 = stride * 3
|
|
981
|
+
|
|
982
|
+
var td = t1 - t0
|
|
983
|
+
|
|
984
|
+
var p = (t - t0) / td
|
|
985
|
+
var pp = p * p
|
|
986
|
+
var ppp = pp * p
|
|
987
|
+
|
|
988
|
+
var offset1 = i1 * stride3
|
|
989
|
+
var offset0 = offset1 - stride3
|
|
990
|
+
|
|
991
|
+
var s2 = -2 * ppp + 3 * pp
|
|
992
|
+
var s3 = ppp - pp
|
|
993
|
+
var s0 = 1 - s2
|
|
994
|
+
var s1 = s3 - pp + p
|
|
995
|
+
|
|
996
|
+
// Layout of keyframe output values for CUBICSPLINE animations:
|
|
997
|
+
// [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
|
|
998
|
+
for (var i = 0; i !== stride; i++) {
|
|
999
|
+
var p0 = values[offset0 + i + stride] // splineVertex_k
|
|
1000
|
+
var m0 = values[offset0 + i + stride2] * td // outTangent_k * (t_k+1 - t_k)
|
|
1001
|
+
var p1 = values[offset1 + i + stride] // splineVertex_k+1
|
|
1002
|
+
var m1 = values[offset1 + i] * td // inTangent_k+1 * (t_k+1 - t_k)
|
|
1003
|
+
|
|
1004
|
+
result[i] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return result
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/*********************************/
|
|
1011
|
+
/********** INTERNALS ************/
|
|
1012
|
+
/*********************************/
|
|
1013
|
+
|
|
1014
|
+
/* CONSTANTS */
|
|
1015
|
+
|
|
1016
|
+
var WEBGL_CONSTANTS = {
|
|
1017
|
+
FLOAT: 5126,
|
|
1018
|
+
//FLOAT_MAT2: 35674,
|
|
1019
|
+
FLOAT_MAT3: 35675,
|
|
1020
|
+
FLOAT_MAT4: 35676,
|
|
1021
|
+
FLOAT_VEC2: 35664,
|
|
1022
|
+
FLOAT_VEC3: 35665,
|
|
1023
|
+
FLOAT_VEC4: 35666,
|
|
1024
|
+
LINEAR: 9729,
|
|
1025
|
+
REPEAT: 10497,
|
|
1026
|
+
SAMPLER_2D: 35678,
|
|
1027
|
+
POINTS: 0,
|
|
1028
|
+
LINES: 1,
|
|
1029
|
+
LINE_LOOP: 2,
|
|
1030
|
+
LINE_STRIP: 3,
|
|
1031
|
+
TRIANGLES: 4,
|
|
1032
|
+
TRIANGLE_STRIP: 5,
|
|
1033
|
+
TRIANGLE_FAN: 6,
|
|
1034
|
+
UNSIGNED_BYTE: 5121,
|
|
1035
|
+
UNSIGNED_SHORT: 5123,
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
var WEBGL_COMPONENT_TYPES = {
|
|
1039
|
+
5120: Int8Array,
|
|
1040
|
+
5121: Uint8Array,
|
|
1041
|
+
5122: Int16Array,
|
|
1042
|
+
5123: Uint16Array,
|
|
1043
|
+
5125: Uint32Array,
|
|
1044
|
+
5126: Float32Array,
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
var WEBGL_FILTERS = {
|
|
1048
|
+
9728: THREE.NearestFilter,
|
|
1049
|
+
9729: THREE.LinearFilter,
|
|
1050
|
+
9984: THREE.NearestMipmapNearestFilter,
|
|
1051
|
+
9985: THREE.LinearMipmapNearestFilter,
|
|
1052
|
+
9986: THREE.NearestMipmapLinearFilter,
|
|
1053
|
+
9987: THREE.LinearMipmapLinearFilter,
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
var WEBGL_WRAPPINGS = {
|
|
1057
|
+
33071: THREE.ClampToEdgeWrapping,
|
|
1058
|
+
33648: THREE.MirroredRepeatWrapping,
|
|
1059
|
+
10497: THREE.RepeatWrapping,
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
var WEBGL_TYPE_SIZES = {
|
|
1063
|
+
SCALAR: 1,
|
|
1064
|
+
VEC2: 2,
|
|
1065
|
+
VEC3: 3,
|
|
1066
|
+
VEC4: 4,
|
|
1067
|
+
MAT2: 4,
|
|
1068
|
+
MAT3: 9,
|
|
1069
|
+
MAT4: 16,
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
var ATTRIBUTES = {
|
|
1073
|
+
POSITION: 'position',
|
|
1074
|
+
NORMAL: 'normal',
|
|
1075
|
+
TANGENT: 'tangent',
|
|
1076
|
+
TEXCOORD_0: 'uv',
|
|
1077
|
+
TEXCOORD_1: 'uv2',
|
|
1078
|
+
COLOR_0: 'color',
|
|
1079
|
+
WEIGHTS_0: 'skinWeight',
|
|
1080
|
+
JOINTS_0: 'skinIndex',
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
var PATH_PROPERTIES = {
|
|
1084
|
+
scale: 'scale',
|
|
1085
|
+
translation: 'position',
|
|
1086
|
+
rotation: 'quaternion',
|
|
1087
|
+
weights: 'morphTargetInfluences',
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
var INTERPOLATION = {
|
|
1091
|
+
CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
|
|
1092
|
+
// keyframe track will be initialized with a default interpolation type, then modified.
|
|
1093
|
+
LINEAR: THREE.InterpolateLinear,
|
|
1094
|
+
STEP: THREE.InterpolateDiscrete,
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
var ALPHA_MODES = {
|
|
1098
|
+
OPAQUE: 'OPAQUE',
|
|
1099
|
+
MASK: 'MASK',
|
|
1100
|
+
BLEND: 'BLEND',
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/* UTILITY FUNCTIONS */
|
|
1104
|
+
|
|
1105
|
+
function resolveURL(url, path) {
|
|
1106
|
+
// Invalid URL
|
|
1107
|
+
if (typeof url !== 'string' || url === '') return ''
|
|
1108
|
+
|
|
1109
|
+
// Host Relative URL
|
|
1110
|
+
if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
|
|
1111
|
+
path = path.replace(/(^https?:\/\/[^\/]+).*/i, '$1')
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// Absolute URL http://,https://,//
|
|
1115
|
+
if (/^(https?:)?\/\//i.test(url)) return url
|
|
1116
|
+
|
|
1117
|
+
// Data URI
|
|
1118
|
+
if (/^data:.*,.*$/i.test(url)) return url
|
|
1119
|
+
|
|
1120
|
+
// Blob URL
|
|
1121
|
+
if (/^blob:.*$/i.test(url)) return url
|
|
1122
|
+
|
|
1123
|
+
// Relative URL
|
|
1124
|
+
return path + url
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
|
|
1129
|
+
*/
|
|
1130
|
+
function createDefaultMaterial(cache) {
|
|
1131
|
+
if (cache['DefaultMaterial'] === undefined) {
|
|
1132
|
+
cache['DefaultMaterial'] = new THREE.MeshStandardMaterial({
|
|
1133
|
+
color: 0xffffff,
|
|
1134
|
+
emissive: 0x000000,
|
|
1135
|
+
metalness: 1,
|
|
1136
|
+
roughness: 1,
|
|
1137
|
+
transparent: false,
|
|
1138
|
+
depthTest: true,
|
|
1139
|
+
side: THREE.FrontSide,
|
|
1140
|
+
})
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
return cache['DefaultMaterial']
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function addUnknownExtensionsToUserData(knownExtensions, object, objectDef) {
|
|
1147
|
+
// Add unknown glTF extensions to an object's userData.
|
|
1148
|
+
|
|
1149
|
+
for (var name in objectDef.extensions) {
|
|
1150
|
+
if (knownExtensions[name] === undefined) {
|
|
1151
|
+
object.userData.gltfExtensions = object.userData.gltfExtensions || {}
|
|
1152
|
+
object.userData.gltfExtensions[name] = objectDef.extensions[name]
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object
|
|
1159
|
+
* @param {GLTF.definition} gltfDef
|
|
1160
|
+
*/
|
|
1161
|
+
function assignExtrasToUserData(object, gltfDef) {
|
|
1162
|
+
if (gltfDef.extras !== undefined) {
|
|
1163
|
+
if (typeof gltfDef.extras === 'object') {
|
|
1164
|
+
Object.assign(object.userData, gltfDef.extras)
|
|
1165
|
+
} else {
|
|
1166
|
+
console.warn('THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras)
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
|
|
1173
|
+
*
|
|
1174
|
+
* @param {THREE.BufferGeometry} geometry
|
|
1175
|
+
* @param {Array<GLTF.Target>} targets
|
|
1176
|
+
* @param {GLTFParser} parser
|
|
1177
|
+
* @return {Promise<THREE.BufferGeometry>}
|
|
1178
|
+
*/
|
|
1179
|
+
function addMorphTargets(geometry, targets, parser) {
|
|
1180
|
+
var hasMorphPosition = false
|
|
1181
|
+
var hasMorphNormal = false
|
|
1182
|
+
|
|
1183
|
+
for (var i = 0, il = targets.length; i < il; i++) {
|
|
1184
|
+
var target = targets[i]
|
|
1185
|
+
|
|
1186
|
+
if (target.POSITION !== undefined) hasMorphPosition = true
|
|
1187
|
+
if (target.NORMAL !== undefined) hasMorphNormal = true
|
|
1188
|
+
|
|
1189
|
+
if (hasMorphPosition && hasMorphNormal) break
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
if (!hasMorphPosition && !hasMorphNormal) return Promise.resolve(geometry)
|
|
1193
|
+
|
|
1194
|
+
var pendingPositionAccessors = []
|
|
1195
|
+
var pendingNormalAccessors = []
|
|
1196
|
+
|
|
1197
|
+
for (var i = 0, il = targets.length; i < il; i++) {
|
|
1198
|
+
var target = targets[i]
|
|
1199
|
+
|
|
1200
|
+
if (hasMorphPosition) {
|
|
1201
|
+
var pendingAccessor =
|
|
1202
|
+
target.POSITION !== undefined
|
|
1203
|
+
? parser.getDependency('accessor', target.POSITION)
|
|
1204
|
+
: geometry.attributes.position
|
|
1205
|
+
|
|
1206
|
+
pendingPositionAccessors.push(pendingAccessor)
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (hasMorphNormal) {
|
|
1210
|
+
var pendingAccessor =
|
|
1211
|
+
target.NORMAL !== undefined ? parser.getDependency('accessor', target.NORMAL) : geometry.attributes.normal
|
|
1212
|
+
|
|
1213
|
+
pendingNormalAccessors.push(pendingAccessor)
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return Promise.all([Promise.all(pendingPositionAccessors), Promise.all(pendingNormalAccessors)]).then(function (
|
|
1218
|
+
accessors
|
|
1219
|
+
) {
|
|
1220
|
+
var morphPositions = accessors[0]
|
|
1221
|
+
var morphNormals = accessors[1]
|
|
1222
|
+
|
|
1223
|
+
if (hasMorphPosition) geometry.morphAttributes.position = morphPositions
|
|
1224
|
+
if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals
|
|
1225
|
+
geometry.morphTargetsRelative = true
|
|
1226
|
+
|
|
1227
|
+
return geometry
|
|
1228
|
+
})
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* @param {THREE.Mesh} mesh
|
|
1233
|
+
* @param {GLTF.Mesh} meshDef
|
|
1234
|
+
*/
|
|
1235
|
+
function updateMorphTargets(mesh, meshDef) {
|
|
1236
|
+
mesh.updateMorphTargets()
|
|
1237
|
+
|
|
1238
|
+
if (meshDef.weights !== undefined) {
|
|
1239
|
+
for (var i = 0, il = meshDef.weights.length; i < il; i++) {
|
|
1240
|
+
mesh.morphTargetInfluences[i] = meshDef.weights[i]
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// .extras has user-defined data, so check that .extras.targetNames is an array.
|
|
1245
|
+
if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) {
|
|
1246
|
+
var targetNames = meshDef.extras.targetNames
|
|
1247
|
+
|
|
1248
|
+
if (mesh.morphTargetInfluences.length === targetNames.length) {
|
|
1249
|
+
mesh.morphTargetDictionary = {}
|
|
1250
|
+
|
|
1251
|
+
for (var i = 0, il = targetNames.length; i < il; i++) {
|
|
1252
|
+
mesh.morphTargetDictionary[targetNames[i]] = i
|
|
1253
|
+
}
|
|
1254
|
+
} else {
|
|
1255
|
+
console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.')
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function createPrimitiveKey(primitiveDef) {
|
|
1261
|
+
var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]
|
|
1262
|
+
var geometryKey
|
|
1263
|
+
|
|
1264
|
+
if (dracoExtension) {
|
|
1265
|
+
geometryKey =
|
|
1266
|
+
'draco:' +
|
|
1267
|
+
dracoExtension.bufferView +
|
|
1268
|
+
':' +
|
|
1269
|
+
dracoExtension.indices +
|
|
1270
|
+
':' +
|
|
1271
|
+
createAttributesKey(dracoExtension.attributes)
|
|
1272
|
+
} else {
|
|
1273
|
+
geometryKey = primitiveDef.indices + ':' + createAttributesKey(primitiveDef.attributes) + ':' + primitiveDef.mode
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
return geometryKey
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function createAttributesKey(attributes) {
|
|
1280
|
+
var attributesKey = ''
|
|
1281
|
+
|
|
1282
|
+
var keys = Object.keys(attributes).sort()
|
|
1283
|
+
|
|
1284
|
+
for (var i = 0, il = keys.length; i < il; i++) {
|
|
1285
|
+
attributesKey += keys[i] + ':' + attributes[keys[i]] + ';'
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
return attributesKey
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
/* GLTF PARSER */
|
|
1292
|
+
|
|
1293
|
+
function GLTFParser(json, options) {
|
|
1294
|
+
this.json = json || {}
|
|
1295
|
+
this.extensions = {}
|
|
1296
|
+
this.plugins = {}
|
|
1297
|
+
this.options = options || {}
|
|
1298
|
+
|
|
1299
|
+
// loader object cache
|
|
1300
|
+
this.cache = new GLTFRegistry()
|
|
1301
|
+
|
|
1302
|
+
// associations between Three.js objects and glTF elements
|
|
1303
|
+
this.associations = new Map()
|
|
1304
|
+
|
|
1305
|
+
// BufferGeometry caching
|
|
1306
|
+
this.primitiveCache = {}
|
|
1307
|
+
|
|
1308
|
+
// Object3D instance caches
|
|
1309
|
+
this.meshCache = { refs: {}, uses: {} }
|
|
1310
|
+
this.cameraCache = { refs: {}, uses: {} }
|
|
1311
|
+
this.lightCache = { refs: {}, uses: {} }
|
|
1312
|
+
|
|
1313
|
+
// Track node names, to ensure no duplicates
|
|
1314
|
+
this.nodeNamesUsed = {}
|
|
1315
|
+
|
|
1316
|
+
// Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the
|
|
1317
|
+
// expensive work of uploading a texture to the GPU off the main thread.
|
|
1318
|
+
if (typeof createImageBitmap !== 'undefined' && /Firefox/.test(navigator.userAgent) === false) {
|
|
1319
|
+
this.textureLoader = new THREE.ImageBitmapLoader(this.options.manager)
|
|
1320
|
+
} else {
|
|
1321
|
+
this.textureLoader = new THREE.TextureLoader(this.options.manager)
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
this.textureLoader.setCrossOrigin(this.options.crossOrigin)
|
|
1325
|
+
|
|
1326
|
+
this.fileLoader = new THREE.FileLoader(this.options.manager)
|
|
1327
|
+
this.fileLoader.setResponseType('arraybuffer')
|
|
1328
|
+
|
|
1329
|
+
if (this.options.crossOrigin === 'use-credentials') {
|
|
1330
|
+
this.fileLoader.setWithCredentials(true)
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
GLTFParser.prototype.setExtensions = function (extensions) {
|
|
1335
|
+
this.extensions = extensions
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
GLTFParser.prototype.setPlugins = function (plugins) {
|
|
1339
|
+
this.plugins = plugins
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
GLTFParser.prototype.parse = function (onLoad, onError) {
|
|
1343
|
+
var parser = this
|
|
1344
|
+
var json = this.json
|
|
1345
|
+
var extensions = this.extensions
|
|
1346
|
+
|
|
1347
|
+
// Clear the loader cache
|
|
1348
|
+
this.cache.removeAll()
|
|
1349
|
+
|
|
1350
|
+
// Mark the special nodes/meshes in json for efficient parse
|
|
1351
|
+
this._invokeAll(function (ext) {
|
|
1352
|
+
return ext._markDefs && ext._markDefs()
|
|
1353
|
+
})
|
|
1354
|
+
|
|
1355
|
+
Promise.all([this.getDependencies('scene'), this.getDependencies('animation'), this.getDependencies('camera')])
|
|
1356
|
+
.then(function (dependencies) {
|
|
1357
|
+
var result = {
|
|
1358
|
+
scene: dependencies[0][json.scene || 0],
|
|
1359
|
+
scenes: dependencies[0],
|
|
1360
|
+
animations: dependencies[1],
|
|
1361
|
+
cameras: dependencies[2],
|
|
1362
|
+
asset: json.asset,
|
|
1363
|
+
parser: parser,
|
|
1364
|
+
userData: {},
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
addUnknownExtensionsToUserData(extensions, result, json)
|
|
1368
|
+
|
|
1369
|
+
assignExtrasToUserData(result, json)
|
|
1370
|
+
|
|
1371
|
+
onLoad(result)
|
|
1372
|
+
})
|
|
1373
|
+
.catch(onError)
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* Marks the special nodes/meshes in json for efficient parse.
|
|
1378
|
+
*/
|
|
1379
|
+
GLTFParser.prototype._markDefs = function () {
|
|
1380
|
+
var nodeDefs = this.json.nodes || []
|
|
1381
|
+
var skinDefs = this.json.skins || []
|
|
1382
|
+
var meshDefs = this.json.meshes || []
|
|
1383
|
+
|
|
1384
|
+
// Nothing in the node definition indicates whether it is a Bone or an
|
|
1385
|
+
// Object3D. Use the skins' joint references to mark bones.
|
|
1386
|
+
for (var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) {
|
|
1387
|
+
var joints = skinDefs[skinIndex].joints
|
|
1388
|
+
|
|
1389
|
+
for (var i = 0, il = joints.length; i < il; i++) {
|
|
1390
|
+
nodeDefs[joints[i]].isBone = true
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// Iterate over all nodes, marking references to shared resources,
|
|
1395
|
+
// as well as skeleton joints.
|
|
1396
|
+
for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {
|
|
1397
|
+
var nodeDef = nodeDefs[nodeIndex]
|
|
1398
|
+
|
|
1399
|
+
if (nodeDef.mesh !== undefined) {
|
|
1400
|
+
this._addNodeRef(this.meshCache, nodeDef.mesh)
|
|
1401
|
+
|
|
1402
|
+
// Nothing in the mesh definition indicates whether it is
|
|
1403
|
+
// a SkinnedMesh or Mesh. Use the node's mesh reference
|
|
1404
|
+
// to mark SkinnedMesh if node has skin.
|
|
1405
|
+
if (nodeDef.skin !== undefined) {
|
|
1406
|
+
meshDefs[nodeDef.mesh].isSkinnedMesh = true
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
if (nodeDef.camera !== undefined) {
|
|
1411
|
+
this._addNodeRef(this.cameraCache, nodeDef.camera)
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Counts references to shared node / Object3D resources. These resources
|
|
1418
|
+
* can be reused, or "instantiated", at multiple nodes in the scene
|
|
1419
|
+
* hierarchy. Mesh, Camera, and Light instances are instantiated and must
|
|
1420
|
+
* be marked. Non-scenegraph resources (like Materials, Geometries, and
|
|
1421
|
+
* Textures) can be reused directly and are not marked here.
|
|
1422
|
+
*
|
|
1423
|
+
* Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
|
|
1424
|
+
*/
|
|
1425
|
+
GLTFParser.prototype._addNodeRef = function (cache, index) {
|
|
1426
|
+
if (index === undefined) return
|
|
1427
|
+
|
|
1428
|
+
if (cache.refs[index] === undefined) {
|
|
1429
|
+
cache.refs[index] = cache.uses[index] = 0
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
cache.refs[index]++
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/** Returns a reference to a shared resource, cloning it if necessary. */
|
|
1436
|
+
GLTFParser.prototype._getNodeRef = function (cache, index, object) {
|
|
1437
|
+
if (cache.refs[index] <= 1) return object
|
|
1438
|
+
|
|
1439
|
+
var ref = object.clone()
|
|
1440
|
+
|
|
1441
|
+
ref.name += '_instance_' + cache.uses[index]++
|
|
1442
|
+
|
|
1443
|
+
return ref
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
GLTFParser.prototype._invokeOne = function (func) {
|
|
1447
|
+
var extensions = Object.values(this.plugins)
|
|
1448
|
+
extensions.push(this)
|
|
1449
|
+
|
|
1450
|
+
for (var i = 0; i < extensions.length; i++) {
|
|
1451
|
+
var result = func(extensions[i])
|
|
1452
|
+
|
|
1453
|
+
if (result) return result
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
GLTFParser.prototype._invokeAll = function (func) {
|
|
1458
|
+
var extensions = Object.values(this.plugins)
|
|
1459
|
+
extensions.unshift(this)
|
|
1460
|
+
|
|
1461
|
+
var pending = []
|
|
1462
|
+
|
|
1463
|
+
for (var i = 0; i < extensions.length; i++) {
|
|
1464
|
+
var result = func(extensions[i])
|
|
1465
|
+
|
|
1466
|
+
if (result) pending.push(result)
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
return pending
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Requests the specified dependency asynchronously, with caching.
|
|
1474
|
+
* @param {string} type
|
|
1475
|
+
* @param {number} index
|
|
1476
|
+
* @return {Promise<THREE.Object3D|THREE.Material|THREE.Texture|THREE.AnimationClip|ArrayBuffer|Object>}
|
|
1477
|
+
*/
|
|
1478
|
+
GLTFParser.prototype.getDependency = function (type, index) {
|
|
1479
|
+
var cacheKey = type + ':' + index
|
|
1480
|
+
var dependency = this.cache.get(cacheKey)
|
|
1481
|
+
|
|
1482
|
+
if (!dependency) {
|
|
1483
|
+
switch (type) {
|
|
1484
|
+
case 'scene':
|
|
1485
|
+
dependency = this.loadScene(index)
|
|
1486
|
+
break
|
|
1487
|
+
|
|
1488
|
+
case 'node':
|
|
1489
|
+
dependency = this.loadNode(index)
|
|
1490
|
+
break
|
|
1491
|
+
|
|
1492
|
+
case 'mesh':
|
|
1493
|
+
dependency = this._invokeOne(function (ext) {
|
|
1494
|
+
return ext.loadMesh && ext.loadMesh(index)
|
|
1495
|
+
})
|
|
1496
|
+
break
|
|
1497
|
+
|
|
1498
|
+
case 'accessor':
|
|
1499
|
+
dependency = this.loadAccessor(index)
|
|
1500
|
+
break
|
|
1501
|
+
|
|
1502
|
+
case 'bufferView':
|
|
1503
|
+
dependency = Promise.resolve(new Float32Array(0))
|
|
1504
|
+
break
|
|
1505
|
+
|
|
1506
|
+
case 'buffer':
|
|
1507
|
+
dependency = Promise.resolve(new Float32Array(0))
|
|
1508
|
+
break
|
|
1509
|
+
|
|
1510
|
+
case 'material':
|
|
1511
|
+
dependency = this._invokeOne(function (ext) {
|
|
1512
|
+
return ext.loadMaterial && ext.loadMaterial(index)
|
|
1513
|
+
})
|
|
1514
|
+
break
|
|
1515
|
+
|
|
1516
|
+
case 'skin':
|
|
1517
|
+
dependency = this.loadSkin(index)
|
|
1518
|
+
break
|
|
1519
|
+
|
|
1520
|
+
case 'animation':
|
|
1521
|
+
dependency = this.loadAnimation(index)
|
|
1522
|
+
break
|
|
1523
|
+
|
|
1524
|
+
case 'camera':
|
|
1525
|
+
dependency = this.loadCamera(index)
|
|
1526
|
+
break
|
|
1527
|
+
|
|
1528
|
+
default:
|
|
1529
|
+
throw new Error('Unknown type: ' + type)
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
this.cache.add(cacheKey, dependency)
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
return dependency
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Requests all dependencies of the specified type asynchronously, with caching.
|
|
1540
|
+
* @param {string} type
|
|
1541
|
+
* @return {Promise<Array<Object>>}
|
|
1542
|
+
*/
|
|
1543
|
+
GLTFParser.prototype.getDependencies = function (type) {
|
|
1544
|
+
var dependencies = this.cache.get(type)
|
|
1545
|
+
|
|
1546
|
+
if (!dependencies) {
|
|
1547
|
+
var parser = this
|
|
1548
|
+
var defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || []
|
|
1549
|
+
|
|
1550
|
+
dependencies = Promise.all(
|
|
1551
|
+
defs.map(function (def, index) {
|
|
1552
|
+
return parser.getDependency(type, index)
|
|
1553
|
+
})
|
|
1554
|
+
)
|
|
1555
|
+
|
|
1556
|
+
this.cache.add(type, dependencies)
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
return dependencies
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
|
|
1564
|
+
* @param {number} bufferIndex
|
|
1565
|
+
* @return {Promise<ArrayBuffer>}
|
|
1566
|
+
*/
|
|
1567
|
+
GLTFParser.prototype.loadBuffer = function (bufferIndex) {
|
|
1568
|
+
var bufferDef = this.json.buffers[bufferIndex]
|
|
1569
|
+
var loader = this.fileLoader
|
|
1570
|
+
|
|
1571
|
+
if (bufferDef.type && bufferDef.type !== 'arraybuffer') {
|
|
1572
|
+
throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.')
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// If present, GLB container is required to be the first buffer.
|
|
1576
|
+
if (bufferDef.uri === undefined && bufferIndex === 0) {
|
|
1577
|
+
return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body)
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
var options = this.options
|
|
1581
|
+
|
|
1582
|
+
return new Promise(function (resolve, reject) {
|
|
1583
|
+
loader.load(resolveURL(bufferDef.uri, options.path), resolve, undefined, function () {
|
|
1584
|
+
reject(new Error('THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".'))
|
|
1585
|
+
})
|
|
1586
|
+
})
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
|
|
1591
|
+
* @param {number} bufferViewIndex
|
|
1592
|
+
* @return {Promise<ArrayBuffer>}
|
|
1593
|
+
*/
|
|
1594
|
+
GLTFParser.prototype.loadBufferView = function (bufferViewIndex) {
|
|
1595
|
+
var bufferViewDef = this.json.bufferViews[bufferViewIndex]
|
|
1596
|
+
|
|
1597
|
+
return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) {
|
|
1598
|
+
var byteLength = bufferViewDef.byteLength || 0
|
|
1599
|
+
var byteOffset = bufferViewDef.byteOffset || 0
|
|
1600
|
+
return buffer.slice(byteOffset, byteOffset + byteLength)
|
|
1601
|
+
})
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
|
|
1606
|
+
* @param {number} accessorIndex
|
|
1607
|
+
* @return {Promise<THREE.BufferAttribute|THREE.InterleavedBufferAttribute>}
|
|
1608
|
+
*/
|
|
1609
|
+
GLTFParser.prototype.loadAccessor = function (accessorIndex) {
|
|
1610
|
+
var parser = this
|
|
1611
|
+
var json = this.json
|
|
1612
|
+
|
|
1613
|
+
var accessorDef = this.json.accessors[accessorIndex]
|
|
1614
|
+
|
|
1615
|
+
if (accessorDef.bufferView === undefined && accessorDef.sparse === undefined) {
|
|
1616
|
+
// Ignore empty accessors, which may be used to declare runtime
|
|
1617
|
+
// information about attributes coming from another source (e.g. Draco
|
|
1618
|
+
// compression extension).
|
|
1619
|
+
return Promise.resolve(null)
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
var pendingBufferViews = []
|
|
1623
|
+
|
|
1624
|
+
if (accessorDef.bufferView !== undefined) {
|
|
1625
|
+
pendingBufferViews.push(this.getDependency('bufferView', accessorDef.bufferView))
|
|
1626
|
+
} else {
|
|
1627
|
+
pendingBufferViews.push(null)
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (accessorDef.sparse !== undefined) {
|
|
1631
|
+
pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.indices.bufferView))
|
|
1632
|
+
pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.values.bufferView))
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
return Promise.all(pendingBufferViews).then(function (bufferViews) {
|
|
1636
|
+
var bufferView = bufferViews[0]
|
|
1637
|
+
|
|
1638
|
+
var itemSize = WEBGL_TYPE_SIZES[accessorDef.type]
|
|
1639
|
+
var TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType]
|
|
1640
|
+
|
|
1641
|
+
// For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
|
|
1642
|
+
var elementBytes = TypedArray.BYTES_PER_ELEMENT
|
|
1643
|
+
var itemBytes = elementBytes * itemSize
|
|
1644
|
+
var byteOffset = accessorDef.byteOffset || 0
|
|
1645
|
+
var byteStride =
|
|
1646
|
+
accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined
|
|
1647
|
+
var normalized = accessorDef.normalized === true
|
|
1648
|
+
var array, bufferAttribute
|
|
1649
|
+
|
|
1650
|
+
// The buffer is not interleaved if the stride is the item size in bytes.
|
|
1651
|
+
if (byteStride && byteStride !== itemBytes) {
|
|
1652
|
+
// Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer
|
|
1653
|
+
// This makes sure that IBA.count reflects accessor.count properly
|
|
1654
|
+
var ibSlice = Math.floor(byteOffset / byteStride)
|
|
1655
|
+
var ibCacheKey =
|
|
1656
|
+
'InterleavedBuffer:' +
|
|
1657
|
+
accessorDef.bufferView +
|
|
1658
|
+
':' +
|
|
1659
|
+
accessorDef.componentType +
|
|
1660
|
+
':' +
|
|
1661
|
+
ibSlice +
|
|
1662
|
+
':' +
|
|
1663
|
+
accessorDef.count
|
|
1664
|
+
var ib = parser.cache.get(ibCacheKey)
|
|
1665
|
+
|
|
1666
|
+
if (!ib) {
|
|
1667
|
+
array = new TypedArray(bufferView, ibSlice * byteStride, (accessorDef.count * byteStride) / elementBytes)
|
|
1668
|
+
|
|
1669
|
+
// Integer parameters to IB/IBA are in array elements, not bytes.
|
|
1670
|
+
ib = new THREE.InterleavedBuffer(array, byteStride / elementBytes)
|
|
1671
|
+
|
|
1672
|
+
parser.cache.add(ibCacheKey, ib)
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
bufferAttribute = new THREE.InterleavedBufferAttribute(
|
|
1676
|
+
ib,
|
|
1677
|
+
itemSize,
|
|
1678
|
+
(byteOffset % byteStride) / elementBytes,
|
|
1679
|
+
normalized
|
|
1680
|
+
)
|
|
1681
|
+
} else {
|
|
1682
|
+
if (bufferView === null) {
|
|
1683
|
+
array = new TypedArray(accessorDef.count * itemSize)
|
|
1684
|
+
} else {
|
|
1685
|
+
array = new TypedArray(bufferView, byteOffset, accessorDef.count * itemSize)
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
bufferAttribute = new THREE.BufferAttribute(array, itemSize, normalized)
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
|
|
1692
|
+
if (accessorDef.sparse !== undefined) {
|
|
1693
|
+
var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR
|
|
1694
|
+
var TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType]
|
|
1695
|
+
|
|
1696
|
+
var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0
|
|
1697
|
+
var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0
|
|
1698
|
+
|
|
1699
|
+
var sparseIndices = new TypedArrayIndices(
|
|
1700
|
+
bufferViews[1],
|
|
1701
|
+
byteOffsetIndices,
|
|
1702
|
+
accessorDef.sparse.count * itemSizeIndices
|
|
1703
|
+
)
|
|
1704
|
+
var sparseValues = new TypedArray(bufferViews[2], byteOffsetValues, accessorDef.sparse.count * itemSize)
|
|
1705
|
+
|
|
1706
|
+
if (bufferView !== null) {
|
|
1707
|
+
// Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
|
|
1708
|
+
bufferAttribute = new THREE.BufferAttribute(
|
|
1709
|
+
bufferAttribute.array.slice(),
|
|
1710
|
+
bufferAttribute.itemSize,
|
|
1711
|
+
bufferAttribute.normalized
|
|
1712
|
+
)
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
for (var i = 0, il = sparseIndices.length; i < il; i++) {
|
|
1716
|
+
var index = sparseIndices[i]
|
|
1717
|
+
|
|
1718
|
+
bufferAttribute.setX(index, sparseValues[i * itemSize])
|
|
1719
|
+
if (itemSize >= 2) bufferAttribute.setY(index, sparseValues[i * itemSize + 1])
|
|
1720
|
+
if (itemSize >= 3) bufferAttribute.setZ(index, sparseValues[i * itemSize + 2])
|
|
1721
|
+
if (itemSize >= 4) bufferAttribute.setW(index, sparseValues[i * itemSize + 3])
|
|
1722
|
+
if (itemSize >= 5) throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.')
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
return bufferAttribute
|
|
1727
|
+
})
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
|
|
1732
|
+
* @param {number} textureIndex
|
|
1733
|
+
* @return {Promise<THREE.Texture>}
|
|
1734
|
+
*/
|
|
1735
|
+
GLTFParser.prototype.loadTexture = function (textureIndex) {
|
|
1736
|
+
return Promise.resolve(new THREE.Texture())
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* Assigns final material to a Mesh, Line, or Points instance. The instance
|
|
1741
|
+
* already has a material (generated from the glTF material options alone)
|
|
1742
|
+
* but reuse of the same glTF material may require multiple threejs materials
|
|
1743
|
+
* to accomodate different primitive types, defines, etc. New materials will
|
|
1744
|
+
* be created if necessary, and reused from a cache.
|
|
1745
|
+
* @param {THREE.Object3D} mesh Mesh, Line, or Points instance.
|
|
1746
|
+
*/
|
|
1747
|
+
GLTFParser.prototype.assignFinalMaterial = function (mesh) {
|
|
1748
|
+
var geometry = mesh.geometry
|
|
1749
|
+
var material = mesh.material
|
|
1750
|
+
|
|
1751
|
+
var useVertexTangents = geometry.attributes.tangent !== undefined
|
|
1752
|
+
var useVertexColors = geometry.attributes.color !== undefined
|
|
1753
|
+
var useFlatShading = geometry.attributes.normal === undefined
|
|
1754
|
+
var useSkinning = mesh.isSkinnedMesh === true
|
|
1755
|
+
var useMorphTargets = Object.keys(geometry.morphAttributes).length > 0
|
|
1756
|
+
var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined
|
|
1757
|
+
|
|
1758
|
+
if (mesh.isPoints) {
|
|
1759
|
+
var cacheKey = 'PointsMaterial:' + material.uuid
|
|
1760
|
+
|
|
1761
|
+
var pointsMaterial = this.cache.get(cacheKey)
|
|
1762
|
+
|
|
1763
|
+
if (!pointsMaterial) {
|
|
1764
|
+
pointsMaterial = new THREE.PointsMaterial()
|
|
1765
|
+
THREE.Material.prototype.copy.call(pointsMaterial, material)
|
|
1766
|
+
pointsMaterial.color.copy(material.color)
|
|
1767
|
+
pointsMaterial.map = material.map
|
|
1768
|
+
pointsMaterial.sizeAttenuation = false // glTF spec says points should be 1px
|
|
1769
|
+
|
|
1770
|
+
this.cache.add(cacheKey, pointsMaterial)
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
material = pointsMaterial
|
|
1774
|
+
} else if (mesh.isLine) {
|
|
1775
|
+
var cacheKey = 'LineBasicMaterial:' + material.uuid
|
|
1776
|
+
|
|
1777
|
+
var lineMaterial = this.cache.get(cacheKey)
|
|
1778
|
+
|
|
1779
|
+
if (!lineMaterial) {
|
|
1780
|
+
lineMaterial = new THREE.LineBasicMaterial()
|
|
1781
|
+
THREE.Material.prototype.copy.call(lineMaterial, material)
|
|
1782
|
+
lineMaterial.color.copy(material.color)
|
|
1783
|
+
|
|
1784
|
+
this.cache.add(cacheKey, lineMaterial)
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
material = lineMaterial
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// Clone the material if it will be modified
|
|
1791
|
+
if (useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets) {
|
|
1792
|
+
var cacheKey = 'ClonedMaterial:' + material.uuid + ':'
|
|
1793
|
+
|
|
1794
|
+
if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:'
|
|
1795
|
+
if (useSkinning) cacheKey += 'skinning:'
|
|
1796
|
+
if (useVertexTangents) cacheKey += 'vertex-tangents:'
|
|
1797
|
+
if (useVertexColors) cacheKey += 'vertex-colors:'
|
|
1798
|
+
if (useFlatShading) cacheKey += 'flat-shading:'
|
|
1799
|
+
if (useMorphTargets) cacheKey += 'morph-targets:'
|
|
1800
|
+
if (useMorphNormals) cacheKey += 'morph-normals:'
|
|
1801
|
+
|
|
1802
|
+
var cachedMaterial = this.cache.get(cacheKey)
|
|
1803
|
+
|
|
1804
|
+
if (!cachedMaterial) {
|
|
1805
|
+
cachedMaterial = material.clone()
|
|
1806
|
+
|
|
1807
|
+
if (useSkinning) cachedMaterial.skinning = true
|
|
1808
|
+
if (useVertexTangents) cachedMaterial.vertexTangents = true
|
|
1809
|
+
if (useVertexColors) cachedMaterial.vertexColors = true
|
|
1810
|
+
if (useFlatShading) cachedMaterial.flatShading = true
|
|
1811
|
+
if (useMorphTargets) cachedMaterial.morphTargets = true
|
|
1812
|
+
if (useMorphNormals) cachedMaterial.morphNormals = true
|
|
1813
|
+
|
|
1814
|
+
this.cache.add(cacheKey, cachedMaterial)
|
|
1815
|
+
|
|
1816
|
+
this.associations.set(cachedMaterial, this.associations.get(material))
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
material = cachedMaterial
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// workarounds for mesh and geometry
|
|
1823
|
+
|
|
1824
|
+
if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) {
|
|
1825
|
+
geometry.setAttribute('uv2', geometry.attributes.uv)
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
|
|
1829
|
+
if (material.normalScale && !useVertexTangents) {
|
|
1830
|
+
material.normalScale.y = -material.normalScale.y
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
if (material.clearcoatNormalScale && !useVertexTangents) {
|
|
1834
|
+
material.clearcoatNormalScale.y = -material.clearcoatNormalScale.y
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
mesh.material = material
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
GLTFParser.prototype.getMaterialType = function (/* materialIndex */) {
|
|
1841
|
+
return THREE.MeshStandardMaterial
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
|
|
1846
|
+
* @param {number} materialIndex
|
|
1847
|
+
* @return {Promise<THREE.Material>}
|
|
1848
|
+
*/
|
|
1849
|
+
GLTFParser.prototype.loadMaterial = function (materialIndex) {
|
|
1850
|
+
var parser = this
|
|
1851
|
+
var json = this.json
|
|
1852
|
+
var extensions = this.extensions
|
|
1853
|
+
var materialDef = json.materials[materialIndex]
|
|
1854
|
+
|
|
1855
|
+
var materialType
|
|
1856
|
+
var materialParams = {}
|
|
1857
|
+
var materialExtensions = materialDef.extensions || {}
|
|
1858
|
+
|
|
1859
|
+
var pending = []
|
|
1860
|
+
|
|
1861
|
+
if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) {
|
|
1862
|
+
var sgExtension = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]
|
|
1863
|
+
materialType = sgExtension.getMaterialType()
|
|
1864
|
+
pending.push(sgExtension.extendParams(materialParams, materialDef, parser))
|
|
1865
|
+
} else if (materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]) {
|
|
1866
|
+
var kmuExtension = extensions[EXTENSIONS.KHR_MATERIALS_UNLIT]
|
|
1867
|
+
materialType = kmuExtension.getMaterialType()
|
|
1868
|
+
pending.push(kmuExtension.extendParams(materialParams, materialDef, parser))
|
|
1869
|
+
} else {
|
|
1870
|
+
// Specification:
|
|
1871
|
+
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
|
|
1872
|
+
|
|
1873
|
+
var metallicRoughness = materialDef.pbrMetallicRoughness || {}
|
|
1874
|
+
|
|
1875
|
+
materialParams.color = new THREE.Color(1.0, 1.0, 1.0)
|
|
1876
|
+
materialParams.opacity = 1.0
|
|
1877
|
+
|
|
1878
|
+
if (Array.isArray(metallicRoughness.baseColorFactor)) {
|
|
1879
|
+
var array = metallicRoughness.baseColorFactor
|
|
1880
|
+
|
|
1881
|
+
materialParams.color.fromArray(array)
|
|
1882
|
+
materialParams.opacity = array[3]
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0
|
|
1886
|
+
materialParams.roughness =
|
|
1887
|
+
metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0
|
|
1888
|
+
|
|
1889
|
+
materialType = this._invokeOne(function (ext) {
|
|
1890
|
+
return ext.getMaterialType && ext.getMaterialType(materialIndex)
|
|
1891
|
+
})
|
|
1892
|
+
|
|
1893
|
+
pending.push(
|
|
1894
|
+
Promise.all(
|
|
1895
|
+
this._invokeAll(function (ext) {
|
|
1896
|
+
return ext.extendMaterialParams && ext.extendMaterialParams(materialIndex, materialParams)
|
|
1897
|
+
})
|
|
1898
|
+
)
|
|
1899
|
+
)
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
if (materialDef.doubleSided === true) {
|
|
1903
|
+
materialParams.side = THREE.DoubleSide
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE
|
|
1907
|
+
|
|
1908
|
+
if (alphaMode === ALPHA_MODES.BLEND) {
|
|
1909
|
+
materialParams.transparent = true
|
|
1910
|
+
|
|
1911
|
+
// See: https://github.com/mrdoob/three.js/issues/17706
|
|
1912
|
+
materialParams.depthWrite = false
|
|
1913
|
+
} else {
|
|
1914
|
+
materialParams.transparent = false
|
|
1915
|
+
|
|
1916
|
+
if (alphaMode === ALPHA_MODES.MASK) {
|
|
1917
|
+
materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
if (materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
|
|
1922
|
+
materialParams.normalScale = new THREE.Vector2(1, 1)
|
|
1923
|
+
|
|
1924
|
+
if (materialDef.normalTexture.scale !== undefined) {
|
|
1925
|
+
materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale)
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
|
|
1930
|
+
if (materialDef.occlusionTexture.strength !== undefined) {
|
|
1931
|
+
materialParams.aoMapIntensity = materialDef.occlusionTexture.strength
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) {
|
|
1936
|
+
materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor)
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
return Promise.all(pending).then(function () {
|
|
1940
|
+
var material
|
|
1941
|
+
|
|
1942
|
+
if (materialType === GLTFMeshStandardSGMaterial) {
|
|
1943
|
+
material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams)
|
|
1944
|
+
} else {
|
|
1945
|
+
material = new materialType(materialParams)
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
if (materialDef.name) material.name = materialDef.name
|
|
1949
|
+
|
|
1950
|
+
// baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding.
|
|
1951
|
+
if (material.map) material.map.encoding = THREE.sRGBEncoding
|
|
1952
|
+
if (material.emissiveMap) material.emissiveMap.encoding = THREE.sRGBEncoding
|
|
1953
|
+
|
|
1954
|
+
assignExtrasToUserData(material, materialDef)
|
|
1955
|
+
|
|
1956
|
+
parser.associations.set(material, { type: 'materials', index: materialIndex })
|
|
1957
|
+
|
|
1958
|
+
if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef)
|
|
1959
|
+
|
|
1960
|
+
return material
|
|
1961
|
+
})
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/** When Object3D instances are targeted by animation, they need unique names. */
|
|
1965
|
+
GLTFParser.prototype.createUniqueName = function (originalName) {
|
|
1966
|
+
var sanitizedName = THREE.PropertyBinding.sanitizeNodeName(originalName || '')
|
|
1967
|
+
|
|
1968
|
+
var name = sanitizedName
|
|
1969
|
+
|
|
1970
|
+
for (var i = 1; this.nodeNamesUsed[name]; ++i) {
|
|
1971
|
+
name = sanitizedName + '_' + i
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
this.nodeNamesUsed[name] = true
|
|
1975
|
+
|
|
1976
|
+
return name
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
/**
|
|
1980
|
+
* @param {THREE.BufferGeometry} geometry
|
|
1981
|
+
* @param {GLTF.Primitive} primitiveDef
|
|
1982
|
+
* @param {GLTFParser} parser
|
|
1983
|
+
*/
|
|
1984
|
+
function computeBounds(geometry, primitiveDef, parser) {
|
|
1985
|
+
var attributes = primitiveDef.attributes
|
|
1986
|
+
|
|
1987
|
+
var box = new THREE.Box3()
|
|
1988
|
+
|
|
1989
|
+
if (attributes.POSITION !== undefined) {
|
|
1990
|
+
var accessor = parser.json.accessors[attributes.POSITION]
|
|
1991
|
+
|
|
1992
|
+
var min = accessor.min
|
|
1993
|
+
var max = accessor.max
|
|
1994
|
+
|
|
1995
|
+
// glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
|
|
1996
|
+
|
|
1997
|
+
if (min !== undefined && max !== undefined) {
|
|
1998
|
+
box.set(new THREE.Vector3(min[0], min[1], min[2]), new THREE.Vector3(max[0], max[1], max[2]))
|
|
1999
|
+
} else {
|
|
2000
|
+
console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.')
|
|
2001
|
+
|
|
2002
|
+
return
|
|
2003
|
+
}
|
|
2004
|
+
} else {
|
|
2005
|
+
return
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
var targets = primitiveDef.targets
|
|
2009
|
+
|
|
2010
|
+
if (targets !== undefined) {
|
|
2011
|
+
var maxDisplacement = new THREE.Vector3()
|
|
2012
|
+
var vector = new THREE.Vector3()
|
|
2013
|
+
|
|
2014
|
+
for (var i = 0, il = targets.length; i < il; i++) {
|
|
2015
|
+
var target = targets[i]
|
|
2016
|
+
|
|
2017
|
+
if (target.POSITION !== undefined) {
|
|
2018
|
+
var accessor = parser.json.accessors[target.POSITION]
|
|
2019
|
+
var min = accessor.min
|
|
2020
|
+
var max = accessor.max
|
|
2021
|
+
|
|
2022
|
+
// glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
|
|
2023
|
+
|
|
2024
|
+
if (min !== undefined && max !== undefined) {
|
|
2025
|
+
// we need to get max of absolute components because target weight is [-1,1]
|
|
2026
|
+
vector.setX(Math.max(Math.abs(min[0]), Math.abs(max[0])))
|
|
2027
|
+
vector.setY(Math.max(Math.abs(min[1]), Math.abs(max[1])))
|
|
2028
|
+
vector.setZ(Math.max(Math.abs(min[2]), Math.abs(max[2])))
|
|
2029
|
+
|
|
2030
|
+
// Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative
|
|
2031
|
+
// to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets
|
|
2032
|
+
// are used to implement key-frame animations and as such only two are active at a time - this results in very large
|
|
2033
|
+
// boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size.
|
|
2034
|
+
maxDisplacement.max(vector)
|
|
2035
|
+
} else {
|
|
2036
|
+
console.warn('THREE.GLTFLoader: Missing min/max properties for accessor POSITION.')
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
// As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
|
|
2042
|
+
box.expandByVector(maxDisplacement)
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
geometry.boundingBox = box
|
|
2046
|
+
|
|
2047
|
+
var sphere = new THREE.Sphere()
|
|
2048
|
+
|
|
2049
|
+
box.getCenter(sphere.center)
|
|
2050
|
+
sphere.radius = box.min.distanceTo(box.max) / 2
|
|
2051
|
+
|
|
2052
|
+
geometry.boundingSphere = sphere
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/**
|
|
2056
|
+
* @param {THREE.BufferGeometry} geometry
|
|
2057
|
+
* @param {GLTF.Primitive} primitiveDef
|
|
2058
|
+
* @param {GLTFParser} parser
|
|
2059
|
+
* @return {Promise<THREE.BufferGeometry>}
|
|
2060
|
+
*/
|
|
2061
|
+
function addPrimitiveAttributes(geometry, primitiveDef, parser) {
|
|
2062
|
+
var attributes = primitiveDef.attributes
|
|
2063
|
+
|
|
2064
|
+
var pending = []
|
|
2065
|
+
|
|
2066
|
+
function assignAttributeAccessor(accessorIndex, attributeName) {
|
|
2067
|
+
return parser.getDependency('accessor', accessorIndex).then(function (accessor) {
|
|
2068
|
+
geometry.setAttribute(attributeName, accessor)
|
|
2069
|
+
})
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
for (var gltfAttributeName in attributes) {
|
|
2073
|
+
var threeAttributeName = ATTRIBUTES[gltfAttributeName] || gltfAttributeName.toLowerCase()
|
|
2074
|
+
|
|
2075
|
+
// Skip attributes already provided by e.g. Draco extension.
|
|
2076
|
+
if (threeAttributeName in geometry.attributes) continue
|
|
2077
|
+
|
|
2078
|
+
pending.push(assignAttributeAccessor(attributes[gltfAttributeName], threeAttributeName))
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
if (primitiveDef.indices !== undefined && !geometry.index) {
|
|
2082
|
+
var accessor = parser.getDependency('accessor', primitiveDef.indices).then(function (accessor) {
|
|
2083
|
+
geometry.setIndex(accessor)
|
|
2084
|
+
})
|
|
2085
|
+
|
|
2086
|
+
pending.push(accessor)
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
assignExtrasToUserData(geometry, primitiveDef)
|
|
2090
|
+
|
|
2091
|
+
computeBounds(geometry, primitiveDef, parser)
|
|
2092
|
+
|
|
2093
|
+
return Promise.all(pending).then(function () {
|
|
2094
|
+
return primitiveDef.targets !== undefined ? addMorphTargets(geometry, primitiveDef.targets, parser) : geometry
|
|
2095
|
+
})
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
/**
|
|
2099
|
+
* @param {THREE.BufferGeometry} geometry
|
|
2100
|
+
* @param {Number} drawMode
|
|
2101
|
+
* @return {THREE.BufferGeometry}
|
|
2102
|
+
*/
|
|
2103
|
+
function toTrianglesDrawMode(geometry, drawMode) {
|
|
2104
|
+
var index = geometry.getIndex()
|
|
2105
|
+
|
|
2106
|
+
// generate index if not present
|
|
2107
|
+
|
|
2108
|
+
if (index === null) {
|
|
2109
|
+
var indices = []
|
|
2110
|
+
|
|
2111
|
+
var position = geometry.getAttribute('position')
|
|
2112
|
+
|
|
2113
|
+
if (position !== undefined) {
|
|
2114
|
+
for (var i = 0; i < position.count; i++) {
|
|
2115
|
+
indices.push(i)
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
geometry.setIndex(indices)
|
|
2119
|
+
index = geometry.getIndex()
|
|
2120
|
+
} else {
|
|
2121
|
+
console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.')
|
|
2122
|
+
return geometry
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
//
|
|
2127
|
+
|
|
2128
|
+
var numberOfTriangles = index.count - 2
|
|
2129
|
+
var newIndices = []
|
|
2130
|
+
|
|
2131
|
+
if (drawMode === THREE.TriangleFanDrawMode) {
|
|
2132
|
+
// gl.TRIANGLE_FAN
|
|
2133
|
+
|
|
2134
|
+
for (var i = 1; i <= numberOfTriangles; i++) {
|
|
2135
|
+
newIndices.push(index.getX(0))
|
|
2136
|
+
newIndices.push(index.getX(i))
|
|
2137
|
+
newIndices.push(index.getX(i + 1))
|
|
2138
|
+
}
|
|
2139
|
+
} else {
|
|
2140
|
+
// gl.TRIANGLE_STRIP
|
|
2141
|
+
|
|
2142
|
+
for (var i = 0; i < numberOfTriangles; i++) {
|
|
2143
|
+
if (i % 2 === 0) {
|
|
2144
|
+
newIndices.push(index.getX(i))
|
|
2145
|
+
newIndices.push(index.getX(i + 1))
|
|
2146
|
+
newIndices.push(index.getX(i + 2))
|
|
2147
|
+
} else {
|
|
2148
|
+
newIndices.push(index.getX(i + 2))
|
|
2149
|
+
newIndices.push(index.getX(i + 1))
|
|
2150
|
+
newIndices.push(index.getX(i))
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
if (newIndices.length / 3 !== numberOfTriangles) {
|
|
2156
|
+
console.error('THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.')
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
// build final geometry
|
|
2160
|
+
|
|
2161
|
+
var newGeometry = geometry.clone()
|
|
2162
|
+
newGeometry.setIndex(newIndices)
|
|
2163
|
+
|
|
2164
|
+
return newGeometry
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
/**
|
|
2168
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
|
|
2169
|
+
*
|
|
2170
|
+
* Creates BufferGeometries from primitives.
|
|
2171
|
+
*
|
|
2172
|
+
* @param {Array<GLTF.Primitive>} primitives
|
|
2173
|
+
* @return {Promise<Array<THREE.BufferGeometry>>}
|
|
2174
|
+
*/
|
|
2175
|
+
GLTFParser.prototype.loadGeometries = function (primitives) {
|
|
2176
|
+
var parser = this
|
|
2177
|
+
var extensions = this.extensions
|
|
2178
|
+
var cache = this.primitiveCache
|
|
2179
|
+
|
|
2180
|
+
function createDracoPrimitive(primitive) {
|
|
2181
|
+
return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]
|
|
2182
|
+
.decodePrimitive(primitive, parser)
|
|
2183
|
+
.then(function (geometry) {
|
|
2184
|
+
return addPrimitiveAttributes(geometry, primitive, parser)
|
|
2185
|
+
})
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
var pending = []
|
|
2189
|
+
|
|
2190
|
+
for (var i = 0, il = primitives.length; i < il; i++) {
|
|
2191
|
+
var primitive = primitives[i]
|
|
2192
|
+
var cacheKey = createPrimitiveKey(primitive)
|
|
2193
|
+
|
|
2194
|
+
// See if we've already created this geometry
|
|
2195
|
+
var cached = cache[cacheKey]
|
|
2196
|
+
|
|
2197
|
+
if (cached) {
|
|
2198
|
+
// Use the cached geometry if it exists
|
|
2199
|
+
pending.push(cached.promise)
|
|
2200
|
+
} else {
|
|
2201
|
+
var geometryPromise
|
|
2202
|
+
|
|
2203
|
+
if (primitive.extensions && primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]) {
|
|
2204
|
+
// Use DRACO geometry if available
|
|
2205
|
+
geometryPromise = createDracoPrimitive(primitive)
|
|
2206
|
+
} else {
|
|
2207
|
+
// Otherwise create a new geometry
|
|
2208
|
+
geometryPromise = addPrimitiveAttributes(new THREE.BufferGeometry(), primitive, parser)
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// Cache this geometry
|
|
2212
|
+
cache[cacheKey] = { primitive: primitive, promise: geometryPromise }
|
|
2213
|
+
|
|
2214
|
+
pending.push(geometryPromise)
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
return Promise.all(pending)
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
/**
|
|
2222
|
+
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
|
|
2223
|
+
* @param {number} meshIndex
|
|
2224
|
+
* @return {Promise<THREE.Group|THREE.Mesh|THREE.SkinnedMesh>}
|
|
2225
|
+
*/
|
|
2226
|
+
GLTFParser.prototype.loadMesh = function (meshIndex) {
|
|
2227
|
+
var parser = this
|
|
2228
|
+
var json = this.json
|
|
2229
|
+
var extensions = this.extensions
|
|
2230
|
+
|
|
2231
|
+
var meshDef = json.meshes[meshIndex]
|
|
2232
|
+
var primitives = meshDef.primitives
|
|
2233
|
+
|
|
2234
|
+
var pending = []
|
|
2235
|
+
|
|
2236
|
+
for (var i = 0, il = primitives.length; i < il; i++) {
|
|
2237
|
+
var material =
|
|
2238
|
+
primitives[i].material === undefined
|
|
2239
|
+
? createDefaultMaterial(this.cache)
|
|
2240
|
+
: this.getDependency('material', primitives[i].material)
|
|
2241
|
+
|
|
2242
|
+
pending.push(material)
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
pending.push(parser.loadGeometries(primitives))
|
|
2246
|
+
|
|
2247
|
+
return Promise.all(pending).then(function (results) {
|
|
2248
|
+
var materials = results.slice(0, results.length - 1)
|
|
2249
|
+
var geometries = results[results.length - 1]
|
|
2250
|
+
|
|
2251
|
+
var meshes = []
|
|
2252
|
+
|
|
2253
|
+
for (var i = 0, il = geometries.length; i < il; i++) {
|
|
2254
|
+
var geometry = geometries[i]
|
|
2255
|
+
var primitive = primitives[i]
|
|
2256
|
+
|
|
2257
|
+
// 1. create Mesh
|
|
2258
|
+
|
|
2259
|
+
var mesh
|
|
2260
|
+
|
|
2261
|
+
var material = materials[i]
|
|
2262
|
+
|
|
2263
|
+
if (
|
|
2264
|
+
primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
|
|
2265
|
+
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
|
|
2266
|
+
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
|
|
2267
|
+
primitive.mode === undefined
|
|
2268
|
+
) {
|
|
2269
|
+
// .isSkinnedMesh isn't in glTF spec. See ._markDefs()
|
|
2270
|
+
mesh =
|
|
2271
|
+
meshDef.isSkinnedMesh === true
|
|
2272
|
+
? new THREE.SkinnedMesh(geometry, material)
|
|
2273
|
+
: new THREE.Mesh(geometry, material)
|
|
2274
|
+
|
|
2275
|
+
if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP) {
|
|
2276
|
+
mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleStripDrawMode)
|
|
2277
|
+
} else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) {
|
|
2278
|
+
mesh.geometry = toTrianglesDrawMode(mesh.geometry, THREE.TriangleFanDrawMode)
|
|
2279
|
+
}
|
|
2280
|
+
} else if (primitive.mode === WEBGL_CONSTANTS.LINES) {
|
|
2281
|
+
mesh = new THREE.LineSegments(geometry, material)
|
|
2282
|
+
} else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) {
|
|
2283
|
+
mesh = new THREE.Line(geometry, material)
|
|
2284
|
+
} else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) {
|
|
2285
|
+
mesh = new THREE.LineLoop(geometry, material)
|
|
2286
|
+
} else if (primitive.mode === WEBGL_CONSTANTS.POINTS) {
|
|
2287
|
+
mesh = new THREE.Points(geometry, material)
|
|
2288
|
+
} else {
|
|
2289
|
+
throw new Error('THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode)
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
if (Object.keys(mesh.geometry.morphAttributes).length > 0) {
|
|
2293
|
+
updateMorphTargets(mesh, meshDef)
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
mesh.name = parser.createUniqueName(meshDef.name || 'mesh_' + meshIndex)
|
|
2297
|
+
|
|
2298
|
+
assignExtrasToUserData(mesh, meshDef)
|
|
2299
|
+
if (primitive.extensions) addUnknownExtensionsToUserData(extensions, mesh, primitive)
|
|
2300
|
+
|
|
2301
|
+
parser.assignFinalMaterial(mesh)
|
|
2302
|
+
|
|
2303
|
+
meshes.push(mesh)
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
if (meshes.length === 1) {
|
|
2307
|
+
return meshes[0]
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
var group = new THREE.Group()
|
|
2311
|
+
|
|
2312
|
+
for (var i = 0, il = meshes.length; i < il; i++) {
|
|
2313
|
+
group.add(meshes[i])
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
return group
|
|
2317
|
+
})
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
/**
|
|
2321
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
|
|
2322
|
+
* @param {number} cameraIndex
|
|
2323
|
+
* @return {Promise<THREE.Camera>}
|
|
2324
|
+
*/
|
|
2325
|
+
GLTFParser.prototype.loadCamera = function (cameraIndex) {
|
|
2326
|
+
var camera
|
|
2327
|
+
var cameraDef = this.json.cameras[cameraIndex]
|
|
2328
|
+
var params = cameraDef[cameraDef.type]
|
|
2329
|
+
|
|
2330
|
+
if (!params) {
|
|
2331
|
+
console.warn('THREE.GLTFLoader: Missing camera parameters.')
|
|
2332
|
+
return
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
if (cameraDef.type === 'perspective') {
|
|
2336
|
+
camera = new THREE.PerspectiveCamera(
|
|
2337
|
+
THREE.MathUtils.radToDeg(params.yfov),
|
|
2338
|
+
params.aspectRatio || 1,
|
|
2339
|
+
params.znear || 1,
|
|
2340
|
+
params.zfar || 2e6
|
|
2341
|
+
)
|
|
2342
|
+
} else if (cameraDef.type === 'orthographic') {
|
|
2343
|
+
camera = new THREE.OrthographicCamera(
|
|
2344
|
+
-params.xmag,
|
|
2345
|
+
params.xmag,
|
|
2346
|
+
params.ymag,
|
|
2347
|
+
-params.ymag,
|
|
2348
|
+
params.znear,
|
|
2349
|
+
params.zfar
|
|
2350
|
+
)
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
if (cameraDef.name) camera.name = this.createUniqueName(cameraDef.name)
|
|
2354
|
+
|
|
2355
|
+
assignExtrasToUserData(camera, cameraDef)
|
|
2356
|
+
|
|
2357
|
+
return Promise.resolve(camera)
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
/**
|
|
2361
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
|
|
2362
|
+
* @param {number} skinIndex
|
|
2363
|
+
* @return {Promise<Object>}
|
|
2364
|
+
*/
|
|
2365
|
+
GLTFParser.prototype.loadSkin = function (skinIndex) {
|
|
2366
|
+
var skinDef = this.json.skins[skinIndex]
|
|
2367
|
+
|
|
2368
|
+
var skinEntry = { joints: skinDef.joints }
|
|
2369
|
+
|
|
2370
|
+
if (skinDef.inverseBindMatrices === undefined) {
|
|
2371
|
+
return Promise.resolve(skinEntry)
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
return this.getDependency('accessor', skinDef.inverseBindMatrices).then(function (accessor) {
|
|
2375
|
+
skinEntry.inverseBindMatrices = accessor
|
|
2376
|
+
|
|
2377
|
+
return skinEntry
|
|
2378
|
+
})
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
/**
|
|
2382
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
|
|
2383
|
+
* @param {number} animationIndex
|
|
2384
|
+
* @return {Promise<THREE.AnimationClip>}
|
|
2385
|
+
*/
|
|
2386
|
+
GLTFParser.prototype.loadAnimation = function (animationIndex) {
|
|
2387
|
+
var json = this.json
|
|
2388
|
+
|
|
2389
|
+
var animationDef = json.animations[animationIndex]
|
|
2390
|
+
|
|
2391
|
+
var pendingNodes = []
|
|
2392
|
+
var pendingInputAccessors = []
|
|
2393
|
+
var pendingOutputAccessors = []
|
|
2394
|
+
var pendingSamplers = []
|
|
2395
|
+
var pendingTargets = []
|
|
2396
|
+
|
|
2397
|
+
for (var i = 0, il = animationDef.channels.length; i < il; i++) {
|
|
2398
|
+
var channel = animationDef.channels[i]
|
|
2399
|
+
var sampler = animationDef.samplers[channel.sampler]
|
|
2400
|
+
var target = channel.target
|
|
2401
|
+
var name = target.node !== undefined ? target.node : target.id // NOTE: target.id is deprecated.
|
|
2402
|
+
var input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input
|
|
2403
|
+
var output = animationDef.parameters !== undefined ? animationDef.parameters[sampler.output] : sampler.output
|
|
2404
|
+
|
|
2405
|
+
pendingNodes.push(this.getDependency('node', name))
|
|
2406
|
+
pendingInputAccessors.push(this.getDependency('accessor', input))
|
|
2407
|
+
pendingOutputAccessors.push(this.getDependency('accessor', output))
|
|
2408
|
+
pendingSamplers.push(sampler)
|
|
2409
|
+
pendingTargets.push(target)
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
return Promise.all([
|
|
2413
|
+
Promise.all(pendingNodes),
|
|
2414
|
+
Promise.all(pendingInputAccessors),
|
|
2415
|
+
Promise.all(pendingOutputAccessors),
|
|
2416
|
+
Promise.all(pendingSamplers),
|
|
2417
|
+
Promise.all(pendingTargets),
|
|
2418
|
+
]).then(function (dependencies) {
|
|
2419
|
+
var nodes = dependencies[0]
|
|
2420
|
+
var inputAccessors = dependencies[1]
|
|
2421
|
+
var outputAccessors = dependencies[2]
|
|
2422
|
+
var samplers = dependencies[3]
|
|
2423
|
+
var targets = dependencies[4]
|
|
2424
|
+
|
|
2425
|
+
var tracks = []
|
|
2426
|
+
|
|
2427
|
+
for (var i = 0, il = nodes.length; i < il; i++) {
|
|
2428
|
+
var node = nodes[i]
|
|
2429
|
+
var inputAccessor = inputAccessors[i]
|
|
2430
|
+
var outputAccessor = outputAccessors[i]
|
|
2431
|
+
var sampler = samplers[i]
|
|
2432
|
+
var target = targets[i]
|
|
2433
|
+
|
|
2434
|
+
if (node === undefined) continue
|
|
2435
|
+
|
|
2436
|
+
node.updateMatrix()
|
|
2437
|
+
node.matrixAutoUpdate = true
|
|
2438
|
+
|
|
2439
|
+
var TypedKeyframeTrack
|
|
2440
|
+
|
|
2441
|
+
switch (PATH_PROPERTIES[target.path]) {
|
|
2442
|
+
case PATH_PROPERTIES.weights:
|
|
2443
|
+
TypedKeyframeTrack = THREE.NumberKeyframeTrack
|
|
2444
|
+
break
|
|
2445
|
+
|
|
2446
|
+
case PATH_PROPERTIES.rotation:
|
|
2447
|
+
TypedKeyframeTrack = THREE.QuaternionKeyframeTrack
|
|
2448
|
+
break
|
|
2449
|
+
|
|
2450
|
+
case PATH_PROPERTIES.position:
|
|
2451
|
+
case PATH_PROPERTIES.scale:
|
|
2452
|
+
default:
|
|
2453
|
+
TypedKeyframeTrack = THREE.VectorKeyframeTrack
|
|
2454
|
+
break
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
var targetName = node.name ? node.name : node.uuid
|
|
2458
|
+
|
|
2459
|
+
var interpolation =
|
|
2460
|
+
sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear
|
|
2461
|
+
|
|
2462
|
+
var targetNames = []
|
|
2463
|
+
|
|
2464
|
+
if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
|
|
2465
|
+
// Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.Mesh.
|
|
2466
|
+
node.traverse(function (object) {
|
|
2467
|
+
if (object.isMesh === true && object.morphTargetInfluences) {
|
|
2468
|
+
targetNames.push(object.name ? object.name : object.uuid)
|
|
2469
|
+
}
|
|
2470
|
+
})
|
|
2471
|
+
} else {
|
|
2472
|
+
targetNames.push(targetName)
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
var outputArray = outputAccessor.array
|
|
2476
|
+
|
|
2477
|
+
if (outputAccessor.normalized) {
|
|
2478
|
+
var scale
|
|
2479
|
+
|
|
2480
|
+
if (outputArray.constructor === Int8Array) {
|
|
2481
|
+
scale = 1 / 127
|
|
2482
|
+
} else if (outputArray.constructor === Uint8Array) {
|
|
2483
|
+
scale = 1 / 255
|
|
2484
|
+
} else if (outputArray.constructor == Int16Array) {
|
|
2485
|
+
scale = 1 / 32767
|
|
2486
|
+
} else if (outputArray.constructor === Uint16Array) {
|
|
2487
|
+
scale = 1 / 65535
|
|
2488
|
+
} else {
|
|
2489
|
+
throw new Error('THREE.GLTFLoader: Unsupported output accessor component type.')
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
var scaled = new Float32Array(outputArray.length)
|
|
2493
|
+
|
|
2494
|
+
for (var j = 0, jl = outputArray.length; j < jl; j++) {
|
|
2495
|
+
scaled[j] = outputArray[j] * scale
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
outputArray = scaled
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex
|
|
2503
|
+
var clip = new THREE.AnimationClip(name, undefined, tracks)
|
|
2504
|
+
clip.targetNames = targetNames
|
|
2505
|
+
return clip
|
|
2506
|
+
})
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
/**
|
|
2510
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
|
|
2511
|
+
* @param {number} nodeIndex
|
|
2512
|
+
* @return {Promise<THREE.Object3D>}
|
|
2513
|
+
*/
|
|
2514
|
+
GLTFParser.prototype.loadNode = function (nodeIndex) {
|
|
2515
|
+
var json = this.json
|
|
2516
|
+
var extensions = this.extensions
|
|
2517
|
+
var parser = this
|
|
2518
|
+
|
|
2519
|
+
var nodeDef = json.nodes[nodeIndex]
|
|
2520
|
+
|
|
2521
|
+
// reserve node's name before its dependencies, so the root has the intended name.
|
|
2522
|
+
var nodeName = nodeDef.name ? parser.createUniqueName(nodeDef.name) : ''
|
|
2523
|
+
|
|
2524
|
+
return (function () {
|
|
2525
|
+
var pending = []
|
|
2526
|
+
|
|
2527
|
+
if (nodeDef.mesh !== undefined) {
|
|
2528
|
+
pending.push(
|
|
2529
|
+
parser.getDependency('mesh', nodeDef.mesh).then(function (mesh) {
|
|
2530
|
+
var node = parser._getNodeRef(parser.meshCache, nodeDef.mesh, mesh)
|
|
2531
|
+
|
|
2532
|
+
// if weights are provided on the node, override weights on the mesh.
|
|
2533
|
+
if (nodeDef.weights !== undefined) {
|
|
2534
|
+
node.traverse(function (o) {
|
|
2535
|
+
if (!o.isMesh) return
|
|
2536
|
+
|
|
2537
|
+
for (var i = 0, il = nodeDef.weights.length; i < il; i++) {
|
|
2538
|
+
o.morphTargetInfluences[i] = nodeDef.weights[i]
|
|
2539
|
+
}
|
|
2540
|
+
})
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
return node
|
|
2544
|
+
})
|
|
2545
|
+
)
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
if (nodeDef.camera !== undefined) {
|
|
2549
|
+
pending.push(
|
|
2550
|
+
parser.getDependency('camera', nodeDef.camera).then(function (camera) {
|
|
2551
|
+
return parser._getNodeRef(parser.cameraCache, nodeDef.camera, camera)
|
|
2552
|
+
})
|
|
2553
|
+
)
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
parser
|
|
2557
|
+
._invokeAll(function (ext) {
|
|
2558
|
+
return ext.createNodeAttachment && ext.createNodeAttachment(nodeIndex)
|
|
2559
|
+
})
|
|
2560
|
+
.forEach(function (promise) {
|
|
2561
|
+
pending.push(promise)
|
|
2562
|
+
})
|
|
2563
|
+
|
|
2564
|
+
return Promise.all(pending)
|
|
2565
|
+
})().then(function (objects) {
|
|
2566
|
+
var node
|
|
2567
|
+
|
|
2568
|
+
// .isBone isn't in glTF spec. See ._markDefs
|
|
2569
|
+
if (nodeDef.isBone === true) {
|
|
2570
|
+
node = new THREE.Bone()
|
|
2571
|
+
} else if (objects.length > 1) {
|
|
2572
|
+
node = new THREE.Group()
|
|
2573
|
+
} else if (objects.length === 1) {
|
|
2574
|
+
node = objects[0]
|
|
2575
|
+
} else {
|
|
2576
|
+
node = new THREE.Object3D()
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
if (node !== objects[0]) {
|
|
2580
|
+
for (var i = 0, il = objects.length; i < il; i++) {
|
|
2581
|
+
node.add(objects[i])
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
if (nodeDef.name) {
|
|
2586
|
+
node.userData.name = nodeDef.name
|
|
2587
|
+
node.name = nodeName
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
assignExtrasToUserData(node, nodeDef)
|
|
2591
|
+
|
|
2592
|
+
if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef)
|
|
2593
|
+
|
|
2594
|
+
if (nodeDef.matrix !== undefined) {
|
|
2595
|
+
var matrix = new THREE.Matrix4()
|
|
2596
|
+
matrix.fromArray(nodeDef.matrix)
|
|
2597
|
+
node.applyMatrix4(matrix)
|
|
2598
|
+
} else {
|
|
2599
|
+
if (nodeDef.translation !== undefined) {
|
|
2600
|
+
node.position.fromArray(nodeDef.translation)
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
if (nodeDef.rotation !== undefined) {
|
|
2604
|
+
node.quaternion.fromArray(nodeDef.rotation)
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
if (nodeDef.scale !== undefined) {
|
|
2608
|
+
node.scale.fromArray(nodeDef.scale)
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
parser.associations.set(node, { type: 'nodes', index: nodeIndex })
|
|
2613
|
+
|
|
2614
|
+
return node
|
|
2615
|
+
})
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
/**
|
|
2619
|
+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
|
|
2620
|
+
* @param {number} sceneIndex
|
|
2621
|
+
* @return {Promise<THREE.Group>}
|
|
2622
|
+
*/
|
|
2623
|
+
GLTFParser.prototype.loadScene = (function () {
|
|
2624
|
+
// scene node hierachy builder
|
|
2625
|
+
|
|
2626
|
+
function buildNodeHierachy(nodeId, parentObject, json, parser) {
|
|
2627
|
+
var nodeDef = json.nodes[nodeId]
|
|
2628
|
+
|
|
2629
|
+
return parser
|
|
2630
|
+
.getDependency('node', nodeId)
|
|
2631
|
+
.then(function (node) {
|
|
2632
|
+
if (nodeDef.skin === undefined) return node
|
|
2633
|
+
|
|
2634
|
+
// build skeleton here as well
|
|
2635
|
+
|
|
2636
|
+
var skinEntry
|
|
2637
|
+
|
|
2638
|
+
return parser
|
|
2639
|
+
.getDependency('skin', nodeDef.skin)
|
|
2640
|
+
.then(function (skin) {
|
|
2641
|
+
skinEntry = skin
|
|
2642
|
+
|
|
2643
|
+
var pendingJoints = []
|
|
2644
|
+
|
|
2645
|
+
for (var i = 0, il = skinEntry.joints.length; i < il; i++) {
|
|
2646
|
+
pendingJoints.push(parser.getDependency('node', skinEntry.joints[i]))
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
return Promise.all(pendingJoints)
|
|
2650
|
+
})
|
|
2651
|
+
.then(function (jointNodes) {
|
|
2652
|
+
node.traverse(function (mesh) {
|
|
2653
|
+
if (!mesh.isMesh) return
|
|
2654
|
+
|
|
2655
|
+
var bones = []
|
|
2656
|
+
var boneInverses = []
|
|
2657
|
+
|
|
2658
|
+
for (var j = 0, jl = jointNodes.length; j < jl; j++) {
|
|
2659
|
+
var jointNode = jointNodes[j]
|
|
2660
|
+
|
|
2661
|
+
if (jointNode) {
|
|
2662
|
+
bones.push(jointNode)
|
|
2663
|
+
|
|
2664
|
+
var mat = new THREE.Matrix4()
|
|
2665
|
+
|
|
2666
|
+
if (skinEntry.inverseBindMatrices !== undefined) {
|
|
2667
|
+
mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16)
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
boneInverses.push(mat)
|
|
2671
|
+
} else {
|
|
2672
|
+
console.warn('THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[j])
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
mesh.bind(new THREE.Skeleton(bones, boneInverses), mesh.matrixWorld)
|
|
2677
|
+
})
|
|
2678
|
+
|
|
2679
|
+
return node
|
|
2680
|
+
})
|
|
2681
|
+
})
|
|
2682
|
+
.then(function (node) {
|
|
2683
|
+
// build node hierachy
|
|
2684
|
+
|
|
2685
|
+
parentObject.add(node)
|
|
2686
|
+
|
|
2687
|
+
var pending = []
|
|
2688
|
+
|
|
2689
|
+
if (nodeDef.children) {
|
|
2690
|
+
var children = nodeDef.children
|
|
2691
|
+
|
|
2692
|
+
for (var i = 0, il = children.length; i < il; i++) {
|
|
2693
|
+
var child = children[i]
|
|
2694
|
+
pending.push(buildNodeHierachy(child, node, json, parser))
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
return Promise.all(pending)
|
|
2699
|
+
})
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
return function loadScene(sceneIndex) {
|
|
2703
|
+
var json = this.json
|
|
2704
|
+
var extensions = this.extensions
|
|
2705
|
+
var sceneDef = this.json.scenes[sceneIndex]
|
|
2706
|
+
var parser = this
|
|
2707
|
+
|
|
2708
|
+
// Loader returns Group, not Scene.
|
|
2709
|
+
// See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
|
|
2710
|
+
var scene = new THREE.Group()
|
|
2711
|
+
if (sceneDef.name) scene.name = parser.createUniqueName(sceneDef.name)
|
|
2712
|
+
|
|
2713
|
+
assignExtrasToUserData(scene, sceneDef)
|
|
2714
|
+
|
|
2715
|
+
if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef)
|
|
2716
|
+
|
|
2717
|
+
var nodeIds = sceneDef.nodes || []
|
|
2718
|
+
|
|
2719
|
+
var pending = []
|
|
2720
|
+
|
|
2721
|
+
for (var i = 0, il = nodeIds.length; i < il; i++) {
|
|
2722
|
+
pending.push(buildNodeHierachy(nodeIds[i], scene, json, parser))
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
return Promise.all(pending).then(function () {
|
|
2726
|
+
return scene
|
|
2727
|
+
})
|
|
2728
|
+
}
|
|
2729
|
+
})()
|
|
2730
|
+
|
|
2731
|
+
return GLTFLoader
|
|
2732
|
+
})())
|
|
2733
|
+
|
|
2734
|
+
export default GLTFLoader
|