itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
1,275 lines (1,225 loc) • 54.8 kB
JavaScript
/* eslint-disable */
// LegacyGLTFLoader for loading gltf 1.0 files taken from THREE v110 because it was removed in THREE v111 and maintained
// since
import * as THREE from 'three';
const threeExamples = {};
/**
* @author Rich Tibbett / https://github.com/richtr
* @author mrdoob / http://mrdoob.com/
* @author Tony Parisi / http://www.tonyparisi.com/
* @author Takahiro / https://github.com/takahirox
*/
threeExamples.LegacyGLTFLoader = function () {
class LegacyGLTFLoader extends THREE.Loader {
constructor(manager) {
super(manager);
}
load(url, onLoad, onProgress, onError) {
var scope = this;
var resourcePath;
if (this.resourcePath !== '') {
resourcePath = this.resourcePath;
} else if (this.path !== '') {
resourcePath = this.path;
} else {
resourcePath = THREE.LoaderUtils.extractUrlBase(url);
}
var loader = new THREE.FileLoader(scope.manager);
loader.setPath(this.path);
loader.setResponseType('arraybuffer');
loader.load(url, function (data) {
scope.parse(data, resourcePath, onLoad);
}, onProgress, onError);
}
parse(data, path, callback) {
var content;
var extensions = {};
var magic = THREE.LoaderUtils.decodeText(new Uint8Array(data, 0, 4));
if (magic === BINARY_EXTENSION_HEADER_DEFAULTS.magic) {
extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data);
content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content;
} else {
content = THREE.LoaderUtils.decodeText(new Uint8Array(data));
}
var json = JSON.parse(content);
if (json.extensionsUsed && json.extensionsUsed.indexOf(EXTENSIONS.KHR_MATERIALS_COMMON) >= 0) {
extensions[EXTENSIONS.KHR_MATERIALS_COMMON] = new GLTFMaterialsCommonExtension(json);
}
if (json.extensionsUsed && json.extensionsUsed.indexOf(EXTENSIONS.CESIUM_RTC) >= 0) {
extensions[EXTENSIONS.CESIUM_RTC] = new CesiumRTCExtension(json);
}
var parser = new GLTFParser(json, extensions, {
crossOrigin: this.crossOrigin,
manager: this.manager,
path: path || this.resourcePath || ''
});
parser.parse(function (scene, scenes, cameras, animations) {
callback({
"scene": scene,
"scenes": scenes,
"cameras": cameras,
"animations": animations
});
});
}
}
/* GLTFREGISTRY */
function GLTFRegistry() {
var objects = {};
return {
get: function (key) {
return objects[key];
},
add: function (key, object) {
objects[key] = object;
},
remove: function (key) {
delete objects[key];
},
removeAll: function () {
objects = {};
},
update: function (scene, camera) {
for (var name in objects) {
var object = objects[name];
if (object.update) {
object.update(scene, camera);
}
}
}
};
}
/* GLTFSHADERS */
LegacyGLTFLoader.Shaders = {
update: function () {
console.warn('threeExamples.LegacyGLTFLoader.Shaders has been deprecated, and now updates automatically.');
}
};
/* GLTFSHADER */
function GLTFShader(targetNode, allNodes) {
var boundUniforms = {};
// bind each uniform to its source node
var uniforms = targetNode.material.uniforms;
for (var uniformId in uniforms) {
var uniform = uniforms[uniformId];
if (uniform.semantic) {
var sourceNodeRef = uniform.node;
var sourceNode = targetNode;
if (sourceNodeRef) {
sourceNode = allNodes[sourceNodeRef];
}
boundUniforms[uniformId] = {
semantic: uniform.semantic,
sourceNode: sourceNode,
targetNode: targetNode,
uniform: uniform
};
}
}
this.boundUniforms = boundUniforms;
this._m4 = new THREE.Matrix4();
}
// Update - update all the uniform values
GLTFShader.prototype.update = function (scene, camera) {
var boundUniforms = this.boundUniforms;
for (var name in boundUniforms) {
var boundUniform = boundUniforms[name];
switch (boundUniform.semantic) {
case "MODELVIEW":
var m4 = boundUniform.uniform.value;
m4.multiplyMatrices(camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld);
break;
case "MODELVIEWINVERSETRANSPOSE":
var m3 = boundUniform.uniform.value;
this._m4.multiplyMatrices(camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld);
m3.getNormalMatrix(this._m4);
break;
case "PROJECTION":
var m4 = boundUniform.uniform.value;
m4.copy(camera.projectionMatrix);
break;
case "JOINTMATRIX":
var m4v = boundUniform.uniform.value;
for (var mi = 0; mi < m4v.length; mi++) {
// So it goes like this:
// SkinnedMesh world matrix is already baked into MODELVIEW;
// transform joints to local space,
// then transform using joint's inverse
m4v[mi].getInverse(boundUniform.sourceNode.matrixWorld).multiply(boundUniform.targetNode.skeleton.bones[mi].matrixWorld).multiply(boundUniform.targetNode.skeleton.boneInverses[mi]).multiply(boundUniform.targetNode.bindMatrix);
}
break;
default:
console.warn("Unhandled shader semantic: " + boundUniform.semantic);
break;
}
}
};
/* ANIMATION */
LegacyGLTFLoader.Animations = {
update: function () {
console.warn('threeExamples.LegacyGLTFLoader.Animation has been deprecated. Use THREE.AnimationMixer instead.');
}
};
/*********************************/
/********** EXTENSIONS ***********/
/*********************************/
var EXTENSIONS = {
KHR_BINARY_GLTF: 'KHR_binary_glTF',
KHR_MATERIALS_COMMON: 'KHR_materials_common',
CESIUM_RTC: 'CESIUM_RTC'
};
/* MATERIALS COMMON EXTENSION */
function GLTFMaterialsCommonExtension(json) {
this.name = EXTENSIONS.KHR_MATERIALS_COMMON;
this.lights = {};
var extension = json.extensions && json.extensions[EXTENSIONS.KHR_MATERIALS_COMMON] || {};
var lights = extension.lights || {};
for (var lightId in lights) {
var light = lights[lightId];
var lightNode;
var lightParams = light[light.type];
var color = new THREE.Color().fromArray(lightParams.color);
switch (light.type) {
case "directional":
lightNode = new THREE.DirectionalLight(color);
lightNode.position.set(0, 0, 1);
break;
case "point":
lightNode = new THREE.PointLight(color);
break;
case "spot":
lightNode = new THREE.SpotLight(color);
lightNode.position.set(0, 0, 1);
break;
case "ambient":
lightNode = new THREE.AmbientLight(color);
break;
}
if (lightNode) {
this.lights[lightId] = lightNode;
}
}
}
/* BINARY EXTENSION */
var BINARY_EXTENSION_HEADER_DEFAULTS = {
magic: 'glTF',
version: 1,
contentFormat: 0
};
var BINARY_EXTENSION_HEADER_LENGTH = 20;
function GLTFBinaryExtension(data) {
this.name = EXTENSIONS.KHR_BINARY_GLTF;
var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
var header = {
magic: THREE.LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))),
version: headerView.getUint32(4, true),
length: headerView.getUint32(8, true),
contentLength: headerView.getUint32(12, true),
contentFormat: headerView.getUint32(16, true)
};
for (var key in BINARY_EXTENSION_HEADER_DEFAULTS) {
var value = BINARY_EXTENSION_HEADER_DEFAULTS[key];
if (header[key] !== value) {
throw new Error('Unsupported glTF-Binary header: Expected "%s" to be "%s".', key, value);
}
}
var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH, header.contentLength);
this.header = header;
this.content = THREE.LoaderUtils.decodeText(contentArray);
this.body = data.slice(BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length);
}
GLTFBinaryExtension.prototype.loadShader = function (shader, bufferViews) {
var bufferView = bufferViews[shader.extensions[EXTENSIONS.KHR_BINARY_GLTF].bufferView];
var array = new Uint8Array(bufferView);
return THREE.LoaderUtils.decodeText(array);
};
// Ref spec https://github.com/KhronosGroup/glTF/blob/main/extensions/1.0/Vendor/CESIUM_RTC/README.md
// Only the json storage method is implemented since it is the only one we've seen out there and since this extension
// is specific to deprecated 3D tiles with GLTF 1.0
function CesiumRTCExtension(json) {
this.name = EXTENSIONS.CESIUM_RTC;
this.center = [0, 0, 0];
if (json.extensions && json.extensions[EXTENSIONS.CESIUM_RTC] && json.extensions[EXTENSIONS.CESIUM_RTC].center && json.extensions[EXTENSIONS.CESIUM_RTC].center.length === 3) {
this.center = json.extensions[EXTENSIONS.CESIUM_RTC].center;
}
}
/*********************************/
/********** INTERNALS ************/
/*********************************/
/* CONSTANTS */
var WEBGL_CONSTANTS = {
FLOAT: 5126,
//FLOAT_MAT2: 35674,
FLOAT_MAT3: 35675,
FLOAT_MAT4: 35676,
FLOAT_VEC2: 35664,
FLOAT_VEC3: 35665,
FLOAT_VEC4: 35666,
LINEAR: 9729,
REPEAT: 10497,
SAMPLER_2D: 35678,
TRIANGLES: 4,
LINES: 1,
UNSIGNED_BYTE: 5121,
UNSIGNED_SHORT: 5123,
VERTEX_SHADER: 35633,
FRAGMENT_SHADER: 35632
};
var WEBGL_TYPE = {
5126: Number,
//35674: THREE.Matrix2,
35675: THREE.Matrix3,
35676: THREE.Matrix4,
35664: THREE.Vector2,
35665: THREE.Vector3,
35666: THREE.Vector4,
35678: THREE.Texture
};
var WEBGL_COMPONENT_TYPES = {
5120: Int8Array,
5121: Uint8Array,
5122: Int16Array,
5123: Uint16Array,
5125: Uint32Array,
5126: Float32Array
};
var WEBGL_FILTERS = {
9728: THREE.NearestFilter,
9729: THREE.LinearFilter,
9984: THREE.NearestMipmapNearestFilter,
9985: THREE.LinearMipmapNearestFilter,
9986: THREE.NearestMipmapLinearFilter,
9987: THREE.LinearMipmapLinearFilter
};
var WEBGL_WRAPPINGS = {
33071: THREE.ClampToEdgeWrapping,
33648: THREE.MirroredRepeatWrapping,
10497: THREE.RepeatWrapping
};
var WEBGL_TEXTURE_FORMATS = {
6406: THREE.AlphaFormat,
6407: THREE.RGBAFormat,
6408: THREE.RGBAFormat,
6409: THREE.LuminanceFormat,
6410: THREE.LuminanceAlphaFormat
};
var WEBGL_TEXTURE_DATATYPES = {
5121: THREE.UnsignedByteType,
32819: THREE.UnsignedShort4444Type,
32820: THREE.UnsignedShort5551Type,
33635: THREE.UnsignedShort5551Type
};
var WEBGL_SIDES = {
1028: THREE.BackSide,
// Culling front
1029: THREE.FrontSide // Culling back
//1032: THREE.NoSide // Culling front and back, what to do?
};
var WEBGL_DEPTH_FUNCS = {
512: THREE.NeverDepth,
513: THREE.LessDepth,
514: THREE.EqualDepth,
515: THREE.LessEqualDepth,
516: THREE.GreaterEqualDepth,
517: THREE.NotEqualDepth,
518: THREE.GreaterEqualDepth,
519: THREE.AlwaysDepth
};
var WEBGL_BLEND_EQUATIONS = {
32774: THREE.AddEquation,
32778: THREE.SubtractEquation,
32779: THREE.ReverseSubtractEquation
};
var WEBGL_BLEND_FUNCS = {
0: THREE.ZeroFactor,
1: THREE.OneFactor,
768: THREE.SrcColorFactor,
769: THREE.OneMinusSrcColorFactor,
770: THREE.SrcAlphaFactor,
771: THREE.OneMinusSrcAlphaFactor,
772: THREE.DstAlphaFactor,
773: THREE.OneMinusDstAlphaFactor,
774: THREE.DstColorFactor,
775: THREE.OneMinusDstColorFactor,
776: THREE.SrcAlphaSaturateFactor
// The followings are not supported by Three.js yet
//32769: CONSTANT_COLOR,
//32770: ONE_MINUS_CONSTANT_COLOR,
//32771: CONSTANT_ALPHA,
//32772: ONE_MINUS_CONSTANT_COLOR
};
var WEBGL_TYPE_SIZES = {
'SCALAR': 1,
'VEC2': 2,
'VEC3': 3,
'VEC4': 4,
'MAT2': 4,
'MAT3': 9,
'MAT4': 16
};
var PATH_PROPERTIES = {
scale: 'scale',
translation: 'position',
rotation: 'quaternion'
};
var INTERPOLATION = {
LINEAR: THREE.InterpolateLinear,
STEP: THREE.InterpolateDiscrete
};
var STATES_ENABLES = {
2884: 'CULL_FACE',
2929: 'DEPTH_TEST',
3042: 'BLEND',
3089: 'SCISSOR_TEST',
32823: 'POLYGON_OFFSET_FILL',
32926: 'SAMPLE_ALPHA_TO_COVERAGE'
};
/* UTILITY FUNCTIONS */
function _each(object, callback, thisObj) {
if (!object) {
return Promise.resolve();
}
var results;
var fns = [];
if (Object.prototype.toString.call(object) === '[object Array]') {
results = [];
var length = object.length;
for (var idx = 0; idx < length; idx++) {
var value = callback.call(thisObj || this, object[idx], idx);
if (value) {
fns.push(value);
if (value instanceof Promise) {
value.then(function (key, value) {
results[key] = value;
}.bind(this, idx));
} else {
results[idx] = value;
}
}
}
} else {
results = {};
for (var key in object) {
if (object.hasOwnProperty(key)) {
var value = callback.call(thisObj || this, object[key], key);
if (value) {
fns.push(value);
if (value instanceof Promise) {
value.then(function (key, value) {
results[key] = value;
}.bind(this, key));
} else {
results[key] = value;
}
}
}
}
}
return Promise.all(fns).then(function () {
return results;
});
}
function resolveURL(url, path) {
// Invalid URL
if (typeof url !== 'string' || url === '') return '';
// Absolute URL http://,https://,//
if (/^(https?:)?\/\//i.test(url)) {
return url;
}
// Data URI
if (/^data:.*,.*$/i.test(url)) {
return url;
}
// Blob URL
if (/^blob:.*$/i.test(url)) {
return url;
}
// Relative URL
return (path || '') + url;
}
// Three.js seems too dependent on attribute names so globally
// replace those in the shader code
function replaceTHREEShaderAttributes(shaderText, technique) {
// Expected technique attributes
var attributes = {};
for (var attributeId in technique.attributes) {
var pname = technique.attributes[attributeId];
var param = technique.parameters[pname];
var atype = param.type;
var semantic = param.semantic;
attributes[attributeId] = {
type: atype,
semantic: semantic
};
}
// Figure out which attributes to change in technique
var shaderParams = technique.parameters;
var shaderAttributes = technique.attributes;
var params = {};
for (var attributeId in attributes) {
var pname = shaderAttributes[attributeId];
var shaderParam = shaderParams[pname];
var semantic = shaderParam.semantic;
if (semantic) {
params[attributeId] = shaderParam;
}
}
for (var pname in params) {
var param = params[pname];
var semantic = param.semantic;
var regEx = new RegExp("\\b" + pname + "\\b", "g");
switch (semantic) {
case "POSITION":
shaderText = shaderText.replace(regEx, 'position');
break;
case "NORMAL":
shaderText = shaderText.replace(regEx, 'normal');
break;
case 'TEXCOORD_0':
case 'TEXCOORD0':
case 'TEXCOORD':
shaderText = shaderText.replace(regEx, 'uv');
break;
case 'TEXCOORD_1':
shaderText = shaderText.replace(regEx, 'uv2');
break;
case 'COLOR_0':
case 'COLOR0':
case 'COLOR':
shaderText = shaderText.replace(regEx, 'color');
break;
case "WEIGHT":
shaderText = shaderText.replace(regEx, 'skinWeight');
break;
case "JOINT":
shaderText = shaderText.replace(regEx, 'skinIndex');
break;
}
}
return shaderText;
}
function createDefaultMaterial() {
return new THREE.MeshPhongMaterial({
color: 0x00000,
emissive: 0x888888,
specular: 0x000000,
shininess: 0,
transparent: false,
depthTest: true,
side: THREE.FrontSide
});
}
/**
* Verifies if the shaders are Cesium specific: if they contain attributes, uniforms or functions starting with
* `czm_`. The cesium gltf-pipeline (the ancestor of cesium ion) used to create 3D Tiles tilesets they are only
* defined in Cesium.
* @param {Object} shaders
*/
function areShadersCesiumSpecific(shaders) {
for (const shaderId in shaders) {
if (shaders[shaderId].includes('czm_')) {
return true;
}
}
return false;
}
// Deferred constructor for RawShaderMaterial types
function DeferredShaderMaterial(params) {
this.isDeferredShaderMaterial = true;
this.params = params;
}
DeferredShaderMaterial.prototype.create = function () {
var uniforms = THREE.UniformsUtils.clone(this.params.uniforms);
for (var uniformId in this.params.uniforms) {
var originalUniform = this.params.uniforms[uniformId];
if (originalUniform.value instanceof THREE.Texture) {
uniforms[uniformId].value = originalUniform.value;
uniforms[uniformId].value.needsUpdate = true;
}
uniforms[uniformId].semantic = originalUniform.semantic;
uniforms[uniformId].node = originalUniform.node;
}
this.params.uniforms = uniforms;
return new THREE.RawShaderMaterial(this.params);
};
/* GLTF PARSER */
function GLTFParser(json, extensions, options) {
this.json = json || {};
this.extensions = extensions || {};
this.options = options || {};
// loader object cache
this.cache = new GLTFRegistry();
}
GLTFParser.prototype._withDependencies = function (dependencies) {
var _dependencies = {};
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
var fnName = "load" + dependency.charAt(0).toUpperCase() + dependency.slice(1);
var cached = this.cache.get(dependency);
if (cached !== undefined) {
_dependencies[dependency] = cached;
} else if (this[fnName]) {
var fn = this[fnName]();
this.cache.add(dependency, fn);
_dependencies[dependency] = fn;
}
}
return _each(_dependencies, function (dependency) {
return dependency;
});
};
GLTFParser.prototype.parse = function (callback) {
var json = this.json;
// Clear the loader cache
this.cache.removeAll();
// Fire the callback on complete
this._withDependencies(["scenes", "cameras", "animations"]).then(function (dependencies) {
var scenes = [];
for (var name in dependencies.scenes) {
scenes.push(dependencies.scenes[name]);
}
var scene = json.scene !== undefined ? dependencies.scenes[json.scene] : scenes[0];
var cameras = [];
for (var name in dependencies.cameras) {
var camera = dependencies.cameras[name];
cameras.push(camera);
}
var animations = [];
for (var name in dependencies.animations) {
animations.push(dependencies.animations[name]);
}
callback(scene, scenes, cameras, animations);
});
};
GLTFParser.prototype.loadShaders = function () {
var json = this.json;
var extensions = this.extensions;
var options = this.options;
return this._withDependencies(["bufferViews"]).then(function (dependencies) {
return _each(json.shaders, function (shader) {
if (shader.extensions && shader.extensions[EXTENSIONS.KHR_BINARY_GLTF]) {
return extensions[EXTENSIONS.KHR_BINARY_GLTF].loadShader(shader, dependencies.bufferViews);
}
return new Promise(function (resolve) {
var loader = new THREE.FileLoader(options.manager);
loader.setResponseType('text');
loader.load(resolveURL(shader.uri, options.path), function (shaderText) {
resolve(shaderText);
});
});
});
});
};
GLTFParser.prototype.loadBuffers = function () {
var json = this.json;
var extensions = this.extensions;
var options = this.options;
return _each(json.buffers, function (buffer, name) {
if (name === 'binary_glTF' || name === EXTENSIONS.KHR_BINARY_GLTF) {
return extensions[EXTENSIONS.KHR_BINARY_GLTF].body;
}
if (buffer.type === 'arraybuffer' || buffer.type === undefined) {
return new Promise(function (resolve) {
var loader = new THREE.FileLoader(options.manager);
loader.setResponseType('arraybuffer');
loader.load(resolveURL(buffer.uri, options.path), function (buffer) {
resolve(buffer);
});
});
} else {
console.warn('threeExamples.LegacyGLTFLoader: ' + buffer.type + ' buffer type is not supported');
}
});
};
GLTFParser.prototype.loadBufferViews = function () {
var json = this.json;
return this._withDependencies(["buffers"]).then(function (dependencies) {
return _each(json.bufferViews, function (bufferView) {
var arraybuffer = dependencies.buffers[bufferView.buffer];
var byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0;
return arraybuffer.slice(bufferView.byteOffset, bufferView.byteOffset + byteLength);
});
});
};
GLTFParser.prototype.loadAccessors = function () {
var json = this.json;
return this._withDependencies(["bufferViews"]).then(function (dependencies) {
return _each(json.accessors, function (accessor) {
var arraybuffer = dependencies.bufferViews[accessor.bufferView];
var itemSize = WEBGL_TYPE_SIZES[accessor.type];
var TypedArray = WEBGL_COMPONENT_TYPES[accessor.componentType];
// For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
var elementBytes = TypedArray.BYTES_PER_ELEMENT;
// The buffer is not interleaved if the stride is the item size in bytes.
if (accessor.byteStride && accessor.byteStride !== elementBytes * itemSize) {
// Use the full buffer if it's interleaved.
var array = new TypedArray(arraybuffer);
// Integer parameters to IB/IBA are in array elements, not bytes.
var ib = new THREE.InterleavedBuffer(array, accessor.byteStride / elementBytes);
return new THREE.InterleavedBufferAttribute(ib, itemSize, accessor.byteOffset / elementBytes);
} else {
array = new TypedArray(arraybuffer, accessor.byteOffset, accessor.count * itemSize);
return new THREE.BufferAttribute(array, itemSize);
}
});
});
};
GLTFParser.prototype.loadTextures = function () {
var json = this.json;
var options = this.options;
return this._withDependencies(["bufferViews"]).then(function (dependencies) {
return _each(json.textures, function (texture) {
if (texture.source) {
return new Promise(function (resolve) {
var source = json.images[texture.source];
var sourceUri = source.uri;
var isObjectURL = false;
if (source.extensions && source.extensions[EXTENSIONS.KHR_BINARY_GLTF]) {
var metadata = source.extensions[EXTENSIONS.KHR_BINARY_GLTF];
var bufferView = dependencies.bufferViews[metadata.bufferView];
var blob = new Blob([bufferView], {
type: metadata.mimeType
});
sourceUri = URL.createObjectURL(blob);
isObjectURL = true;
}
var textureLoader = options.manager.getHandler(sourceUri);
if (textureLoader === null) {
textureLoader = new THREE.TextureLoader(options.manager);
}
textureLoader.setCrossOrigin(options.crossOrigin);
textureLoader.load(resolveURL(sourceUri, options.path), function (_texture) {
if (isObjectURL) URL.revokeObjectURL(sourceUri);
_texture.flipY = false;
if (texture.name !== undefined) _texture.name = texture.name;
_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[texture.format] : THREE.RGBAFormat;
if (texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[texture.internalFormat]) {
console.warn('threeExamples.LegacyGLTFLoader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' + 'internalFormat will be forced to be the same value as format.');
}
_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[texture.type] : THREE.UnsignedByteType;
if (texture.sampler) {
var sampler = json.samplers[texture.sampler];
_texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || THREE.LinearFilter;
_texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.NearestMipmapLinearFilter;
_texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping;
_texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.RepeatWrapping;
}
resolve(_texture);
}, undefined, function () {
if (isObjectURL) URL.revokeObjectURL(sourceUri);
resolve();
});
});
}
});
});
};
GLTFParser.prototype.loadMaterials = function () {
var json = this.json;
return this._withDependencies(["shaders", "textures"]).then(function (dependencies) {
return _each(json.materials, function (material) {
var materialType;
var materialValues = {};
var materialParams = {};
var khr_material;
if (material.extensions && material.extensions[EXTENSIONS.KHR_MATERIALS_COMMON]) {
khr_material = material.extensions[EXTENSIONS.KHR_MATERIALS_COMMON];
}
if (khr_material) {
// don't copy over unused values to avoid material warning spam
var keys = ['ambient', 'emission', 'transparent', 'transparency', 'doubleSided'];
switch (khr_material.technique) {
case 'BLINN':
case 'PHONG':
materialType = THREE.MeshPhongMaterial;
keys.push('diffuse', 'specular', 'shininess');
break;
case 'LAMBERT':
materialType = THREE.MeshLambertMaterial;
keys.push('diffuse');
break;
case 'CONSTANT':
default:
materialType = THREE.MeshBasicMaterial;
break;
}
keys.forEach(function (v) {
if (khr_material.values[v] !== undefined) materialValues[v] = khr_material.values[v];
});
if (khr_material.doubleSided || materialValues.doubleSided) {
materialParams.side = THREE.DoubleSide;
}
if (khr_material.transparent || materialValues.transparent) {
materialParams.transparent = true;
materialParams.opacity = materialValues.transparency !== undefined ? materialValues.transparency : 1;
}
} else if (material.technique === undefined) {
materialType = THREE.MeshPhongMaterial;
Object.assign(materialValues, material.values);
} else {
const technique = json.techniques[material.technique];
// If shaders are Cesium specific, set up a MeshBasicMaterial to avoid an error at shader compilation
if (areShadersCesiumSpecific(dependencies.shaders)) {
materialType = THREE.MeshBasicMaterial;
// Retrieve texture from uniforms so it is not lost in the process.
let texture = null;
const uniforms = technique.uniforms;
for (const uniformId in uniforms) {
const pname = uniforms[uniformId];
const shaderParam = technique.parameters[pname];
const ptype = shaderParam.type;
if (ptype === WEBGL_CONSTANTS.SAMPLER_2D) {
let value;
if (material.values !== undefined) value = material.values[pname];
if (value !== undefined) {
texture = dependencies.textures[value];
} else if (shaderParam.value !== undefined) {
texture = dependencies.textures[shaderParam.value];
}
}
}
if (texture) {
materialParams.map = texture;
}
} else {
materialType = DeferredShaderMaterial;
materialParams.uniforms = {};
var program = json.programs[technique.program];
if (program) {
materialParams.fragmentShader = dependencies.shaders[program.fragmentShader];
if (!materialParams.fragmentShader) {
console.warn("ERROR: Missing fragment shader definition:", program.fragmentShader);
materialType = THREE.MeshPhongMaterial;
}
var vertexShader = dependencies.shaders[program.vertexShader];
if (!vertexShader) {
console.warn("ERROR: Missing vertex shader definition:", program.vertexShader);
materialType = THREE.MeshPhongMaterial;
}
// IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS
materialParams.vertexShader = replaceTHREEShaderAttributes(vertexShader, technique);
const uniforms = technique.uniforms;
for (const uniformId in uniforms) {
const pname = uniforms[uniformId];
const shaderParam = technique.parameters[pname];
const ptype = shaderParam.type;
if (WEBGL_TYPE[ptype]) {
const pcount = shaderParam.count;
let value;
if (material.values !== undefined) value = material.values[pname];
var uvalue = new WEBGL_TYPE[ptype]();
var usemantic = shaderParam.semantic;
var unode = shaderParam.node;
switch (ptype) {
case WEBGL_CONSTANTS.FLOAT:
uvalue = shaderParam.value;
if (pname == "transparency") {
materialParams.transparent = true;
}
if (value !== undefined) {
uvalue = value;
}
break;
case WEBGL_CONSTANTS.FLOAT_VEC2:
case WEBGL_CONSTANTS.FLOAT_VEC3:
case WEBGL_CONSTANTS.FLOAT_VEC4:
case WEBGL_CONSTANTS.FLOAT_MAT3:
if (shaderParam && shaderParam.value) {
uvalue.fromArray(shaderParam.value);
}
if (value) {
uvalue.fromArray(value);
}
break;
case WEBGL_CONSTANTS.FLOAT_MAT2:
// what to do?
console.warn("FLOAT_MAT2 is not a supported uniform type");
break;
case WEBGL_CONSTANTS.FLOAT_MAT4:
if (pcount) {
uvalue = new Array(pcount);
for (var mi = 0; mi < pcount; mi++) {
uvalue[mi] = new WEBGL_TYPE[ptype]();
}
if (shaderParam && shaderParam.value) {
var m4v = shaderParam.value;
uvalue.fromArray(m4v);
}
if (value) {
uvalue.fromArray(value);
}
} else {
if (shaderParam && shaderParam.value) {
var m4 = shaderParam.value;
uvalue.fromArray(m4);
}
if (value) {
uvalue.fromArray(value);
}
}
break;
case WEBGL_CONSTANTS.SAMPLER_2D:
if (value !== undefined) {
uvalue = dependencies.textures[value];
} else if (shaderParam.value !== undefined) {
uvalue = dependencies.textures[shaderParam.value];
} else {
uvalue = null;
}
break;
}
materialParams.uniforms[uniformId] = {
value: uvalue,
semantic: usemantic,
node: unode
};
} else {
throw new Error("Unknown shader uniform param type: " + ptype);
}
}
var states = technique.states || {};
var enables = states.enable || [];
var functions = states.functions || {};
var enableCullFace = false;
var enableDepthTest = false;
var enableBlend = false;
for (var i = 0, il = enables.length; i < il; i++) {
var enable = enables[i];
switch (STATES_ENABLES[enable]) {
case 'CULL_FACE':
enableCullFace = true;
break;
case 'DEPTH_TEST':
enableDepthTest = true;
break;
case 'BLEND':
enableBlend = true;
break;
// TODO: implement
case 'SCISSOR_TEST':
case 'POLYGON_OFFSET_FILL':
case 'SAMPLE_ALPHA_TO_COVERAGE':
break;
default:
throw new Error("Unknown technique.states.enable: " + enable);
}
}
if (enableCullFace) {
materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[functions.cullFace] : THREE.FrontSide;
} else {
materialParams.side = THREE.DoubleSide;
}
materialParams.depthTest = enableDepthTest;
materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[functions.depthFunc] : THREE.LessDepth;
materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[0] : true;
materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending;
materialParams.transparent = enableBlend;
var blendEquationSeparate = functions.blendEquationSeparate;
if (blendEquationSeparate !== undefined) {
materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[blendEquationSeparate[0]];
materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[blendEquationSeparate[1]];
} else {
materialParams.blendEquation = THREE.AddEquation;
materialParams.blendEquationAlpha = THREE.AddEquation;
}
var blendFuncSeparate = functions.blendFuncSeparate;
if (blendFuncSeparate !== undefined) {
materialParams.blendSrc = WEBGL_BLEND_FUNCS[blendFuncSeparate[0]];
materialParams.blendDst = WEBGL_BLEND_FUNCS[blendFuncSeparate[1]];
materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[blendFuncSeparate[2]];
materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[blendFuncSeparate[3]];
} else {
materialParams.blendSrc = THREE.OneFactor;
materialParams.blendDst = THREE.ZeroFactor;
materialParams.blendSrcAlpha = THREE.OneFactor;
materialParams.blendDstAlpha = THREE.ZeroFactor;
}
}
}
}
if (Array.isArray(materialValues.diffuse)) {
materialParams.color = new THREE.Color().fromArray(materialValues.diffuse);
} else if (typeof materialValues.diffuse === 'string') {
materialParams.map = dependencies.textures[materialValues.diffuse];
}
delete materialParams.diffuse;
if (typeof materialValues.reflective === 'string') {
materialParams.envMap = dependencies.textures[materialValues.reflective];
}
if (typeof materialValues.bump === 'string') {
materialParams.bumpMap = dependencies.textures[materialValues.bump];
}
if (Array.isArray(materialValues.emission)) {
if (materialType === THREE.MeshBasicMaterial) {
materialParams.color = new THREE.Color().fromArray(materialValues.emission);
} else {
materialParams.emissive = new THREE.Color().fromArray(materialValues.emission);
}
} else if (typeof materialValues.emission === 'string') {
if (materialType === THREE.MeshBasicMaterial) {
materialParams.map = dependencies.textures[materialValues.emission];
} else {
materialParams.emissiveMap = dependencies.textures[materialValues.emission];
}
}
if (Array.isArray(materialValues.specular)) {
materialParams.specular = new THREE.Color().fromArray(materialValues.specular);
} else if (typeof materialValues.specular === 'string') {
materialParams.specularMap = dependencies.textures[materialValues.specular];
}
if (materialValues.shininess !== undefined) {
materialParams.shininess = materialValues.shininess;
}
var _material = new materialType(materialParams);
if (material.name !== undefined) _material.name = material.name;
return _material;
});
});
};
GLTFParser.prototype.loadMeshes = function () {
var json = this.json;
return this._withDependencies(["accessors", "materials"]).then(function (dependencies) {
return _each(json.meshes, function (mesh) {
var group = new THREE.Group();
if (mesh.name !== undefined) group.name = mesh.name;
if (mesh.extras) group.userData = mesh.extras;
var primitives = mesh.primitives || [];
for (var name in primitives) {
var primitive = primitives[name];
if (primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined) {
var geometry = new THREE.BufferGeometry();
var attributes = primitive.attributes;
for (var attributeId in attributes) {
var attributeEntry = attributes[attributeId];
if (!attributeEntry) return;
var bufferAttribute = dependencies.accessors[attributeEntry];
switch (attributeId) {
case 'POSITION':
geometry.setAttribute('position', bufferAttribute);
break;
case 'NORMAL':
geometry.setAttribute('normal', bufferAttribute);
break;
case 'TEXCOORD_0':
case 'TEXCOORD0':
case 'TEXCOORD':
geometry.setAttribute('uv', bufferAttribute);
break;
case 'TEXCOORD_1':
geometry.setAttribute('uv2', bufferAttribute);
break;
case 'COLOR_0':
case 'COLOR0':
case 'COLOR':
geometry.setAttribute('color', bufferAttribute);
break;
case 'WEIGHT':
geometry.setAttribute('skinWeight', bufferAttribute);
break;
case 'JOINT':
geometry.setAttribute('skinIndex', bufferAttribute);
break;
default:
if (!primitive.material) break;
var material = json.materials[primitive.material];
if (!material.technique) break;
var parameters = json.techniques[material.technique].parameters || {};
for (var attributeName in parameters) {
if (parameters[attributeName]['semantic'] === attributeId) {
geometry.setAttribute(attributeName, bufferAttribute);
}
}
}
}
if (primitive.indices) {
geometry.setIndex(dependencies.accessors[primitive.indices]);
}
var material = dependencies.materials !== undefined ? dependencies.materials[primitive.material] : createDefaultMaterial();
var meshNode = new THREE.Mesh(geometry, material);
meshNode.castShadow = true;
meshNode.name = name === "0" ? group.name : group.name + name;
if (primitive.extras) meshNode.userData = primitive.extras;
group.add(meshNode);
} else if (primitive.mode === WEBGL_CONSTANTS.LINES) {
var geometry = new THREE.BufferGeometry();
var attributes = primitive.attributes;
for (var attributeId in attributes) {
var attributeEntry = attributes[attributeId];
if (!attributeEntry) return;
var bufferAttribute = dependencies.accessors[attributeEntry];
switch (attributeId) {
case 'POSITION':
geometry.setAttribute('position', bufferAttribute);
break;
case 'COLOR_0':
case 'COLOR0':
case 'COLOR':
geometry.setAttribute('color', bufferAttribute);
break;
}
}
var material = dependencies.materials[primitive.material];
var meshNode;
if (primitive.indices) {
geometry.setIndex(dependencies.accessors[primitive.indices]);
meshNode = new THREE.LineSegments(geometry, material);
} else {
meshNode = new THREE.Line(geometry, material);
}
meshNode.name = name === "0" ? group.name : group.name + name;
if (primitive.extras) meshNode.userData = primitive.extras;
group.add(meshNode);
} else {
console.warn("Only triangular and line primitives are supported");
}
}
return group;
});
});
};
GLTFParser.prototype.loadCameras = function () {
var json = this.json;
return _each(json.cameras, function (camera) {
if (camera.type == "perspective" && camera.perspective) {
var yfov = camera.perspective.yfov;
var aspectRatio = camera.perspective.aspectRatio !== undefined ? camera.perspective.aspectRatio : 1;
// According to COLLADA spec...
// aspectRatio = xfov / yfov
var _camera = new THREE.PerspectiveCamera(THREE.MathUtils.radToDeg(yfov * aspectRatio), aspectRatio, camera.perspective.znear || 1, camera.perspective.zfar || 2e6);
if (camera.name !== undefined) _camera.name = camera.name;
if (camera.extras) _camera.userData = camera.extras;
return _camera;
} else if (camera.type == "orthographic" && camera.orthographic) {
var _camera = new THREE.OrthographicCamera(window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, camera.orthographic.znear, camera.orthographic.zfar);
if (camera.name !== undefined) _camera.name = camera.name;
if (camera.extras) _camera.userData = camera.extras;
return _camera;
}
});
};
GLTFParser.prototype.loadSkins = function () {
var json = this.json;
return this._withDependencies(["accessors"]).then(function (dependencies) {
return _each(json.skins, function (skin) {
var bindShapeMatrix = new THREE.Matrix4();
if (skin.bindShapeMatrix !== undefined) bindShapeMatrix.fromArray(skin.bindShapeMatrix);
var _skin = {
bindShapeMatrix: bindShapeMatrix,
jointNames: skin.jointNames,
inverseBindMatrices: dependencies.accessors[skin.inverseBindMatrices]
};
return _skin;
});
});
};
GLTFParser.prototype.loadAnimations = function () {
var json = this.json;
return this._withDependencies(["accessors", "nodes"]).then(function (dependencies) {
return _each(json.animations, function (animation, animationId) {
var tracks = [];
for (var channelId in animation.channels) {
var channel = animation.channels[channelId];
var sampler = animation.samplers[channel.sampler];
if (sampler) {
var target = channel.target;
var name = target.id;
var input = animation.parameters !== undefined ? animation.parameters[sampler.input] : sampler.input;
var output = animation.parameters !== undefined ? animation.parameters[sampler.output] : sampler.output;
var inputAccessor = dependencies.accessors[input];
var outputAccessor = dependencies.accessors[output];
var node = dependencies.nodes[name];
if (node) {
node.updateMatrix();
node.matrixAutoUpdate = true;
var TypedKeyframeTrack = PATH_PROPERTIES[target.path] === PATH_PROPERTIES.rotation ? THREE.QuaternionKeyframeTrack : THREE.VectorKeyframeTrack;
var targetName = node.name ? node.name : node.uuid;
var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear;
// KeyframeTrack.optimize() will modify given 'times' and 'values'
// buffers before creating a truncated copy to keep. Because buffers may
// be reused by other tracks, make copies here.
tracks.push(new TypedKeyframeTrack(targetName + '.' + PATH_PROPERTIES[target.path], inputAccessor.array.slice(), outputAccessor.array.slice(), interpolation));
}
}
}
var name = animation.name !== undefined ? animation.name : "animation_" + animationId;
return new THREE.AnimationClip(name, undefined, tracks);
});
});
};
GLTFParser.prototype.loadNodes = function () {
var json = this.json;
var extensions = this.extensions;
var scope = this;
return _each(json.nodes, function (node) {
var matrix = new THREE.Matrix4();
var _node;
if (node.jointName) {
_node = new THREE.Bone();
_node.name = node.name !== undefined ? node.name : node.jointName;
_node.jointName = node.jointName;
} else {
_node = new THREE.Object3D();
if (node.name !== undefined) _node.name = node.name;
}
if (node.extras) _node.userData = node.extras;
if (node.matrix !== undefined) {
matrix.fromArray(node.matrix);
_node.applyMatrix4(matrix);
} else {
if (node.translation !== undefined) {
_node.position.fromArray(node.translation);
}
if (node.rotation !== undefined) {
_node.quaternion.fromArray(node.rotation);
}
if (node.scale !== undefined) {
_node.scale.fromArray(node.scale);
}
}
return _node;
}).then(function (__nodes) {
return scope._withDependencies(["meshes", "skins", "cameras"]).then(function (dependencies) {
return _each(__nodes, function (_node, nodeId) {
var node = json.nodes[nodeId];
if (node.meshes !== undefined) {
for (var meshId in node.meshes) {
var mesh = node.meshes[meshId];
var group = dependencies.meshes[mesh];
if (group === undefined) {
console.warn('LegacyGLTFLoader: Couldn\'t find node "' + mesh + '".');
continue;
}
for (var childrenId in group.children) {
var child = group.children[childrenId];
// clone Mesh to add to _node
var originalMaterial = child.material;
var originalGeometry = child.geometry;
var originalUserData = child.userData;
var originalName = child.name;
var material;
if (originalMaterial.isDeferredShaderMaterial) {
originalMaterial = material = originalMaterial.create();
} else {
material = originalMaterial;
}
switch (child.type) {
case 'LineSegments':
child = new THREE.LineSegments(originalGeometry, material);
break;
case 'LineLoop':
child = new THREE.LineLoop(originalGeometry, material);
break;
case 'Line':
child = new THREE.Line(originalGeometry, material);
break;
default:
child = new THREE.Mesh(originalGeometry, material);
}
child.castShadow = true;
child.userData = originalUserData;
child.name = originalName;
var skinEntry;
if (node.skin) {
skinEntry = dependencies.skins[node.skin];
}
// R