diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 97626ba45445..bc437610c7f5 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -1,6 +1,7 @@
i
in times
such that the parameter
+ * time
is in the interval [times[i], times[i + 1]]
.
+ * @function
+ *
+ * @param {Number} time The time.
+ * @returns {Number} The index for the element at the start of the interval.
+ *
+ * @exception {DeveloperError} time must be in the range [t0, tn]
, where t0
+ * is the first element in the array times
and tn
is the last element
+ * in the array times
.
+ */
+ WeightSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;
+
+ /**
+ * Evaluates the curve at a given time.
+ *
+ * @param {Number} time The time at which to evaluate the curve.
+ * @param {Number[]} [result] The object onto which to store the result.
+ * @returns {Number[]} The modified result parameter or a new instance of the point on the curve at the given time.
+ *
+ * @exception {DeveloperError} time must be in the range [t0, tn]
, where t0
+ * is the first element in the array times
and tn
is the last element
+ * in the array times
.
+ */
+ WeightSpline.prototype.evaluate = function(time, result) {
+ var weights = this.weights;
+ var times = this.times;
+
+ var i = this._lastTimeIndex = this.findTimeInterval(time, this._lastTimeIndex);
+ var u = (time - times[i]) / (times[i + 1] - times[i]);
+
+ if (!defined(result)) {
+ result = new Array(this._count);
+ }
+
+ for (var j = 0; j < this._count; j++) {
+ var index = (i * this._count) + j;
+ result[j] = weights[index] * (1.0 - u) + weights[index + this._count] * u;
+ }
+
+ return result;
+ };
+
+ return WeightSpline;
+});
diff --git a/Source/Renderer/AutomaticUniforms.js b/Source/Renderer/AutomaticUniforms.js
index 31b313ab7f60..89dafdc31d3e 100644
--- a/Source/Renderer/AutomaticUniforms.js
+++ b/Source/Renderer/AutomaticUniforms.js
@@ -1436,6 +1436,51 @@ define([
}
}),
+ /**
+ * An automatic GLSL uniform containing the BRDF look up texture used for image-based lighting computations.
+ *
+ * @alias czm_brdfLut
+ * @glslUniform
+ *
+ * @example
+ * // GLSL declaration
+ * uniform sampler2D czm_brdfLut;
+ *
+ * // Example: For a given roughness and NdotV value, find the material's BRDF information in the red and green channels
+ * float roughness = 0.5;
+ * float NdotV = dot(normal, view);
+ * vec2 brdfLut = texture2D(czm_brdfLut, vec2(NdotV, 1.0 - roughness)).rg;
+ */
+ czm_brdfLut : new AutomaticUniform({
+ size : 1,
+ datatype : WebGLConstants.SAMPLER_2D,
+ getValue : function(uniformState) {
+ return uniformState.brdfLut;
+ }
+ }),
+
+ /**
+ * An automatic GLSL uniform containing the environment map used within the scene.
+ *
+ * @alias czm_environmentMap
+ * @glslUniform
+ *
+ * @example
+ * // GLSL declaration
+ * uniform samplerCube czm_environmentMap;
+ *
+ * // Example: Create a perfect reflection of the environment map on a model
+ * float reflected = reflect(view, normal);
+ * vec4 reflectedColor = textureCube(czm_environmentMap, reflected);
+ */
+ czm_environmentMap : new AutomaticUniform({
+ size : 1,
+ datatype : WebGLConstants.SAMPLER_CUBE,
+ getValue : function(uniformState) {
+ return uniformState.environmentMap;
+ }
+ }),
+
/**
* An automatic GLSL uniform representing a 3x3 rotation matrix that transforms
* from True Equator Mean Equinox (TEME) axes to the pseudo-fixed axes at the current scene time.
diff --git a/Source/Renderer/UniformState.js b/Source/Renderer/UniformState.js
index 55af63a0fc0b..665b7317d7ea 100644
--- a/Source/Renderer/UniformState.js
+++ b/Source/Renderer/UniformState.js
@@ -1,10 +1,12 @@
define([
+ './Sampler',
'../Core/BoundingRectangle',
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
'../Core/Cartographic',
'../Core/Color',
+ '../Core/defaultValue',
'../Core/defined',
'../Core/defineProperties',
'../Core/EncodedCartesian3',
@@ -16,12 +18,14 @@ define([
'../Core/Transforms',
'../Scene/SceneMode'
], function(
+ Sampler,
BoundingRectangle,
Cartesian2,
Cartesian3,
Cartesian4,
Cartographic,
Color,
+ defaultValue,
defined,
defineProperties,
EncodedCartesian3,
@@ -150,6 +154,9 @@ define([
this._orthographicIn3D = false;
this._backgroundColor = new Color();
+ this._brdfLut = new Sampler();
+ this._environmentMap = new Sampler();
+
this._fogDensity = undefined;
this._imagerySplitPosition = 0.0;
@@ -796,6 +803,28 @@ define([
}
},
+ /**
+ * The look up texture used to find the BRDF for a material
+ * @memberof UniformState.prototype
+ * @type {Sampler}
+ */
+ brdfLut : {
+ get : function() {
+ return this._brdfLut;
+ }
+ },
+
+ /**
+ * The environment map of the scene
+ * @memberof UniformState.prototype
+ * @type {Sampler}
+ */
+ environmentMap : {
+ get : function() {
+ return this._environmentMap;
+ }
+ },
+
/**
* @memberof UniformState.prototype
* @type {Number}
@@ -975,6 +1004,12 @@ define([
setSunAndMoonDirections(this, frameState);
+ var brdfLutGenerator = frameState.brdfLutGenerator;
+ var brdfLut = defined(brdfLutGenerator) ? brdfLutGenerator.colorTexture : undefined;
+ this._brdfLut = brdfLut;
+
+ this._environmentMap = defaultValue(frameState.environmentMap, frameState.context.defaultCubeMap);
+
this._fogDensity = frameState.fog.density;
this._frameState = frameState;
diff --git a/Source/Scene/Batched3DModel3DTileContent.js b/Source/Scene/Batched3DModel3DTileContent.js
index 0bcd5f7e3181..7db1c61386cb 100644
--- a/Source/Scene/Batched3DModel3DTileContent.js
+++ b/Source/Scene/Batched3DModel3DTileContent.js
@@ -282,7 +282,7 @@ define([
batchTableBinaryByteLength = 0;
featureTableJsonByteLength = 0;
featureTableBinaryByteLength = 0;
- deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.');
+ Batched3DModel3DTileContent._deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.');
} else if (batchTableBinaryByteLength >= 570425344) {
// Second legacy check
byteOffset -= sizeOfUint32;
@@ -291,7 +291,7 @@ define([
batchTableBinaryByteLength = featureTableBinaryByteLength;
featureTableJsonByteLength = 0;
featureTableBinaryByteLength = 0;
- deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.');
+ Batched3DModel3DTileContent._deprecationWarning('b3dm-legacy-header', 'This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md.');
}
var featureTableJson;
@@ -341,7 +341,15 @@ define([
if (gltfByteLength === 0) {
throw new RuntimeError('glTF byte length must be greater than 0.');
}
- var gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
+
+ var gltfView;
+ if (byteOffset % 4 === 0) {
+ gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
+ } else {
+ // Create a copy of the glb so that it is 4-byte aligned
+ Batched3DModel3DTileContent._deprecationWarning('b3dm-glb-unaligned', 'The embedded glb is not aligned to a 4-byte boundary.');
+ gltfView = new Uint8Array(uint8Array.subarray(byteOffset, byteOffset + gltfByteLength));
+ }
var pickObject = {
content : content,
diff --git a/Source/Scene/BrdfLutGenerator.js b/Source/Scene/BrdfLutGenerator.js
new file mode 100644
index 000000000000..04ca2f7eb001
--- /dev/null
+++ b/Source/Scene/BrdfLutGenerator.js
@@ -0,0 +1,117 @@
+define([
+ '../Core/BoundingRectangle',
+ '../Core/Color',
+ '../Core/defined',
+ '../Core/defineProperties',
+ '../Core/destroyObject',
+ '../Core/PixelFormat',
+ '../Renderer/Framebuffer',
+ '../Renderer/Pass',
+ '../Renderer/PixelDatatype',
+ '../Renderer/RenderState',
+ '../Renderer/Sampler',
+ '../Renderer/ShaderSource',
+ '../Renderer/Texture',
+ '../Renderer/TextureMagnificationFilter',
+ '../Renderer/TextureMinificationFilter',
+ '../Renderer/TextureWrap',
+ '../Shaders/BrdfLutGeneratorFS'
+ ], function(
+ BoundingRectangle,
+ Color,
+ defined,
+ defineProperties,
+ destroyObject,
+ PixelFormat,
+ Framebuffer,
+ Pass,
+ PixelDatatype,
+ RenderState,
+ Sampler,
+ ShaderSource,
+ Texture,
+ TextureMagnificationFilter,
+ TextureMinificationFilter,
+ TextureWrap,
+ BrdfLutGeneratorFS) {
+ 'use strict';
+
+ /**
+ * @private
+ */
+ function BrdfLutGenerator() {
+ this._framebuffer = undefined;
+ this._colorTexture = undefined;
+ this._drawCommand = undefined;
+ }
+
+ defineProperties(BrdfLutGenerator.prototype, {
+ colorTexture : {
+ get : function() {
+ return this._colorTexture;
+ }
+ }
+ });
+
+ function createCommand(generator, context) {
+ var framebuffer = generator._framebuffer;
+
+ var drawCommand = context.createViewportQuadCommand(BrdfLutGeneratorFS, {
+ framebuffer : framebuffer,
+ renderState : RenderState.fromCache({
+ viewport : new BoundingRectangle(0.0, 0.0, 256.0, 256.0)
+ })
+ });
+
+ generator._drawCommand = drawCommand;
+ }
+
+ function createFramebuffer(generator, context) {
+ var colorTexture = new Texture({
+ context : context,
+ width : 256,
+ height: 256,
+ pixelFormat : PixelFormat.RGBA,
+ pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
+ sampler : new Sampler({
+ wrapS : TextureWrap.CLAMP_TO_EDGE,
+ wrapT : TextureWrap.CLAMP_TO_EDGE,
+ minificationFilter : TextureMinificationFilter.NEAREST,
+ magnificationFilter : TextureMagnificationFilter.NEAREST
+ })
+ });
+
+ generator._colorTexture = colorTexture;
+
+ var framebuffer = new Framebuffer({
+ context : context,
+ colorTextures : [colorTexture],
+ destroyAttachments : false
+ });
+
+ generator._framebuffer = framebuffer;
+ }
+
+ BrdfLutGenerator.prototype.update = function(frameState) {
+ if (!defined(this._colorTexture)) {
+ var context = frameState.context;
+
+ createFramebuffer(this, context);
+ createCommand(this, context);
+ this._drawCommand.execute(context);
+ this._framebuffer = this._framebuffer && this._framebuffer.destroy();
+ this._drawCommand.shaderProgram = this._drawCommand.shaderProgram && this._drawCommand.shaderProgram.destroy();
+ }
+ };
+
+ BrdfLutGenerator.prototype.isDestroyed = function() {
+ return false;
+ };
+
+ BrdfLutGenerator.prototype.destroy = function() {
+ this._colorTexture = this._colorTexture && this._colorTexture.destroy();
+ return destroyObject(this);
+ };
+
+ return BrdfLutGenerator;
+});
diff --git a/Source/Scene/ClassificationPrimitive.js b/Source/Scene/ClassificationPrimitive.js
index 86440fcecf86..ae0d0ef2f7fb 100644
--- a/Source/Scene/ClassificationPrimitive.js
+++ b/Source/Scene/ClassificationPrimitive.js
@@ -1,4 +1,3 @@
-/*global define*/
define([
'../Core/ColorGeometryInstanceAttribute',
'../Core/defaultValue',
diff --git a/Source/Scene/FrameState.js b/Source/Scene/FrameState.js
index 2f3826452682..89e9d1343059 100644
--- a/Source/Scene/FrameState.js
+++ b/Source/Scene/FrameState.js
@@ -38,6 +38,18 @@ define([
*/
this.shadowMaps = [];
+ /**
+ * The BRDF look up texture generator used for image-based lighting for PBR models
+ * @type {BrdfLutGenerator}
+ */
+ this.brdfLutGenerator = undefined;
+
+ /**
+ * The environment map used for image-based lighting for PBR models
+ * @type {CubeMap}
+ */
+ this.environmentMap = undefined;
+
/**
* The current mode of the scene.
*
diff --git a/Source/Scene/Instanced3DModel3DTileContent.js b/Source/Scene/Instanced3DModel3DTileContent.js
index 429f51d35200..2bd40a0ae6db 100644
--- a/Source/Scene/Instanced3DModel3DTileContent.js
+++ b/Source/Scene/Instanced3DModel3DTileContent.js
@@ -6,6 +6,7 @@ define([
'../Core/defaultValue',
'../Core/defined',
'../Core/defineProperties',
+ '../Core/deprecationWarning',
'../Core/destroyObject',
'../Core/DeveloperError',
'../Core/Ellipsoid',
@@ -34,6 +35,7 @@ define([
defaultValue,
defined,
defineProperties,
+ deprecationWarning,
destroyObject,
DeveloperError,
Ellipsoid,
@@ -91,6 +93,9 @@ define([
initialize(this, arrayBuffer, byteOffset);
}
+ // This can be overridden for testing purposes
+ Instanced3DModel3DTileContent._deprecationWarning = deprecationWarning;
+
defineProperties(Instanced3DModel3DTileContent.prototype, {
/**
* @inheritdoc Cesium3DTileContent#featuresLength
@@ -292,8 +297,15 @@ define([
if (gltfByteLength === 0) {
throw new RuntimeError('glTF byte length is zero, i3dm must have a glTF to instance.');
}
- var gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
- byteOffset += gltfByteLength;
+
+ var gltfView;
+ if (byteOffset % 4 === 0) {
+ gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
+ } else {
+ // Create a copy of the glb so that it is 4-byte aligned
+ Instanced3DModel3DTileContent._deprecationWarning('i3dm-glb-unaligned', 'The embedded glb is not aligned to a 4-byte boundary.');
+ gltfView = new Uint8Array(uint8Array.subarray(byteOffset, byteOffset + gltfByteLength));
+ }
// Create model instance collection
var collectionOptions = {
diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js
index 0bd2a4d638bf..bb4ecb33b1ef 100644
--- a/Source/Scene/Model.js
+++ b/Source/Scene/Model.js
@@ -49,7 +49,16 @@ define([
'../Renderer/TextureMinificationFilter',
'../Renderer/TextureWrap',
'../Renderer/VertexArray',
- '../ThirdParty/gltfDefaults',
+ '../ThirdParty/GltfPipeline/addDefaults',
+ '../ThirdParty/GltfPipeline/addPipelineExtras',
+ '../ThirdParty/GltfPipeline/ForEach',
+ '../ThirdParty/GltfPipeline/getAccessorByteStride',
+ '../ThirdParty/GltfPipeline/numberOfComponentsForType',
+ '../ThirdParty/GltfPipeline/parseBinaryGltf',
+ '../ThirdParty/GltfPipeline/processModelMaterialsCommon',
+ '../ThirdParty/GltfPipeline/processPbrMetallicRoughness',
+ '../ThirdParty/GltfPipeline/removePipelineExtras',
+ '../ThirdParty/GltfPipeline/updateVersion',
'../ThirdParty/Uri',
'../ThirdParty/when',
'./AttributeType',
@@ -57,13 +66,11 @@ define([
'./BlendingState',
'./ColorBlendMode',
'./getAttributeOrUniformBySemantic',
- './getBinaryAccessor',
'./HeightReference',
'./JobType',
'./ModelAnimationCache',
'./ModelAnimationCollection',
'./ModelMaterial',
- './modelMaterialsCommon',
'./ModelMesh',
'./ModelNode',
'./SceneMode',
@@ -119,7 +126,16 @@ define([
TextureMinificationFilter,
TextureWrap,
VertexArray,
- gltfDefaults,
+ addDefaults,
+ addPipelineExtras,
+ ForEach,
+ getAccessorByteStride,
+ numberOfComponentsForType,
+ parseBinaryGltf,
+ processModelMaterialsCommon,
+ processPbrMetallicRoughness,
+ removePipelineExtras,
+ updateVersion,
Uri,
when,
AttributeType,
@@ -127,13 +143,11 @@ define([
BlendingState,
ColorBlendMode,
getAttributeOrUniformBySemantic,
- getBinaryAccessor,
HeightReference,
JobType,
ModelAnimationCache,
ModelAnimationCollection,
ModelMaterial,
- modelMaterialsCommon,
ModelMesh,
ModelNode,
SceneMode,
@@ -233,7 +247,6 @@ define([
function setCachedGltf(model, cachedGltf) {
model._cachedGltf = cachedGltf;
- model._animationIds = getAnimationIds(cachedGltf);
}
// glTF JSON can be big given embedded geometry, textures, and animations, so we
@@ -243,10 +256,7 @@ define([
// Note that this is a global cache, compared to renderer resources, which
// are cached per context.
function CachedGltf(options) {
- this._gltf = modelMaterialsCommon(gltfDefaults(options.gltf), {
- addBatchIdToGeneratedShaders : options.addBatchIdToGeneratedShaders
- });
- this._bgltf = options.bgltf;
+ this._gltf = options.gltf;
this.ready = options.ready;
this.modelsToLoad = [];
this.count = 0;
@@ -255,24 +265,17 @@ define([
defineProperties(CachedGltf.prototype, {
gltf : {
set : function(value) {
- this._gltf = modelMaterialsCommon(gltfDefaults(value));
+ this._gltf = value;
},
get : function() {
return this._gltf;
}
- },
-
- bgltf : {
- get : function() {
- return this._bgltf;
- }
}
});
- CachedGltf.prototype.makeReady = function(gltfJson, bgltf) {
+ CachedGltf.prototype.makeReady = function(gltfJson) {
this.gltf = gltfJson;
- this._bgltf = bgltf;
var models = this.modelsToLoad;
var length = models.length;
@@ -288,8 +291,8 @@ define([
function getAnimationIds(cachedGltf) {
var animationIds = [];
- if (defined(cachedGltf) && defined(cachedGltf.gltf)) {
- var animations = cachedGltf.gltf.animations;
+ if (defined(cachedGltf)) {
+ var animations = cachedGltf.animations;
for (var id in animations) {
if (animations.hasOwnProperty(id)) {
animationIds.push(id);
@@ -344,7 +347,6 @@ define([
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
* @param {HeightReference} [options.heightReference] Determines how the model is drawn relative to terrain.
* @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
- * @param {Boolean} [options.addBatchIdToGeneratedShaders=false] Determines if shaders generated for materials using the KHR_materials_common extension should include a batchId attribute. For models contained in b3dm tiles.
* @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
* @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color.
* @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
@@ -384,25 +386,17 @@ define([
if (gltf instanceof Uint8Array) {
// Binary glTF
- var result = parseBinaryGltfHeader(gltf);
-
- // KHR_binary_glTF is from the beginning of the binary section
- if (result.binaryOffset !== 0) {
- gltf = gltf.subarray(result.binaryOffset);
- }
+ var parsedGltf = parseBinaryGltf(gltf);
cachedGltf = new CachedGltf({
- gltf : result.glTF,
- bgltf : gltf,
- ready : true,
- addBatchIdToGeneratedShaders : options.addBatchIdToGeneratedShaders
+ gltf : parsedGltf,
+ ready : true
});
} else {
// Normal glTF (JSON)
cachedGltf = new CachedGltf({
gltf : options.gltf,
- ready : true,
- addBatchIdToGeneratedShaders : options.addBatchIdToGeneratedShaders
+ ready : true
});
}
@@ -627,6 +621,7 @@ define([
this._distanceDisplayCondition = options.distanceDisplayCondition;
// Undocumented options
+ this._addBatchIdToGeneratedShaders = options.addBatchIdToGeneratedShaders;
this._precreatedAttributes = options.precreatedAttributes;
this._vertexShaderLoaded = options.vertexShaderLoaded;
this._fragmentShaderLoaded = options.fragmentShaderLoaded;
@@ -676,7 +671,8 @@ define([
};
this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation
- this._extensionsUsed = undefined; // Cached used extensions in a hash-map so we don't have to search the gltf array
+ this._extensionsUsed = undefined; // Cached used glTF extensions
+ this._extensionsRequired = undefined; // Cached required glTF extensions
this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes
this._programPrimitives = {};
this._rendererResources = { // Cached between models with the same url/cache-key
@@ -684,13 +680,14 @@ define([
vertexArrays : {},
programs : {},
pickPrograms : {},
- silhouettePrograms: {},
+ silhouettePrograms : {},
textures : {},
samplers : {},
renderStates : {}
};
this._cachedRendererResources = undefined;
this._loadRendererResourcesFromCache = false;
+ this._updatedGltfVersion = false;
this._cachedGeometryByteLength = 0;
this._cachedTexturesByteLength = 0;
@@ -983,6 +980,24 @@ define([
}
},
+ extensionsUsed : {
+ get : function() {
+ if (!defined(this._extensionsUsed)) {
+ this._extensionsUsed = getUsedExtensions(this);
+ }
+ return this._extensionsUsed;
+ }
+ },
+
+ extensionsRequired : {
+ get : function() {
+ if (!defined(this._extensionsRequired)) {
+ this._extensionsRequired = getRequiredExtensions(this);
+ }
+ return this._extensionsRequired;
+ }
+ },
+
/**
* Gets the model's up-axis.
* By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.
@@ -1057,8 +1072,6 @@ define([
}
});
- var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
-
function silhouetteSupported(context) {
return context.stencilBuffer;
}
@@ -1086,41 +1099,6 @@ define([
return magic === 'glTF';
}
- function parseBinaryGltfHeader(uint8Array) {
- //>>includeStart('debug', pragmas.debug);
- if (!containsGltfMagic(uint8Array)) {
- throw new DeveloperError('bgltf is not a valid Binary glTF file.');
- }
- //>>includeEnd('debug');
-
- var view = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
- var byteOffset = 0;
-
- byteOffset += sizeOfUint32; // Skip magic number
-
- //>>includeStart('debug', pragmas.debug);
- var version = view.getUint32(byteOffset, true);
- if (version !== 1) {
- throw new DeveloperError('Only Binary glTF version 1 is supported. Version ' + version + ' is not.');
- }
- //>>includeEnd('debug');
- byteOffset += sizeOfUint32;
-
- byteOffset += sizeOfUint32; // Skip length
-
- var sceneLength = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32 + sizeOfUint32; // Skip sceneFormat
-
- var sceneOffset = byteOffset;
- var binOffset = sceneOffset + sceneLength;
-
- var json = getStringFromTypedArray(uint8Array, sceneOffset, sceneLength);
- return {
- glTF: JSON.parse(json),
- binaryOffset: binOffset
- };
- }
-
/**
*
* Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
@@ -1227,12 +1205,9 @@ define([
var array = new Uint8Array(arrayBuffer);
if (containsGltfMagic(array)) {
// Load binary glTF
- var result = parseBinaryGltfHeader(array);
+ var parsedGltf = parseBinaryGltf(array);
// KHR_binary_glTF is from the beginning of the binary section
- if (result.binaryOffset !== 0) {
- array = array.subarray(result.binaryOffset);
- }
- cachedGltf.makeReady(result.glTF, array);
+ cachedGltf.makeReady(parsedGltf, array);
} else {
// Load text (JSON) glTF
var json = getStringFromTypedArray(array);
@@ -1337,7 +1312,8 @@ define([
};
}
- function computeBoundingSphere(model, gltf) {
+ function computeBoundingSphere(model) {
+ var gltf = model.gltf;
var gltfNodes = gltf.nodes;
var gltfMeshes = gltf.meshes;
var rootNodes = gltf.scenes[gltf.scene].nodes;
@@ -1357,24 +1333,22 @@ define([
n = nodeStack.pop();
var transformToRoot = n._transformToRoot;
- var meshes = n.meshes;
- if (defined(meshes)) {
- var meshesLength = meshes.length;
- for (var j = 0; j < meshesLength; ++j) {
- var primitives = gltfMeshes[meshes[j]].primitives;
- var primitivesLength = primitives.length;
- for (var m = 0; m < primitivesLength; ++m) {
- var positionAccessor = primitives[m].attributes.POSITION;
- if (defined(positionAccessor)) {
- var minMax = getAccessorMinMax(gltf, positionAccessor);
- var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
- var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
- if (defined(min) && defined(max)) {
- Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
- Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
- Cartesian3.minimumByComponent(min, aMin, min);
- Cartesian3.maximumByComponent(max, aMax, max);
- }
+ var meshId = n.mesh;
+ if (defined(meshId)) {
+ var mesh = gltfMeshes[meshId];
+ var primitives = mesh.primitives;
+ var primitivesLength = primitives.length;
+ for (var m = 0; m < primitivesLength; ++m) {
+ var positionAccessor = primitives[m].attributes.POSITION;
+ if (defined(positionAccessor)) {
+ var minMax = getAccessorMinMax(gltf, positionAccessor);
+ var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
+ var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
+ if (defined(min) && defined(max)) {
+ Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
+ Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
+ Cartesian3.minimumByComponent(min, aMin, min);
+ Cartesian3.maximumByComponent(max, aMax, max);
}
}
}
@@ -1410,31 +1384,37 @@ define([
};
}
+ function addBuffersToLoadResources(model) {
+ var gltf = model.gltf;
+ var loadResources = model._loadResources;
+ ForEach.buffer(gltf, function(buffer, id) {
+ loadResources.buffers[id] = buffer.extras._pipeline.source;
+ });
+ }
+
function bufferLoad(model, id) {
return function(arrayBuffer) {
var loadResources = model._loadResources;
- loadResources.buffers[id] = new Uint8Array(arrayBuffer);
+ var buffer = new Uint8Array(arrayBuffer);
--loadResources.pendingBufferLoads;
+ model.gltf.buffers[id].extras._pipeline.source = buffer;
};
}
function parseBuffers(model) {
+ var loadResources = model._loadResources;
+ // Iterate this way for compatibility with objects and arrays
var buffers = model.gltf.buffers;
for (var id in buffers) {
if (buffers.hasOwnProperty(id)) {
var buffer = buffers[id];
-
- // The extension 'KHR_binary_glTF' uses a special buffer entitled just 'binary_glTF'.
- // The 'KHR_binary_glTF' check is for backwards compatibility for the Cesium model converter
- // circa Cesium 1.15-1.20 when the converter incorrectly used the buffer name 'KHR_binary_glTF'.
- if ((id === 'binary_glTF') || (id === 'KHR_binary_glTF')) {
- // Buffer is the binary glTF file itself that is already loaded
- var loadResources = model._loadResources;
- loadResources.buffers[id] = model._cachedGltf.bgltf;
- }
- else if (buffer.type === 'arraybuffer') {
- ++model._loadResources.pendingBufferLoads;
+ buffer.extras = defaultValue(buffer.extras, {});
+ buffer.extras._pipeline = defaultValue(buffer.extras._pipeline, {});
+ if (defined(buffer.extras._pipeline.source)) {
+ loadResources.buffers[id] = buffer.extras._pipeline.source;
+ } else {
var bufferPath = joinUrls(model._baseUri, buffer.uri);
+ ++loadResources.pendingBufferLoads;
loadArrayBuffer(bufferPath).then(bufferLoad(model, id)).otherwise(getFailedLoadFunction(model, 'buffer', bufferPath));
}
}
@@ -1443,18 +1423,15 @@ define([
function parseBufferViews(model) {
var bufferViews = model.gltf.bufferViews;
- var id;
var vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate;
// Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below.
- for (id in bufferViews) {
- if (bufferViews.hasOwnProperty(id)) {
- if (bufferViews[id].target === WebGLConstants.ARRAY_BUFFER) {
- vertexBuffersToCreate.enqueue(id);
- }
+ ForEach.bufferView(model.gltf, function(bufferView, id) {
+ if (bufferView.target === WebGLConstants.ARRAY_BUFFER) {
+ vertexBuffersToCreate.enqueue(id);
}
- }
+ });
var indexBuffersToCreate = model._loadResources.indexBuffersToCreate;
var indexBufferIds = {};
@@ -1462,25 +1439,18 @@ define([
// The Cesium Renderer requires knowing the datatype for an index buffer
// at creation type, which is not part of the glTF bufferview so loop
// through glTF accessors to create the bufferview's index buffer.
- var accessors = model.gltf.accessors;
- for (id in accessors) {
- if (accessors.hasOwnProperty(id)) {
- var accessor = accessors[id];
- var bufferViewId = accessor.bufferView;
- var bufferView = bufferViews[bufferViewId];
-
- if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(indexBufferIds[bufferViewId])) {
- indexBufferIds[bufferViewId] = true;
- indexBuffersToCreate.enqueue({
- id : bufferViewId,
- // In theory, several glTF accessors with different componentTypes could
- // point to the same glTF bufferView, which would break this.
- // In practice, it is unlikely as it will be UNSIGNED_SHORT.
- componentType : accessor.componentType
- });
- }
+ ForEach.accessor(model.gltf, function(accessor) {
+ var bufferViewId = accessor.bufferView;
+ var bufferView = bufferViews[bufferViewId];
+
+ if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(indexBufferIds[bufferViewId])) {
+ indexBufferIds[bufferViewId] = true;
+ indexBuffersToCreate.enqueue({
+ id : bufferViewId,
+ componentType : accessor.componentType
+ });
}
- }
+ });
}
function shaderLoad(model, type, id) {
@@ -1492,58 +1462,60 @@ define([
bufferView : undefined
};
--loadResources.pendingShaderLoads;
+ model.gltf.shaders[id].extras._pipeline.source = source;
};
}
function parseShaders(model) {
- var shaders = model.gltf.shaders;
- for (var id in shaders) {
- if (shaders.hasOwnProperty(id)) {
- var shader = shaders[id];
-
- // Shader references either uri (external or base64-encoded) or bufferView
- if (defined(shader.extras) && defined(shader.extras.source)) {
- model._loadResources.shaders[id] = {
- source : shader.extras.source,
- bufferView : undefined
- };
- }
- else if (defined(shader.extensions) && defined(shader.extensions.KHR_binary_glTF)) {
- var binary = shader.extensions.KHR_binary_glTF;
- model._loadResources.shaders[id] = {
- source : undefined,
- bufferView : binary.bufferView
- };
- } else {
- ++model._loadResources.pendingShaderLoads;
- var shaderPath = joinUrls(model._baseUri, shader.uri);
- loadText(shaderPath).then(shaderLoad(model, shader.type, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath));
- }
+ var gltf = model.gltf;
+ var buffers = gltf.buffers;
+ var bufferViews = gltf.bufferViews;
+ ForEach.shader(gltf, function(shader, id) {
+ // Shader references either uri (external or base64-encoded) or bufferView
+ if (defined(shader.bufferView)) {
+ var bufferViewId = shader.bufferView;
+ var bufferView = bufferViews[bufferViewId];
+ var bufferId = bufferView.buffer;
+ var buffer = buffers[bufferId];
+ var source = getStringFromTypedArray(buffer.extras._pipeline.source, bufferView.byteOffset, bufferView.byteLength);
+ model._loadResources.shaders[id] = {
+ source : source,
+ bufferView : undefined
+ };
+ shader.extras._pipeline.source = source;
+ } else if (defined(shader.extras._pipeline.source)) {
+ model._loadResources.shaders[id] = {
+ source : shader.extras._pipeline.source,
+ bufferView : undefined
+ };
+ } else {
+ ++model._loadResources.pendingShaderLoads;
+ var shaderPath = joinUrls(model._baseUri, shader.uri);
+ loadText(shaderPath).then(shaderLoad(model, shader.type, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath));
}
- }
+ });
}
function parsePrograms(model) {
- var programs = model.gltf.programs;
- for (var id in programs) {
- if (programs.hasOwnProperty(id)) {
- model._loadResources.programsToCreate.enqueue(id);
- }
- }
+ ForEach.program(model.gltf, function(program, id) {
+ model._loadResources.programsToCreate.enqueue(id);
+ });
}
- function imageLoad(model, id) {
+ function imageLoad(model, textureId, imageId) {
return function(image) {
+ var gltf = model.gltf;
var loadResources = model._loadResources;
--loadResources.pendingTextureLoads;
loadResources.texturesToCreate.enqueue({
- id : id,
+ id : textureId,
image : image,
bufferView : image.bufferView,
width : image.width,
height : image.height,
internalFormat : image.internalFormat
});
+ gltf.images[imageId].extras._pipeline.source = image;
};
}
@@ -1551,85 +1523,75 @@ define([
var crnRegex = /(^data:image\/crn)|(\.crn$)/i;
function parseTextures(model, context) {
- var images = model.gltf.images;
- var textures = model.gltf.textures;
- var binary;
+ var gltf = model.gltf;
+ var images = gltf.images;
var uri;
- for (var id in textures) {
- if (textures.hasOwnProperty(id)) {
- var gltfImage = images[textures[id].source];
- var extras = gltfImage.extras;
-
- binary = undefined;
- uri = undefined;
-
- // First check for a compressed texture
- if (defined(extras) && defined(extras.compressedImage3DTiles)) {
- var crunch = extras.compressedImage3DTiles.crunch;
- var s3tc = extras.compressedImage3DTiles.s3tc;
- var pvrtc = extras.compressedImage3DTiles.pvrtc1;
- var etc1 = extras.compressedImage3DTiles.etc1;
-
- if (context.s3tc && defined(crunch)) {
- if (defined(crunch.extensions)&& defined(crunch.extensions.KHR_binary_glTF)) {
- binary = crunch.extensions.KHR_binary_glTF;
- } else {
- uri = crunch.uri;
- }
- } else if (context.s3tc && defined(s3tc)) {
- if (defined(s3tc.extensions)&& defined(s3tc.extensions.KHR_binary_glTF)) {
- binary = s3tc.extensions.KHR_binary_glTF;
- } else {
- uri = s3tc.uri;
- }
- } else if (context.pvrtc && defined(pvrtc)) {
- if (defined(pvrtc.extensions)&& defined(pvrtc.extensions.KHR_binary_glTF)) {
- binary = pvrtc.extensions.KHR_binary_glTF;
- } else {
- uri = pvrtc.uri;
- }
- } else if (context.etc1 && defined(etc1)) {
- if (defined(etc1.extensions)&& defined(etc1.extensions.KHR_binary_glTF)) {
- binary = etc1.extensions.KHR_binary_glTF;
- } else {
- uri = etc1.uri;
- }
+ ForEach.texture(gltf, function(texture, id) {
+ var imageId = texture.source;
+ var gltfImage = images[imageId];
+ var extras = gltfImage.extras;
+
+ var bufferViewId = gltfImage.bufferView;
+ uri = gltfImage.uri;
+
+ // First check for a compressed texture
+ if (defined(extras) && defined(extras.compressedImage3DTiles)) {
+ var crunch = extras.compressedImage3DTiles.crunch;
+ var s3tc = extras.compressedImage3DTiles.s3tc;
+ var pvrtc = extras.compressedImage3DTiles.pvrtc1;
+ var etc1 = extras.compressedImage3DTiles.etc1;
+
+ if (context.s3tc && defined(crunch)) {
+ if (defined(crunch.bufferView)) {
+ bufferViewId = crunch.bufferView;
+ } else {
+ uri = crunch.uri;
}
- }
-
- // No compressed texture, so image references either uri (external or base64-encoded) or bufferView
- if (!defined(binary) && !defined(uri)) {
- if (defined(gltfImage.extensions) && defined(gltfImage.extensions.KHR_binary_glTF)) {
- binary = gltfImage.extensions.KHR_binary_glTF;
+ } else if (context.s3tc && defined(s3tc)) {
+ if (defined(s3tc.bufferView)) {
+ bufferViewId = s3tc.bufferView;
} else {
- uri = new Uri(gltfImage.uri);
+ uri = s3tc.uri;
+ }
+ } else if (context.pvrtc && defined(pvrtc)) {
+ if (defined(pvrtc.bufferView)) {
+ bufferViewId = pvrtc.bufferView;
+ } else {
+ uri = pvrtc.uri;
+ }
+ } else if (context.etc1 && defined(etc1)) {
+ if (defined(etc1.bufferView)) {
+ bufferViewId = etc1.bufferView;
+ } else {
+ uri = etc1.uri;
}
}
+ }
- // Image references either uri (external or base64-encoded) or bufferView
- if (defined(binary)) {
- model._loadResources.texturesToCreateFromBufferView.enqueue({
- id : id,
- image : undefined,
- bufferView : binary.bufferView,
- mimeType : binary.mimeType
- });
+ // Image references either uri (external or base64-encoded) or bufferView
+ if (defined(bufferViewId)) {
+ model._loadResources.texturesToCreateFromBufferView.enqueue({
+ id : id,
+ image : undefined,
+ bufferView : bufferViewId,
+ mimeType : gltfImage.mimeType
+ });
+ } else {
+ ++model._loadResources.pendingTextureLoads;
+ uri = new Uri(uri);
+ var imagePath = joinUrls(model._baseUri, uri);
+
+ var promise;
+ if (ktxRegex.test(imagePath)) {
+ promise = loadKTX(imagePath);
+ } else if (crnRegex.test(imagePath)) {
+ promise = loadCRN(imagePath);
} else {
- ++model._loadResources.pendingTextureLoads;
- var imagePath = joinUrls(model._baseUri, uri);
-
- var promise;
- if (ktxRegex.test(imagePath)) {
- promise = loadKTX(imagePath);
- } else if (crnRegex.test(imagePath)) {
- promise = loadCRN(imagePath);
- } else {
- promise = loadImage(imagePath);
- }
- promise.then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath));
+ promise = loadImage(imagePath);
}
+ promise.then(imageLoad(model, id, imageId)).otherwise(getFailedLoadFunction(model, 'image', imagePath));
}
- }
+ });
}
var nodeTranslationScratch = new Cartesian3();
@@ -1644,7 +1606,7 @@ define([
return Matrix4.fromTranslationQuaternionRotationScale(
Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch),
Quaternion.unpack(node.rotation, 0, nodeQuaternionScratch),
- Cartesian3.fromArray(node.scale, 0 , nodeScaleScratch));
+ Cartesian3.fromArray(node.scale, 0, nodeScaleScratch));
}
function parseNodes(model) {
@@ -1653,57 +1615,54 @@ define([
var skinnedNodes = [];
var skinnedNodesIds = model._loadResources.skinnedNodesIds;
- var nodes = model.gltf.nodes;
- for (var id in nodes) {
- if (nodes.hasOwnProperty(id)) {
- var node = nodes[id];
+ ForEach.node(model.gltf, function(node, id) {
+ var runtimeNode = {
+ // Animation targets
+ matrix : undefined,
+ translation : undefined,
+ rotation : undefined,
+ scale : undefined,
- var runtimeNode = {
- // Animation targets
- matrix : undefined,
- translation : undefined,
- rotation : undefined,
- scale : undefined,
+ // Per-node show inherited from parent
+ computedShow : true,
- // Per-node show inherited from parent
- computedShow : true,
+ // Computed transforms
+ transformToRoot : new Matrix4(),
+ computedMatrix : new Matrix4(),
+ dirtyNumber : 0, // The frame this node was made dirty by an animation; for graph traversal
- // Computed transforms
- transformToRoot : new Matrix4(),
- computedMatrix : new Matrix4(),
- dirtyNumber : 0, // The frame this node was made dirty by an animation; for graph traversal
+ // Rendering
+ commands : [], // empty for transform, light, and camera nodes
- // Rendering
- commands : [], // empty for transform, light, and camera nodes
+ // Skinned node
+ inverseBindMatrices : undefined, // undefined when node is not skinned
+ bindShapeMatrix : undefined, // undefined when node is not skinned or identity
+ joints : [], // empty when node is not skinned
+ computedJointMatrices : [], // empty when node is not skinned
- // Skinned node
- inverseBindMatrices : undefined, // undefined when node is not skinned
- bindShapeMatrix : undefined, // undefined when node is not skinned or identity
- joints : [], // empty when node is not skinned
- computedJointMatrices : [], // empty when node is not skinned
+ // Joint node
+ jointName : node.jointName, // undefined when node is not a joint
- // Joint node
- jointName : node.jointName, // undefined when node is not a joint
+ weights : [],
- // Graph pointers
- children : [], // empty for leaf nodes
- parents : [], // empty for root nodes
+ // Graph pointers
+ children : [], // empty for leaf nodes
+ parents : [], // empty for root nodes
- // Publicly-accessible ModelNode instance to modify animation targets
- publicNode : undefined
- };
- runtimeNode.publicNode = new ModelNode(model, node, runtimeNode, id, getTransform(node));
+ // Publicly-accessible ModelNode instance to modify animation targets
+ publicNode : undefined
+ };
+ runtimeNode.publicNode = new ModelNode(model, node, runtimeNode, id, getTransform(node));
- runtimeNodes[id] = runtimeNode;
- runtimeNodesByName[node.name] = runtimeNode;
+ runtimeNodes[id] = runtimeNode;
+ runtimeNodesByName[node.name] = runtimeNode;
- if (defined(node.skin)) {
- skinnedNodesIds.push(id);
- skinnedNodes.push(runtimeNode);
- }
+ if (defined(node.skin)) {
+ skinnedNodesIds.push(id);
+ skinnedNodes.push(runtimeNode);
}
- }
+ });
model._runtime.nodes = runtimeNodes;
model._runtime.nodesByName = runtimeNodesByName;
@@ -1713,24 +1672,21 @@ define([
function parseMaterials(model) {
var runtimeMaterialsByName = {};
var runtimeMaterialsById = {};
- var materials = model.gltf.materials;
var uniformMaps = model._uniformMaps;
- for (var id in materials) {
- if (materials.hasOwnProperty(id)) {
- // Allocated now so ModelMaterial can keep a reference to it.
- uniformMaps[id] = {
- uniformMap : undefined,
- values : undefined,
- jointMatrixUniformName : undefined
- };
+ ForEach.material(model.gltf, function(material, id) {
+ // Allocated now so ModelMaterial can keep a reference to it.
+ uniformMaps[id] = {
+ uniformMap : undefined,
+ values : undefined,
+ jointMatrixUniformName : undefined,
+ morphWeightsUniformName : undefined
+ };
- var material = materials[id];
- var modelMaterial = new ModelMaterial(model, material, id);
- runtimeMaterialsByName[material.name] = modelMaterial;
- runtimeMaterialsById[id] = modelMaterial;
- }
- }
+ var modelMaterial = new ModelMaterial(model, material, id);
+ runtimeMaterialsByName[material.name] = modelMaterial;
+ runtimeMaterialsById[id] = modelMaterial;
+ });
model._runtime.materialsByName = runtimeMaterialsByName;
model._runtime.materialsById = runtimeMaterialsById;
@@ -1739,58 +1695,56 @@ define([
function parseMeshes(model) {
var runtimeMeshesByName = {};
var runtimeMaterialsById = model._runtime.materialsById;
- var meshes = model.gltf.meshes;
- var usesQuantizedAttributes = usesExtension(model, 'WEB3D_quantized_attributes');
-
- for (var id in meshes) {
- if (meshes.hasOwnProperty(id)) {
- var mesh = meshes[id];
- runtimeMeshesByName[mesh.name] = new ModelMesh(mesh, runtimeMaterialsById, id);
- if (usesQuantizedAttributes) {
- // Cache primitives according to their program
- var primitives = mesh.primitives;
- var primitivesLength = primitives.length;
- for (var i = 0; i < primitivesLength; i++) {
- var primitive = primitives[i];
- var programId = getProgramForPrimitive(model, primitive);
- var programPrimitives = model._programPrimitives[programId];
- if (!defined(programPrimitives)) {
- programPrimitives = [];
- model._programPrimitives[programId] = programPrimitives;
- }
- programPrimitives.push(primitive);
+
+ ForEach.mesh(model.gltf, function(mesh, id) {
+ runtimeMeshesByName[mesh.name] = new ModelMesh(mesh, runtimeMaterialsById, id);
+ if (defined(model.extensionsUsed.WEB3D_quantized_attributes)) {
+ // Cache primitives according to their program
+ var primitives = mesh.primitives;
+ var primitivesLength = primitives.length;
+ for (var i = 0; i < primitivesLength; i++) {
+ var primitive = primitives[i];
+ var programId = getProgramForPrimitive(model, primitive);
+ var programPrimitives = model._programPrimitives[programId];
+ if (!defined(programPrimitives)) {
+ programPrimitives = [];
+ model._programPrimitives[programId] = programPrimitives;
}
+ programPrimitives.push(primitive);
}
}
- }
+ });
model._runtime.meshesByName = runtimeMeshesByName;
}
- function parse(model, context) {
- if (!model._loadRendererResourcesFromCache) {
- parseBuffers(model);
- parseBufferViews(model);
- parseShaders(model);
- parsePrograms(model);
- parseTextures(model, context);
+ function getUsedExtensions(model) {
+ var extensionsUsed = model.gltf.extensionsUsed;
+ var cachedExtensionsUsed = {};
+
+ if (defined(extensionsUsed)) {
+ var extensionsUsedLength = extensionsUsed.length;
+ for (var i = 0; i < extensionsUsedLength; i++) {
+ var extension = extensionsUsed[i];
+ cachedExtensionsUsed[extension] = true;
+ }
}
- parseMaterials(model);
- parseMeshes(model);
- parseNodes(model);
+ return cachedExtensionsUsed;
}
- function usesExtension(model, extension) {
- var cachedExtensionsUsed = model._extensionsUsed;
- if (!defined(cachedExtensionsUsed)) {
- var extensionsUsed = model.gltf.extensionsUsed;
- cachedExtensionsUsed = {};
- var extensionsLength = extensionsUsed.length;
- for (var i = 0; i < extensionsLength; i++) {
- cachedExtensionsUsed[extensionsUsed[i]] = true;
+ function getRequiredExtensions(model) {
+ var extensionsRequired = model.gltf.extensionsRequired;
+ var cachedExtensionsRequired = {};
+
+ if (defined(extensionsRequired)) {
+ var extensionsRequiredLength = extensionsRequired.length;
+ for (var i = 0; i < extensionsRequiredLength; i++) {
+ var extension = extensionsRequired[i];
+ cachedExtensionsRequired[extension] = true;
}
}
- return defined(cachedExtensionsUsed[extension]);
+
+ return cachedExtensionsRequired;
}
///////////////////////////////////////////////////////////////////////////
@@ -1935,18 +1889,6 @@ define([
return attributeLocations;
}
- function getShaderSource(model, shader) {
- if (defined(shader.source)) {
- return shader.source;
- }
-
- var loadResources = model._loadResources;
- var gltf = model.gltf;
- var bufferView = gltf.bufferViews[shader.bufferView];
-
- return getStringFromTypedArray(loadResources.getBuffer(bufferView));
- }
-
function replaceAllButFirstInString(string, find, replace) {
var index = string.indexOf(find);
return string.replace(new RegExp(find, 'g'), function(match, offset, all) {
@@ -2008,14 +1950,19 @@ define([
if (getProgramForPrimitive(model, primitive) === programName) {
for (var attributeSemantic in primitive.attributes) {
if (primitive.attributes.hasOwnProperty(attributeSemantic)) {
+ var attributeVarName = getAttributeVariableName(model, primitive, attributeSemantic);
+ var accessorId = primitive.attributes[attributeSemantic];
+
+ if (attributeSemantic.charAt(0) === '_') {
+ attributeSemantic = attributeSemantic.substring(1);
+ }
var decodeUniformVarName = 'gltf_u_dec_' + attributeSemantic.toLowerCase();
+
var decodeUniformVarNameScale = decodeUniformVarName + '_scale';
var decodeUniformVarNameTranslate = decodeUniformVarName + '_translate';
if (!defined(quantizedUniforms[decodeUniformVarName]) && !defined(quantizedUniforms[decodeUniformVarNameScale])) {
- var accessorId = primitive.attributes[attributeSemantic];
var quantizedAttributes = getQuantizedAttributes(model, accessorId);
if (defined(quantizedAttributes)) {
- var attributeVarName = getAttributeVariableName(model, primitive, attributeSemantic);
var decodeMatrix = quantizedAttributes.decodeMatrix;
var newMain = 'gltf_decoded_' + attributeSemantic;
var decodedAttributeVarName = attributeVarName.replace('a_', 'gltf_a_dec_');
@@ -2135,12 +2082,12 @@ define([
function createProgram(id, model, context) {
var programs = model.gltf.programs;
- var shaders = model._loadResources.shaders;
+ var shaders = model.gltf.shaders;
var program = programs[id];
var attributeLocations = createAttributeLocations(model, program.attributes);
- var vs = getShaderSource(model, shaders[program.vertexShader]);
- var fs = getShaderSource(model, shaders[program.fragmentShader]);
+ var vs = shaders[program.vertexShader].extras._pipeline.source;
+ var fs = shaders[program.fragmentShader].extras._pipeline.source;
// Add pre-created attributes to attributeLocations
var attributesLength = program.attributes.length;
@@ -2153,7 +2100,7 @@ define([
}
}
- if (usesExtension(model, 'WEB3D_quantized_attributes')) {
+ if (model.extensionsUsed.WEB3D_quantized_attributes) {
vs = modifyShaderForQuantizedAttributes(vs, id, model, context);
}
@@ -2313,6 +2260,10 @@ define([
var rendererSamplers = model._rendererResources.samplers;
var sampler = rendererSamplers[texture.sampler];
+ sampler = defaultValue(sampler, new Sampler({
+ wrapS : TextureWrap.REPEAT,
+ wrapT : TextureWrap.REPEAT
+ }));
var internalFormat = gltfTexture.internalFormat;
@@ -2323,10 +2274,10 @@ define([
(sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST) ||
(sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR));
var requiresNpot = mipmap ||
- (sampler.wrapS === TextureWrap.REPEAT) ||
- (sampler.wrapS === TextureWrap.MIRRORED_REPEAT) ||
- (sampler.wrapT === TextureWrap.REPEAT) ||
- (sampler.wrapT === TextureWrap.MIRRORED_REPEAT);
+ (sampler.wrapS === TextureWrap.REPEAT) ||
+ (sampler.wrapS === TextureWrap.MIRRORED_REPEAT) ||
+ (sampler.wrapT === TextureWrap.REPEAT) ||
+ (sampler.wrapT === TextureWrap.MIRRORED_REPEAT);
var tx;
var source = gltfTexture.image;
@@ -2416,7 +2367,7 @@ define([
var programAttributeLocations = program._attributeLocations;
// Note: WebGL shader compiler may have optimized and removed some attributes from programVertexAttributes
- for (location in programVertexAttributes){
+ for (location in programVertexAttributes) {
if (programVertexAttributes.hasOwnProperty(location)) {
var attribute = attributes[location];
index = programVertexAttributes[location].index;
@@ -2445,8 +2396,9 @@ define([
return attributeLocations;
}
- function searchForest(forest, jointName, nodes) {
+ function mapJointNames(forest, nodes) {
var length = forest.length;
+ var jointNodes = {};
for (var i = 0; i < length; ++i) {
var stack = [forest[i]]; // Push root node of tree
@@ -2454,8 +2406,8 @@ define([
var id = stack.pop();
var n = nodes[id];
- if (n.jointName === jointName) {
- return id;
+ if (defined(n)) {
+ jointNodes[id] = id;
}
var children = n.children;
@@ -2465,9 +2417,7 @@ define([
}
}
}
-
- // This should never happen; the skeleton should have a node for all joints in the skin.
- return undefined;
+ return jointNodes;
}
function createJoints(model, runtimeSkins) {
@@ -2491,17 +2441,18 @@ define([
// 2. These nodes form the root nodes of the forest to search for each joint in skin.jointNames. This search uses jointName, not the node's name.
// 3. Search for the joint name among the gltf node hierarchy instead of the runtime node hierarchy. Child links aren't set up yet for runtime nodes.
var forest = [];
- var gltfSkeletons = node.skeletons;
- var skeletonsLength = gltfSkeletons.length;
- for (var k = 0; k < skeletonsLength; ++k) {
- forest.push(gltfSkeletons[k]);
+ var skin = skins[node.skin];
+ if (defined(skin.skeleton)) {
+ forest.push(skin.skeleton);
}
- var gltfJointNames = skins[node.skin].jointNames;
+ var mappedJointNames = mapJointNames(forest, nodes);
+ var gltfJointNames = skins[node.skin].joints;
var jointNamesLength = gltfJointNames.length;
for (var i = 0; i < jointNamesLength; ++i) {
var jointName = gltfJointNames[i];
- var jointNode = runtimeNodes[searchForest(forest, jointName, nodes)];
+ var nodeId = mappedJointNames[jointName];
+ var jointNode = runtimeNodes[nodeId];
skinnedNode.joints.push(jointNode);
}
}
@@ -2521,25 +2472,21 @@ define([
var gltf = model.gltf;
var accessors = gltf.accessors;
- var skins = gltf.skins;
var runtimeSkins = {};
- for (var id in skins) {
- if (skins.hasOwnProperty(id)) {
- var skin = skins[id];
- var accessor = accessors[skin.inverseBindMatrices];
+ ForEach.skin(gltf, function(skin, id) {
+ var accessor = accessors[skin.inverseBindMatrices];
- var bindShapeMatrix;
- if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {
- bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);
- }
-
- runtimeSkins[id] = {
- inverseBindMatrices : ModelAnimationCache.getSkinInverseBindMatrices(model, accessor),
- bindShapeMatrix : bindShapeMatrix // not used when undefined
- };
+ var bindShapeMatrix;
+ if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {
+ bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);
}
- }
+
+ runtimeSkins[id] = {
+ inverseBindMatrices : ModelAnimationCache.getSkinInverseBindMatrices(model, accessor),
+ bindShapeMatrix : bindShapeMatrix // not used when undefined
+ };
+ });
createJoints(model, runtimeSkins);
}
@@ -2551,8 +2498,10 @@ define([
//if (targetPath === 'translation') {
// return;
//}
- runtimeNode[targetPath] = spline.evaluate(localAnimationTime, runtimeNode[targetPath]);
- runtimeNode.dirtyNumber = model._maxDirtyNumber;
+ if (defined(spline)) {
+ runtimeNode[targetPath] = spline.evaluate(localAnimationTime, runtimeNode[targetPath]);
+ runtimeNode.dirtyNumber = model._maxDirtyNumber;
+ }
};
}
@@ -2568,8 +2517,7 @@ define([
}
loadResources.createRuntimeAnimations = false;
- model._runtime.animations = {
- };
+ model._runtime.animations = {};
var runtimeNodes = model._runtime.nodes;
var animations = model.gltf.animations;
@@ -2579,17 +2527,8 @@ define([
if (animations.hasOwnProperty(animationId)) {
var animation = animations[animationId];
var channels = animation.channels;
- var parameters = animation.parameters;
var samplers = animation.samplers;
- var parameterValues = {};
-
- for (var name in parameters) {
- if (parameters.hasOwnProperty(name)) {
- parameterValues[name] = ModelAnimationCache.getAnimationParameterValues(model, accessors[parameters[name]]);
- }
- }
-
// Find start and stop time for the entire animation
var startTime = Number.MAX_VALUE;
var stopTime = -Number.MAX_VALUE;
@@ -2600,15 +2539,18 @@ define([
for (var i = 0; i < length; ++i) {
var channel = channels[i];
var target = channel.target;
+ var path = target.path;
var sampler = samplers[channel.sampler];
- var times = parameterValues[sampler.input];
+ var input = ModelAnimationCache.getAnimationParameterValues(model, accessors[sampler.input]);
+ var output = ModelAnimationCache.getAnimationParameterValues(model, accessors[sampler.output]);
+
+ startTime = Math.min(startTime, input[0]);
+ stopTime = Math.max(stopTime, input[input.length - 1]);
- startTime = Math.min(startTime, times[0]);
- stopTime = Math.max(stopTime, times[times.length - 1]);
+ var spline = ModelAnimationCache.getAnimationSpline(model, animationId, animation, channel.sampler, sampler, input, path, output);
- var spline = ModelAnimationCache.getAnimationSpline(model, animationId, animation, channel.sampler, sampler, parameterValues);
// GLTF_SPEC: Support more targets like materials. https://github.com/KhronosGroup/glTF/issues/142
- channelEvaluators[i] = getChannelEvaluator(model, runtimeNodes[target.id], target.path, spline);
+ channelEvaluators[i] = getChannelEvaluator(model, runtimeNodes[target.node], target.path, spline);
}
model._runtime.animations[animationId] = {
@@ -2665,15 +2607,19 @@ define([
// with an attribute that wasn't used and the asset wasn't optimized.
if (defined(attributeLocation)) {
var a = accessors[primitiveAttributes[attributeName]];
+ var normalize = false;
+ if (defined(a.normalized) && a.normalized) {
+ normalize = true;
+ }
attributes.push({
index : attributeLocation,
vertexBuffer : rendererBuffers[a.bufferView],
- componentsPerAttribute : getBinaryAccessor(a).componentsPerAttribute,
+ componentsPerAttribute : numberOfComponentsForType(a.type),
componentDatatype : a.componentType,
- normalize : false,
+ normalize : normalize,
offsetInBytes : a.byteOffset,
- strideInBytes : a.byteStride
+ strideInBytes : getAccessorByteStride(gltf, a)
});
}
}
@@ -2716,7 +2662,6 @@ define([
booleanStates[WebGLConstants.CULL_FACE] = false;
booleanStates[WebGLConstants.DEPTH_TEST] = false;
booleanStates[WebGLConstants.POLYGON_OFFSET_FILL] = false;
- booleanStates[WebGLConstants.SCISSOR_TEST] = false;
var enable = states.enable;
var length = enable.length;
@@ -2762,7 +2707,6 @@ define([
var colorMask = defaultValue(statesFunctions.colorMask, [true, true, true, true]);
var depthRange = defaultValue(statesFunctions.depthRange, [0.0, 1.0]);
var polygonOffset = defaultValue(statesFunctions.polygonOffset, [0.0, 0.0]);
- var scissor = defaultValue(statesFunctions.scissor, [0.0, 0.0, 0.0, 0.0]);
// Change the render state to use traditional alpha blending instead of premultiplied alpha blending
if (booleanStates[WebGLConstants.BLEND] && hasPremultipliedAlpha(model)) {
@@ -2786,15 +2730,6 @@ define([
factor : polygonOffset[0],
units : polygonOffset[1]
},
- scissorTest : {
- enabled : booleanStates[WebGLConstants.SCISSOR_TEST],
- rectangle : {
- x : scissor[0],
- y : scissor[1],
- width : scissor[2],
- height : scissor[3]
- }
- },
depthRange : {
near : depthRange[0],
far : depthRange[1]
@@ -2997,7 +2932,7 @@ define([
function DelayLoadedTextureUniform(value, model) {
this._value = undefined;
- this._textureId = value;
+ this._textureId = value.index;
this._model = model;
}
@@ -3181,7 +3116,8 @@ define([
for (var materialId in materials) {
if (materials.hasOwnProperty(materialId)) {
var material = materials[materialId];
- var instanceParameters = material.values;
+ var instanceParameters;
+ instanceParameters = material.values;
var technique = techniques[material.technique];
var parameters = technique.parameters;
var uniforms = technique.uniforms;
@@ -3189,10 +3125,11 @@ define([
var uniformMap = {};
var uniformValues = {};
var jointMatrixUniformName;
+ var morphWeightsUniformName;
// Uniform parameters
for (var name in uniforms) {
- if (uniforms.hasOwnProperty(name)) {
+ if (uniforms.hasOwnProperty(name) && name !== 'extras') {
var parameterName = uniforms[name];
var parameter = parameters[parameterName];
@@ -3215,11 +3152,13 @@ define([
} else if (defined(parameter.node)) {
uniformMap[name] = getUniformFunctionFromSource(parameter.node, model, parameter.semantic, context.uniformState);
} else if (defined(parameter.semantic)) {
- if (parameter.semantic !== 'JOINTMATRIX') {
+ if (parameter.semantic === 'JOINTMATRIX') {
+ jointMatrixUniformName = name;
+ } else if (parameter.semantic === 'MORPHWEIGHTS') {
+ morphWeightsUniformName = name;
+ } else {
// Map glTF semantic to Cesium automatic uniform
uniformMap[name] = gltfSemanticUniforms[parameter.semantic](context.uniformState, model);
- } else {
- jointMatrixUniformName = name;
}
} else if (defined(parameter.value)) {
// Technique value that isn't overridden by a material
@@ -3234,6 +3173,7 @@ define([
u.uniformMap = uniformMap; // uniform name -> function for the renderer
u.values = uniformValues; // material parameter name -> ModelMaterial for modifying the parameter at runtime
u.jointMatrixUniformName = jointMatrixUniformName;
+ u.morphWeightsUniformName = morphWeightsUniformName;
}
}
}
@@ -3263,6 +3203,10 @@ define([
var a = accessors[accessorId];
var extensions = a.extensions;
+ if (attribute.charAt(0) === '_') {
+ attribute = attribute.substring(1);
+ }
+
if (defined(extensions)) {
var quantizedAttributes = extensions.WEB3D_quantized_attributes;
if (defined(quantizedAttributes)) {
@@ -3334,6 +3278,12 @@ define([
};
}
+ function createMorphWeightsFunction(runtimeNode) {
+ return function() {
+ return runtimeNode.weights;
+ };
+ }
+
function createSilhouetteColorFunction(model) {
return function() {
return model.silhouetteColor;
@@ -3389,93 +3339,137 @@ define([
var techniques = gltf.techniques;
var materials = gltf.materials;
- var meshes = gltfNode.meshes;
- var meshesLength = meshes.length;
+ var id = gltfNode.mesh;
+ var mesh = gltfMeshes[id];
- for (var j = 0; j < meshesLength; ++j) {
- var id = meshes[j];
- var mesh = gltfMeshes[id];
- var primitives = mesh.primitives;
- var length = primitives.length;
+ var primitives = mesh.primitives;
+ var length = primitives.length;
- // The glTF node hierarchy is a DAG so a node can have more than one
- // parent, so a node may already have commands. If so, append more
- // since they will have a different model matrix.
+ // The glTF node hierarchy is a DAG so a node can have more than one
+ // parent, so a node may already have commands. If so, append more
+ // since they will have a different model matrix.
- for (var i = 0; i < length; ++i) {
- var primitive = primitives[i];
- var ix = accessors[primitive.indices];
- var material = materials[primitive.material];
- var technique = techniques[material.technique];
- var programId = technique.program;
+ for (var i = 0; i < length; ++i) {
+ var primitive = primitives[i];
+ var ix = accessors[primitive.indices];
+ var material = materials[primitive.material];
+ var technique = techniques[material.technique];
+ var programId = technique.program;
+
+ var boundingSphere;
+ var positionAccessor = primitive.attributes.POSITION;
+ if (defined(positionAccessor)) {
+ var minMax = getAccessorMinMax(gltf, positionAccessor);
+ boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max));
+ }
- var boundingSphere;
- var positionAccessor = primitive.attributes.POSITION;
- if (defined(positionAccessor)) {
- var minMax = getAccessorMinMax(gltf, positionAccessor);
- boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max));
- }
+ var vertexArray = rendererVertexArrays[id + '.primitive.' + i];
+ var offset;
+ var count;
+ if (defined(ix)) {
+ count = ix.count;
+ offset = (ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType)); // glTF has offset in bytes. Cesium has offsets in indices
+ }
+ else {
+ var positions = accessors[primitive.attributes.POSITION];
+ count = positions.count;
+ offset = 0;
+ }
- var vertexArray = rendererVertexArrays[id + '.primitive.' + i];
- var offset;
- var count;
- if (defined(ix)) {
- count = ix.count;
- offset = (ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType)); // glTF has offset in bytes. Cesium has offsets in indices
- }
- else {
- var positions = accessors[primitive.attributes.POSITION];
- count = positions.count;
- offset = 0;
- }
+ // Update model triangle count using number of indices
+ model._trianglesLength += triangleCountFromPrimitiveIndices(primitive, count);
- // Update model triangle count using number of indices
- model._trianglesLength += triangleCountFromPrimitiveIndices(primitive, count);
+ var um = uniformMaps[primitive.material];
+ var uniformMap = um.uniformMap;
+ if (defined(um.jointMatrixUniformName)) {
+ var jointUniformMap = {};
+ jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(runtimeNode);
- var um = uniformMaps[primitive.material];
- var uniformMap = um.uniformMap;
- if (defined(um.jointMatrixUniformName)) {
- var jointUniformMap = {};
- jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(runtimeNode);
+ uniformMap = combine(uniformMap, jointUniformMap);
+ }
+ if (defined(um.morphWeightsUniformName)) {
+ var morphWeightsUniformMap = {};
+ morphWeightsUniformMap[um.morphWeightsUniformName] = createMorphWeightsFunction(runtimeNode);
- uniformMap = combine(uniformMap, jointUniformMap);
- }
+ uniformMap = combine(uniformMap, morphWeightsUniformMap);
+ }
- uniformMap = combine(uniformMap, {
- gltf_color : createColorFunction(model),
- gltf_colorBlend : createColorBlendFunction(model)
- });
+ uniformMap = combine(uniformMap, {
+ gltf_color : createColorFunction(model),
+ gltf_colorBlend : createColorBlendFunction(model)
+ });
- // Allow callback to modify the uniformMap
- if (defined(model._uniformMapLoaded)) {
- uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode);
- }
+ // Allow callback to modify the uniformMap
+ if (defined(model._uniformMapLoaded)) {
+ uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode);
+ }
- // Add uniforms for decoding quantized attributes if used
- if (usesExtension(model, 'WEB3D_quantized_attributes')) {
- var quantizedUniformMap = createUniformsForQuantizedAttributes(model, primitive, context);
- uniformMap = combine(uniformMap, quantizedUniformMap);
- }
+ // Add uniforms for decoding quantized attributes if used
+ if (model.extensionsUsed.WEB3D_quantized_attributes) {
+ var quantizedUniformMap = createUniformsForQuantizedAttributes(model, primitive, context);
+ uniformMap = combine(uniformMap, quantizedUniformMap);
+ }
+
+ var rs = rendererRenderStates[material.technique];
- var rs = rendererRenderStates[material.technique];
+ // GLTF_SPEC: Offical means to determine translucency. https://github.com/KhronosGroup/glTF/issues/105
+ var isTranslucent = rs.blending.enabled;
- // GLTF_SPEC: Offical means to determine translucency. https://github.com/KhronosGroup/glTF/issues/105
- var isTranslucent = rs.blending.enabled;
+ var owner = model._pickObject;
+ if (!defined(owner)) {
+ owner = {
+ primitive : model,
+ id : model.id,
+ node : runtimeNode.publicNode,
+ mesh : runtimeMeshesByName[mesh.name]
+ };
+ }
+
+ var castShadows = ShadowMode.castShadows(model._shadows);
+ var receiveShadows = ShadowMode.receiveShadows(model._shadows);
+
+ var command = new DrawCommand({
+ boundingVolume : new BoundingSphere(), // updated in update()
+ cull : model.cull,
+ modelMatrix : new Matrix4(), // computed in update()
+ primitiveType : primitive.mode,
+ vertexArray : vertexArray,
+ count : count,
+ offset : offset,
+ shaderProgram : rendererPrograms[technique.program],
+ castShadows : castShadows,
+ receiveShadows : receiveShadows,
+ uniformMap : uniformMap,
+ renderState : rs,
+ owner : owner,
+ pass : isTranslucent ? Pass.TRANSLUCENT : model.opaquePass
+ });
- var owner = model._pickObject;
- if (!defined(owner)) {
- owner = {
- primitive : model,
- id : model.id,
- node : runtimeNode.publicNode,
- mesh : runtimeMeshesByName[mesh.name]
+ var pickCommand;
+
+ if (allowPicking) {
+ var pickUniformMap;
+
+ // Callback to override default model picking
+ if (defined(model._pickFragmentShaderLoaded)) {
+ if (defined(model._pickUniformMapLoaded)) {
+ pickUniformMap = model._pickUniformMapLoaded(uniformMap);
+ } else {
+ // This is unlikely, but could happen if the override shader does not
+ // need new uniforms since, for example, its pick ids are coming from
+ // a vertex attribute or are baked into the shader source.
+ pickUniformMap = combine(uniformMap);
+ }
+ } else {
+ var pickId = context.createPickId(owner);
+ pickIds.push(pickId);
+ var pickUniforms = {
+ czm_pickColor : createPickColorFunction(pickId.color)
};
+ pickUniformMap = combine(uniformMap, pickUniforms);
}
- var castShadows = ShadowMode.castShadows(model._shadows);
- var receiveShadows = ShadowMode.receiveShadows(model._shadows);
-
- var command = new DrawCommand({
+ pickCommand = new DrawCommand({
boundingVolume : new BoundingSphere(), // updated in update()
cull : model.cull,
modelMatrix : new Matrix4(), // computed in update()
@@ -3483,89 +3477,48 @@ define([
vertexArray : vertexArray,
count : count,
offset : offset,
- shaderProgram : rendererPrograms[technique.program],
- castShadows : castShadows,
- receiveShadows : receiveShadows,
- uniformMap : uniformMap,
+ shaderProgram : rendererPickPrograms[technique.program],
+ uniformMap : pickUniformMap,
renderState : rs,
owner : owner,
pass : isTranslucent ? Pass.TRANSLUCENT : model.opaquePass
});
+ }
- var pickCommand;
+ var command2D;
+ var pickCommand2D;
+ if (!scene3DOnly) {
+ command2D = DrawCommand.shallowClone(command);
+ command2D.boundingVolume = new BoundingSphere(); // updated in update()
+ command2D.modelMatrix = new Matrix4(); // updated in update()
if (allowPicking) {
- var pickUniformMap;
-
- // Callback to override default model picking
- if (defined(model._pickFragmentShaderLoaded)) {
- if (defined(model._pickUniformMapLoaded)) {
- pickUniformMap = model._pickUniformMapLoaded(uniformMap);
- } else {
- // This is unlikely, but could happen if the override shader does not
- // need new uniforms since, for example, its pick ids are coming from
- // a vertex attribute or are baked into the shader source.
- pickUniformMap = combine(uniformMap);
- }
- } else {
- var pickId = context.createPickId(owner);
- pickIds.push(pickId);
- var pickUniforms = {
- czm_pickColor : createPickColorFunction(pickId.color)
- };
- pickUniformMap = combine(uniformMap, pickUniforms);
- }
-
- pickCommand = new DrawCommand({
- boundingVolume : new BoundingSphere(), // updated in update()
- cull : model.cull,
- modelMatrix : new Matrix4(), // computed in update()
- primitiveType : primitive.mode,
- vertexArray : vertexArray,
- count : count,
- offset : offset,
- shaderProgram : rendererPickPrograms[technique.program],
- uniformMap : pickUniformMap,
- renderState : rs,
- owner : owner,
- pass : isTranslucent ? Pass.TRANSLUCENT : model.opaquePass
- });
- }
-
- var command2D;
- var pickCommand2D;
- if (!scene3DOnly) {
- command2D = DrawCommand.shallowClone(command);
- command2D.boundingVolume = new BoundingSphere(); // updated in update()
- command2D.modelMatrix = new Matrix4(); // updated in update()
-
- if (allowPicking) {
- pickCommand2D = DrawCommand.shallowClone(pickCommand);
- pickCommand2D.boundingVolume = new BoundingSphere(); // updated in update()
- pickCommand2D.modelMatrix = new Matrix4(); // updated in update()
- }
+ pickCommand2D = DrawCommand.shallowClone(pickCommand);
+ pickCommand2D.boundingVolume = new BoundingSphere(); // updated in update()
+ pickCommand2D.modelMatrix = new Matrix4(); // updated in update()
}
-
- var nodeCommand = {
- show : true,
- boundingSphere : boundingSphere,
- command : command,
- pickCommand : pickCommand,
- command2D : command2D,
- pickCommand2D : pickCommand2D,
- // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0
- silhouetteModelCommand : undefined,
- silhouetteModelCommand2D : undefined,
- silhouetteColorCommand : undefined,
- silhouetteColorCommand2D : undefined,
- // Generated on demand when color alpha is less than 1.0
- translucentCommand : undefined,
- translucentCommand2D : undefined
- };
- runtimeNode.commands.push(nodeCommand);
- nodeCommands.push(nodeCommand);
}
+
+ var nodeCommand = {
+ show : true,
+ boundingSphere : boundingSphere,
+ command : command,
+ pickCommand : pickCommand,
+ command2D : command2D,
+ pickCommand2D : pickCommand2D,
+ // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0
+ silhouetteModelCommand : undefined,
+ silhouetteModelCommand2D : undefined,
+ silhouetteColorCommand : undefined,
+ silhouetteColorCommand2D : undefined,
+ // Generated on demand when color alpha is less than 1.0
+ translucentCommand : undefined,
+ translucentCommand2D : undefined
+ };
+ runtimeNode.commands.push(nodeCommand);
+ nodeCommands.push(nodeCommand);
}
+
}
function createRuntimeNodes(model, context, scene3DOnly) {
@@ -3585,12 +3538,14 @@ define([
var gltf = model.gltf;
var nodes = gltf.nodes;
+ var skins = gltf.skins;
var scene = gltf.scenes[gltf.scene];
var sceneNodes = scene.nodes;
var length = sceneNodes.length;
var stack = [];
+ var seen = {};
for (var i = 0; i < length; ++i) {
stack.push({
@@ -3599,8 +3554,10 @@ define([
id : sceneNodes[i]
});
+ var skeletonIds = [];
while (stack.length > 0) {
var n = stack.pop();
+ seen[n.id] = true;
var parentRuntimeNode = n.parentRuntimeNode;
var gltfNode = n.gltfNode;
@@ -3625,18 +3582,39 @@ define([
rootNodes.push(runtimeNode);
}
- if (defined(gltfNode.meshes)) {
+ if (defined(gltfNode.mesh)) {
createCommand(model, gltfNode, runtimeNode, context, scene3DOnly);
}
var children = gltfNode.children;
var childrenLength = children.length;
- for (var k = 0; k < childrenLength; ++k) {
- stack.push({
- parentRuntimeNode : runtimeNode,
- gltfNode : nodes[children[k]],
- id : children[k]
- });
+ for (var j = 0; j < childrenLength; j++) {
+ var childId = children[j];
+ if (!seen[childId]) {
+ stack.push({
+ parentRuntimeNode : runtimeNode,
+ gltfNode : nodes[childId],
+ id : children[j]
+ });
+ }
+ }
+
+ var skin = gltfNode.skin;
+ if (defined(skin)) {
+ skeletonIds.push(skins[skin].skeleton);
+ }
+
+ if (stack.length === 0) {
+ for (var k = 0; k < skeletonIds.length; k++) {
+ var skeleton = skeletonIds[k];
+ if (!seen[skeleton]) {
+ stack.push({
+ parentRuntimeNode : undefined,
+ gltfNode : nodes[skeleton],
+ id : skeleton
+ });
+ }
+ }
}
}
}
@@ -3669,6 +3647,7 @@ define([
var context = frameState.context;
var scene3DOnly = frameState.scene3DOnly;
+ checkSupportedGlExtensions(model, context);
if (model._loadRendererResourcesFromCache) {
var resources = model._rendererResources;
var cachedResources = model._cachedRendererResources;
@@ -3777,7 +3756,7 @@ define([
var commandsLength = commands.length;
if (commandsLength > 0) {
// Node has meshes, which has primitives. Update their commands.
- for (var j = 0 ; j < commandsLength; ++j) {
+ for (var j = 0; j < commandsLength; ++j) {
var primitiveCommand = commands[j];
var command = primitiveCommand.command;
Matrix4.clone(nodeMatrix, command.modelMatrix);
@@ -3897,7 +3876,7 @@ define([
var nodeCommands = n.commands;
var nodeCommandsLength = nodeCommands.length;
- for (var j = 0 ; j < nodeCommandsLength; ++j) {
+ for (var j = 0; j < nodeCommandsLength; ++j) {
nodeCommands[j].show = show;
}
// if commandsLength is zero, the node has a light or camera
@@ -4291,20 +4270,35 @@ define([
}
function checkSupportedExtensions(model) {
- var extensionsUsed = model.gltf.extensionsUsed;
- if (defined(extensionsUsed)) {
- var extensionsUsedCount = extensionsUsed.length;
- for (var index = 0; index < extensionsUsedCount; ++index) {
- var extension = extensionsUsed[index];
-
- if (extension !== 'CESIUM_RTC' && extension !== 'KHR_binary_glTF' &&
- extension !== 'KHR_materials_common' && extension !== 'WEB3D_quantized_attributes') {
+ var extensionsRequired = model.extensionsRequired;
+ for (var extension in extensionsRequired) {
+ if (extensionsRequired.hasOwnProperty(extension)) {
+ if (extension !== 'CESIUM_RTC' &&
+ extension !== 'KHR_technique_webgl' &&
+ extension !== 'KHR_binary_glTF' &&
+ extension !== 'KHR_materials_common' &&
+ extension !== 'WEB3D_quantized_attributes') {
throw new RuntimeError('Unsupported glTF Extension: ' + extension);
}
}
}
}
+ function checkSupportedGlExtensions(model, context) {
+ var glExtensionsUsed = model.gltf.glExtensionsUsed;
+ if (defined(glExtensionsUsed)) {
+ var glExtensionsUsedLength = glExtensionsUsed.length;
+ for (var i = 0; i < glExtensionsUsedLength; i++) {
+ var extension = glExtensionsUsed[i];
+ if (extension !== 'OES_element_index_uint') {
+ throw new RuntimeError('Unsupported WebGL Extension: ' + extension);
+ } else if (!context.elementIndexUint) {
+ throw new RuntimeError('OES_element_index_uint WebGL extension is not enabled.');
+ }
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
function CachedRendererResources(context, cacheKey) {
@@ -4356,7 +4350,7 @@ define([
///////////////////////////////////////////////////////////////////////////
function getUpdateHeightCallback(model, ellipsoid, cartoPosition) {
- return function (clampedPosition) {
+ return function(clampedPosition) {
if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {
var clampedCart = ellipsoid.cartesianToCartographic(clampedPosition, scratchCartographic);
clampedCart.height += cartoPosition.height;
@@ -4502,11 +4496,6 @@ define([
}
this._state = ModelState.LOADING;
-
- this._boundingSphere = computeBoundingSphere(this, this.gltf);
- this._initialRadius = this._boundingSphere.radius;
-
- checkSupportedExtensions(this);
if (this._state !== ModelState.FAILED) {
var extensions = this.gltf.extensions;
if (defined(extensions) && defined(extensions.CESIUM_RTC)) {
@@ -4527,7 +4516,10 @@ define([
}
this._loadResources = new LoadResources();
- parse(this, context);
+ if (!this._loadRendererResourcesFromCache) {
+ // Buffers are required to updateVersion
+ parseBuffers(this);
+ }
}
}
@@ -4536,13 +4528,45 @@ define([
var justLoaded = false;
if (this._state === ModelState.LOADING) {
- // Create WebGL resources as buffers/shaders/textures are downloaded
- createResources(this, frameState);
-
// Transition from LOADING -> LOADED once resources are downloaded and created.
// Textures may continue to stream in while in the LOADED state.
+ if (loadResources.pendingBufferLoads === 0) {
+ if (!this._updatedGltfVersion) {
+ var options = {
+ optimizeForCesium: true,
+ addBatchIdToGeneratedShaders : this._addBatchIdToGeneratedShaders
+ };
+ frameState.brdfLutGenerator.update(frameState);
+ updateVersion(this.gltf);
+ checkSupportedExtensions(this);
+ addPipelineExtras(this.gltf);
+ addDefaults(this.gltf);
+ processModelMaterialsCommon(this.gltf, options);
+ processPbrMetallicRoughness(this.gltf, options);
+ // We do this after to make sure that the ids don't change
+ addBuffersToLoadResources(this);
+ this._animationIds = getAnimationIds(this.gltf);
+
+ if (!this._loadRendererResourcesFromCache) {
+ parseBufferViews(this);
+ parseShaders(this);
+ parsePrograms(this);
+ parseTextures(this, context);
+ }
+ parseMaterials(this);
+ parseMeshes(this);
+ parseNodes(this);
+
+ this._boundingSphere = computeBoundingSphere(this);
+ this._initialRadius = this._boundingSphere.radius;
+ this._updatedGltfVersion = true;
+ }
+ if (this._updatedGltfVersion && loadResources.pendingShaderLoads === 0) {
+ createResources(this, frameState);
+ }
+ }
if (loadResources.finished() ||
- (incrementallyLoadTextures && loadResources.finishedEverythingButTextureCreation())) {
+ (incrementallyLoadTextures && loadResources.finishedEverythingButTextureCreation())) {
this._state = ModelState.LOADED;
justLoaded = true;
}
@@ -4550,7 +4574,6 @@ define([
// Incrementally stream textures.
if (defined(loadResources) && (this._state === ModelState.LOADED)) {
- // Also check justLoaded so we don't process twice during the transition frame
if (incrementallyLoadTextures && !justLoaded) {
createResources(this, frameState);
}
@@ -4570,6 +4593,7 @@ define([
cachedResources.samplers = resources.samplers;
cachedResources.renderStates = resources.renderStates;
cachedResources.ready = true;
+ removePipelineExtras(this.gltf);
// The normal attribute name is required for silhouettes, so get it before the gltf JSON is released
this._normalAttributeName = getAttributeOrUniformBySemantic(this.gltf, 'NORMAL');
diff --git a/Source/Scene/ModelAnimationCache.js b/Source/Scene/ModelAnimationCache.js
index 8a4f9a581533..edcee52f8bfa 100644
--- a/Source/Scene/ModelAnimationCache.js
+++ b/Source/Scene/ModelAnimationCache.js
@@ -1,23 +1,31 @@
define([
+ './AttributeType',
'../Core/Cartesian3',
+ '../Core/ComponentDatatype',
+ '../Core/defaultValue',
'../Core/defined',
'../Core/LinearSpline',
'../Core/Matrix4',
'../Core/Quaternion',
'../Core/QuaternionSpline',
'../Core/WebGLConstants',
- './AttributeType',
- './getBinaryAccessor'
+ '../Core/WeightSpline',
+ '../ThirdParty/GltfPipeline/getAccessorByteStride',
+ '../ThirdParty/GltfPipeline/numberOfComponentsForType'
], function(
+ AttributeType,
Cartesian3,
+ ComponentDatatype,
+ defaultValue,
defined,
LinearSpline,
Matrix4,
Quaternion,
QuaternionSpline,
WebGLConstants,
- AttributeType,
- getBinaryAccessor) {
+ WeightSpline,
+ getAccessorByteStride,
+ numberOfComponentsForType) {
'use strict';
/**
@@ -37,7 +45,7 @@ define([
var buffer = buffers[bufferView.buffer];
var byteOffset = bufferView.byteOffset + accessor.byteOffset;
- var byteLength = accessor.count * getBinaryAccessor(accessor).componentsPerAttribute;
+ var byteLength = accessor.count * numberOfComponentsForType(accessor.type);
var uriKey = dataUriRegex.test(buffer.uri) ? '' : buffer.uri;
return model.cacheKey + '//' + uriKey + '/' + byteOffset + '/' + byteLength;
@@ -46,50 +54,41 @@ define([
var cachedAnimationParameters = {
};
- var axisScratch = new Cartesian3();
-
ModelAnimationCache.getAnimationParameterValues = function(model, accessor) {
var key = getAccessorKey(model, accessor);
var values = cachedAnimationParameters[key];
if (!defined(values)) {
// Cache miss
- var loadResources = model._loadResources;
var gltf = model.gltf;
- var hasAxisAngle = (parseFloat(gltf.asset.version) < 1.0);
+ var buffers = gltf.buffers;
var bufferViews = gltf.bufferViews;
var bufferView = bufferViews[accessor.bufferView];
+ var bufferId = bufferView.buffer;
+ var buffer = buffers[bufferId];
+ var source = buffer.extras._pipeline.source;
var componentType = accessor.componentType;
var type = accessor.type;
+ var numberOfComponents = numberOfComponentsForType(type);
var count = accessor.count;
-
- // Convert typed array to Cesium types
- var buffer = loadResources.getBuffer(bufferView);
- var typedArray = getBinaryAccessor(accessor).createArrayBufferView(buffer.buffer, buffer.byteOffset + accessor.byteOffset, count);
- var i;
-
- if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.SCALAR)) {
- values = typedArray;
- }
- else if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.VEC3)) {
- values = new Array(count);
- for (i = 0; i < count; ++i) {
- values[i] = Cartesian3.fromArray(typedArray, 3 * i);
- }
- } else if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.VEC4)) {
- values = new Array(count);
- for (i = 0; i < count; ++i) {
- var byteOffset = 4 * i;
- if (hasAxisAngle) {
- values[i] = Quaternion.fromAxisAngle(Cartesian3.fromArray(typedArray, byteOffset, axisScratch), typedArray[byteOffset + 3]);
- }
- else {
- values[i] = Quaternion.unpack(typedArray, byteOffset);
- }
+ var byteStride = getAccessorByteStride(gltf, accessor);
+
+ values = new Array(count);
+ var accessorByteOffset = defaultValue(accessor.byteOffset, 0);
+ var byteOffset = bufferView.byteOffset + accessorByteOffset;
+ for (var i = 0; i < count; i++) {
+ var typedArrayView = ComponentDatatype.createArrayBufferView(componentType, source.buffer, byteOffset, numberOfComponents);
+ if (type === 'SCALAR') {
+ values[i] = typedArrayView[0];
+ } else if (type === 'VEC3') {
+ values[i] = Cartesian3.fromArray(typedArrayView);
+ } else if (type === 'VEC4') {
+ values[i] = Quaternion.unpack(typedArrayView);
}
+ byteOffset += byteStride;
}
// GLTF_SPEC: Support more parameter types when glTF supports targeting materials. https://github.com/KhronosGroup/glTF/issues/142
@@ -109,47 +108,41 @@ define([
return model.cacheKey + '//' + animationName + '/' + samplerName;
}
- // GLTF_SPEC: https://github.com/KhronosGroup/glTF/issues/185
function ConstantSpline(value) {
this._value = value;
}
ConstantSpline.prototype.evaluate = function(time, result) {
return this._value;
};
- // END GLTF_SPEC
- ModelAnimationCache.getAnimationSpline = function(model, animationName, animation, samplerName, sampler, parameterValues) {
+ ModelAnimationCache.getAnimationSpline = function(model, animationName, animation, samplerName, sampler, input, path, output) {
var key = getAnimationSplineKey(model, animationName, samplerName);
var spline = cachedAnimationSplines[key];
if (!defined(spline)) {
- var times = parameterValues[sampler.input];
- var accessor = model.gltf.accessors[animation.parameters[sampler.output]];
- var controlPoints = parameterValues[sampler.output];
+ var times = input;
+ var controlPoints = output;
-// GLTF_SPEC: https://github.com/KhronosGroup/glTF/issues/185
if ((times.length === 1) && (controlPoints.length === 1)) {
spline = new ConstantSpline(controlPoints[0]);
- } else {
-// END GLTF_SPEC
- var componentType = accessor.componentType;
- var type = accessor.type;
-
- if (sampler.interpolation === 'LINEAR') {
- if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.VEC3)) {
- spline = new LinearSpline({
- times : times,
- points : controlPoints
- });
- } else if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.VEC4)) {
- spline = new QuaternionSpline({
- times : times,
- points : controlPoints
- });
- }
- // GLTF_SPEC: Support more parameter types when glTF supports targeting materials. https://github.com/KhronosGroup/glTF/issues/142
+ } else if (sampler.interpolation === 'LINEAR') {
+ if (path === 'translation' || path === 'scale') {
+ spline = new LinearSpline({
+ times : times,
+ points : controlPoints
+ });
+ } else if (path === 'rotation') {
+ spline = new QuaternionSpline({
+ times : times,
+ points : controlPoints
+ });
+ } else if (path === 'weights') {
+ spline = new WeightSpline({
+ times : times,
+ weights : controlPoints
+ });
}
- // GLTF_SPEC: Support new interpolators. https://github.com/KhronosGroup/glTF/issues/156
+ // GLTF_SPEC: Support more parameter types when glTF supports targeting materials. https://github.com/KhronosGroup/glTF/issues/142
}
if (defined(model.cacheKey)) {
@@ -170,23 +163,30 @@ define([
if (!defined(matrices)) {
// Cache miss
-
- var loadResources = model._loadResources;
var gltf = model.gltf;
+ var buffers = gltf.buffers;
var bufferViews = gltf.bufferViews;
- var bufferView = bufferViews[accessor.bufferView];
+ var bufferViewId = accessor.bufferView;
+ var bufferView = bufferViews[bufferViewId];
+ var bufferId = bufferView.buffer;
+ var buffer = buffers[bufferId];
+ var source = buffer.extras._pipeline.source;
var componentType = accessor.componentType;
var type = accessor.type;
var count = accessor.count;
- var buffer = loadResources.getBuffer(bufferView);
- var typedArray = getBinaryAccessor(accessor).createArrayBufferView(buffer.buffer, buffer.byteOffset + accessor.byteOffset, count);
- matrices = new Array(count);
+ var byteStride = getAccessorByteStride(gltf, accessor);
+ var byteOffset = bufferView.byteOffset + accessor.byteOffset;
+ var numberOfComponents = numberOfComponentsForType(type);
+
+ matrices = new Array(count);
if ((componentType === WebGLConstants.FLOAT) && (type === AttributeType.MAT4)) {
for (var i = 0; i < count; ++i) {
- matrices[i] = Matrix4.fromArray(typedArray, 16 * i);
+ var typedArrayView = ComponentDatatype.createArrayBufferView(componentType, source.buffer, byteOffset, numberOfComponents);
+ matrices[i] = Matrix4.fromArray(typedArrayView);
+ byteOffset += byteStride;
}
}
diff --git a/Source/Scene/ModelAnimationCollection.js b/Source/Scene/ModelAnimationCollection.js
index b2603afb5821..0ce83970ac46 100644
--- a/Source/Scene/ModelAnimationCollection.js
+++ b/Source/Scene/ModelAnimationCollection.js
@@ -212,7 +212,6 @@ define([
options.name = animationIds[i];
scheduledAnimations.push(this.add(options));
}
-
return scheduledAnimations;
};
diff --git a/Source/Scene/Scene.js b/Source/Scene/Scene.js
index f99853f1ac4e..7ce1b5d96d5d 100644
--- a/Source/Scene/Scene.js
+++ b/Source/Scene/Scene.js
@@ -2,6 +2,7 @@ define([
'../Core/BoundingRectangle',
'../Core/BoundingSphere',
'../Core/BoxGeometry',
+ '../Core/buildModuleUrl',
'../Core/Cartesian2',
'../Core/Cartesian3',
'../Core/Cartesian4',
@@ -42,13 +43,16 @@ define([
'../Renderer/ContextLimits',
'../Renderer/DrawCommand',
'../Renderer/Framebuffer',
+ '../Renderer/loadCubeMap',
'../Renderer/Pass',
'../Renderer/PassState',
'../Renderer/PixelDatatype',
'../Renderer/RenderState',
+ '../Renderer/Sampler',
'../Renderer/ShaderProgram',
'../Renderer/ShaderSource',
'../Renderer/Texture',
+ './BrdfLutGenerator',
'./Camera',
'./CreditDisplay',
'./DebugCameraPrimitive',
@@ -78,6 +82,7 @@ define([
BoundingRectangle,
BoundingSphere,
BoxGeometry,
+ buildModuleUrl,
Cartesian2,
Cartesian3,
Cartesian4,
@@ -118,13 +123,16 @@ define([
ContextLimits,
DrawCommand,
Framebuffer,
+ loadCubeMap,
Pass,
PassState,
PixelDatatype,
RenderState,
+ Sampler,
ShaderProgram,
ShaderSource,
Texture,
+ BrdfLutGenerator,
Camera,
CreditDisplay,
DebugCameraPrimitive,
@@ -623,6 +631,8 @@ define([
enabled : defaultValue(options.shadows, false)
});
+ this._brdfLutGenerator = new BrdfLutGenerator();
+
this._terrainExaggeration = defaultValue(options.terrainExaggeration, 1.0);
this._performanceDisplay = undefined;
@@ -1279,6 +1289,8 @@ define([
var frameState = scene._frameState;
frameState.commandList.length = 0;
frameState.shadowMaps.length = 0;
+ frameState.brdfLutGenerator = scene._brdfLutGenerator;
+ frameState.environmentMap = scene.skyBox && scene.skyBox._cubeMap;
frameState.mode = scene._mode;
frameState.morphTime = scene.morphTime;
frameState.mapProjection = scene.mapProjection;
@@ -3327,6 +3339,7 @@ define([
this._depthPlane = this._depthPlane && this._depthPlane.destroy();
this._transitioner.destroy();
this._debugFrustumPlanes = this._debugFrustumPlanes && this._debugFrustumPlanes.destroy();
+ this._brdfLutGenerator = this._brdfLutGenerator && this._brdfLutGenerator.destroy();
if (defined(this._globeDepth)) {
this._globeDepth.destroy();
diff --git a/Source/Scene/TimeDynamicImagery.js b/Source/Scene/TimeDynamicImagery.js
index 8d2de9a10be4..f51f38657fc0 100644
--- a/Source/Scene/TimeDynamicImagery.js
+++ b/Source/Scene/TimeDynamicImagery.js
@@ -1,4 +1,3 @@
-/*global define*/
define([
'../Core/Check',
'../Core/defaultValue',
diff --git a/Source/Scene/getAttributeOrUniformBySemantic.js b/Source/Scene/getAttributeOrUniformBySemantic.js
index 6afe2bb935e9..b30affe7240b 100644
--- a/Source/Scene/getAttributeOrUniformBySemantic.js
+++ b/Source/Scene/getAttributeOrUniformBySemantic.js
@@ -1,4 +1,7 @@
-define([], function() {
+define([
+ '../Core/defined'
+ ], function(
+ defined) {
'use strict';
/**
@@ -8,6 +11,7 @@ define([], function() {
*/
function getAttributeOrUniformBySemantic(gltf, semantic) {
var techniques = gltf.techniques;
+ var parameter;
for (var techniqueName in techniques) {
if (techniques.hasOwnProperty(techniqueName)) {
var technique = techniques[techniqueName];
@@ -16,14 +20,16 @@ define([], function() {
var uniforms = technique.uniforms;
for (var attributeName in attributes) {
if (attributes.hasOwnProperty(attributeName)) {
- if (parameters[attributes[attributeName]].semantic === semantic) {
+ parameter = parameters[attributes[attributeName]];
+ if (defined(parameter) && parameter.semantic === semantic) {
return attributeName;
}
}
}
for (var uniformName in uniforms) {
if (uniforms.hasOwnProperty(uniformName)) {
- if (parameters[uniforms[uniformName]].semantic === semantic) {
+ parameter = parameters[uniforms[uniformName]];
+ if (defined(parameter) && parameter.semantic === semantic) {
return uniformName;
}
}
diff --git a/Source/Shaders/BrdfLutGeneratorFS.glsl b/Source/Shaders/BrdfLutGeneratorFS.glsl
new file mode 100644
index 000000000000..9a432f1086f2
--- /dev/null
+++ b/Source/Shaders/BrdfLutGeneratorFS.glsl
@@ -0,0 +1,83 @@
+varying vec2 v_textureCoordinates;
+const float M_PI = 3.141592653589793;
+
+float vdcRadicalInverse(int i)
+{
+ float r;
+ float base = 2.0;
+ float value = 0.0;
+ float invBase = 1.0 / base;
+ float invBi = invBase;
+ for (int x = 0; x < 100; x++)
+ {
+ if (i <= 0)
+ {
+ break;
+ }
+ r = mod(float(i), base);
+ value += r * invBi;
+ invBi *= invBase;
+ i = int(float(i) * invBase);
+ }
+ return value;
+}
+
+vec2 hammersley2D(int i, int N)
+{
+ return vec2(float(i) / float(N), vdcRadicalInverse(i));
+}
+
+vec3 importanceSampleGGX(vec2 xi, float roughness, vec3 N)
+{
+ float a = roughness * roughness;
+ float phi = 2.0 * M_PI * xi.x;
+ float cosTheta = sqrt((1.0 - xi.y) / (1.0 + (a * a - 1.0) * xi.y));
+ float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+ vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+ vec3 upVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ vec3 tangentX = normalize(cross(upVector, N));
+ vec3 tangentY = cross(N, tangentX);
+ return tangentX * H.x + tangentY * H.y + N * H.z;
+}
+
+float G1_Smith(float NdotV, float k)
+{
+ return NdotV / (NdotV * (1.0 - k) + k);
+}
+
+float G_Smith(float roughness, float NdotV, float NdotL)
+{
+ float k = roughness * roughness / 2.0;
+ return G1_Smith(NdotV, k) * G1_Smith(NdotL, k);
+}
+
+vec2 integrateBrdf(float roughness, float NdotV)
+{
+ vec3 V = vec3(sqrt(1.0 - NdotV * NdotV), 0.0, NdotV);
+ float A = 0.0;
+ float B = 0.0;
+ const int NumSamples = 1024;
+ for (int i = 0; i < NumSamples; i++)
+ {
+ vec2 xi = hammersley2D(i, NumSamples);
+ vec3 H = importanceSampleGGX(xi, roughness, vec3(0.0, 0.0, 1.0));
+ vec3 L = 2.0 * dot(V, H) * H - V;
+ float NdotL = clamp(L.z, 0.0, 1.0);
+ float NdotH = clamp(H.z, 0.0, 1.0);
+ float VdotH = clamp(dot(V, H), 0.0, 1.0);
+ if (NdotL > 0.0)
+ {
+ float G = G_Smith(roughness, NdotV, NdotL);
+ float G_Vis = G * VdotH / (NdotH * NdotV);
+ float Fc = pow(1.0 - VdotH, 5.0);
+ A += (1.0 - Fc) * G_Vis;
+ B += Fc * G_Vis;
+ }
+ }
+ return vec2(A, B) / float(NumSamples);
+}
+
+void main()
+{
+ gl_FragColor = vec4(integrateBrdf(1.0 - v_textureCoordinates.y, v_textureCoordinates.x), 0.0, 1.0);
+}
diff --git a/Source/ThirdParty/GltfPipeline/ForEach.js b/Source/ThirdParty/GltfPipeline/ForEach.js
new file mode 100644
index 000000000000..814af92e5e7b
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/ForEach.js
@@ -0,0 +1,215 @@
+define([
+ '../../Core/defined'
+ ], function(
+ defined) {
+ 'use strict';
+
+ /**
+ * Contains traversal functions for processing elements of the glTF hierarchy.
+ * @constructor
+ */
+ function ForEach() {}
+
+ ForEach.object = function(arrayOfObjects, handler) {
+ if (defined(arrayOfObjects)) {
+ for (var i = 0; i < arrayOfObjects.length; i++) {
+ var object = arrayOfObjects[i];
+ var returnValue = handler(object, i);
+ if (typeof returnValue === 'number') {
+ i += returnValue;
+ }
+ else if (returnValue) {
+ break;
+ }
+ }
+ }
+ };
+
+ ForEach.topLevel = function(gltf, name, handler) {
+ var arrayOfObjects = gltf[name];
+ ForEach.object(arrayOfObjects, handler);
+ };
+
+ ForEach.accessor = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'accessors', handler);
+ };
+
+ ForEach.accessorWithSemantic = function(gltf, semantic, handler) {
+ ForEach.mesh(gltf, function(mesh) {
+ ForEach.meshPrimitive(mesh, function(primitive) {
+ ForEach.meshPrimitiveAttribute(primitive, function(accessorId, attributeSemantic) {
+ if (attributeSemantic.indexOf(semantic) === 0) {
+ handler(accessorId, attributeSemantic, primitive);
+ }
+ });
+ });
+ });
+ };
+
+ ForEach.animation = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'animations', handler);
+ };
+
+ ForEach.animationSampler = function(animation, handler) {
+ var samplers = animation.samplers;
+ if (defined(samplers)) {
+ ForEach.object(samplers, handler);
+ }
+ };
+
+ ForEach.buffer = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'buffers', handler);
+ };
+
+ ForEach.bufferView = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'bufferViews', handler);
+ };
+
+ ForEach.camera = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'cameras', handler);
+ };
+
+ ForEach.image = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'images', handler);
+ };
+
+ ForEach.material = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'materials', handler);
+ };
+
+ ForEach.materialValue = function(material, handler) {
+ var values = material.values;
+ if (defined(values)) {
+ for (var name in values) {
+ if (values.hasOwnProperty(name)) {
+ handler(values[name], name);
+ }
+ }
+ }
+ };
+
+ ForEach.mesh = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'meshes', handler);
+ };
+
+ ForEach.meshPrimitive = function(mesh, handler) {
+ var primitives = mesh.primitives;
+ if (defined(primitives)) {
+ var primitivesLength = primitives.length;
+ for (var i = 0; i < primitivesLength; i++) {
+ var primitive = primitives[i];
+ handler(primitive, i);
+ }
+ }
+ };
+
+ ForEach.meshPrimitiveAttribute = function(primitive, handler) {
+ var attributes = primitive.attributes;
+ if (defined(attributes)) {
+ for (var semantic in attributes) {
+ if (attributes.hasOwnProperty(semantic)) {
+ handler(attributes[semantic], semantic);
+ }
+ }
+ }
+ };
+
+ ForEach.meshPrimitiveTargetAttribute = function(primitive, handler) {
+ var targets = primitive.targets;
+ if (defined(targets)) {
+ for (var targetId in targets) {
+ if (targets.hasOwnProperty(targetId)) {
+ var target = targets[targetId];
+ for (var attributeId in target) {
+ if (target.hasOwnProperty(attributeId) && attributeId !== 'extras') {
+ handler(target[attributeId], attributeId);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ ForEach.node = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'nodes', handler);
+ };
+
+ ForEach.nodeInTree = function(gltf, nodeIds, handler) {
+ var nodes = gltf.nodes;
+ if (defined(nodes)) {
+ for (var i = 0; i < nodeIds.length; i++) {
+ var nodeId = nodeIds[i];
+ var node = nodes[nodeId];
+ if (defined(node)) {
+ handler(node, nodeId);
+ var children = node.children;
+ if (defined(children)) {
+ ForEach.nodeInTree(gltf, children, handler);
+ }
+ }
+ }
+ }
+ };
+
+ ForEach.nodeInScene = function(gltf, scene, handler) {
+ var sceneNodeIds = scene.nodes;
+ if (defined(sceneNodeIds)) {
+ ForEach.nodeInTree(gltf, sceneNodeIds, handler);
+ }
+ };
+
+ ForEach.program = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'programs', handler);
+ };
+
+ ForEach.sampler = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'samplers', handler);
+ };
+
+ ForEach.scene = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'scenes', handler);
+ };
+
+ ForEach.shader = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'shaders', handler);
+ };
+
+ ForEach.skin = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'skins', handler);
+ };
+
+ ForEach.techniqueAttribute = function(technique, handler) {
+ var attributes = technique.attributes;
+ if (defined(attributes)) {
+ for (var semantic in attributes) {
+ if (attributes.hasOwnProperty(semantic)) {
+ if (handler(attributes[semantic], semantic)) {
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ ForEach.techniqueParameter = function(technique, handler) {
+ var parameters = technique.parameters;
+ if (defined(parameters)) {
+ for (var parameterName in parameters) {
+ if (parameters.hasOwnProperty(parameterName)) {
+ if (handler(parameters[parameterName], parameterName)) {
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ ForEach.technique = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'techniques', handler);
+ };
+
+ ForEach.texture = function(gltf, handler) {
+ ForEach.topLevel(gltf, 'textures', handler);
+ };
+ return ForEach;
+});
diff --git a/Source/ThirdParty/GltfPipeline/addDefaults.js b/Source/ThirdParty/GltfPipeline/addDefaults.js
new file mode 100644
index 000000000000..edb4f5d80ef9
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/addDefaults.js
@@ -0,0 +1,498 @@
+define([
+ './addToArray',
+ './ForEach',
+ '../../Core/clone',
+ '../../Core/defaultValue',
+ '../../Core/defined',
+ '../../Core/WebGLConstants'
+ ], function(
+ addToArray,
+ ForEach,
+ clone,
+ defaultValue,
+ defined,
+ WebGLConstants) {
+ 'use strict';
+
+ var gltfTemplate = {
+ accessors: [],
+ animations : [
+ {
+ channels : [],
+ samplers : [
+ {
+ interpolation : 'LINEAR'
+ }
+ ]
+ }
+ ],
+ asset : {},
+ buffers : [
+ {
+ byteLength: 0,
+ type: 'arraybuffer'
+ }
+ ],
+ bufferViews: [
+ {
+ byteLength: 0
+ }
+ ],
+ cameras: [],
+ images: [],
+ materials: [
+ {
+ values: function(material) {
+ var extensions = defaultValue(material.extensions, {});
+ var materialsCommon = extensions.KHR_materials_common;
+ if (!defined(materialsCommon)) {
+ return {};
+ }
+ },
+ extensions: function(material) {
+ var extensions = defaultValue(material.extensions, {});
+ var materialsCommon = extensions.KHR_materials_common;
+ if (defined(materialsCommon)) {
+ var technique = materialsCommon.technique;
+ var defaults = {
+ ambient: [0.0, 0.0, 0.0, 1.0],
+ emission: [0.0, 0.0, 0.0, 1.0],
+ transparency: [1.0]
+ };
+ if (technique !== 'CONSTANT') {
+ defaults.diffuse = [0.0, 0.0, 0.0, 1.0];
+ if (technique !== 'LAMBERT') {
+ defaults.specular = [0.0, 0.0, 0.0, 1.0];
+ defaults.shininess = [0.0];
+ }
+ }
+ return {
+ KHR_materials_common: {
+ doubleSided: false,
+ transparent: false,
+ values: defaults
+ }
+ };
+ }
+ }
+ }
+ ],
+ meshes : [
+ {
+ primitives : [
+ {
+ attributes : {},
+ mode : WebGLConstants.TRIANGLES
+ }
+ ]
+ }
+ ],
+ nodes : [
+ {
+ children : [],
+ matrix : function(node) {
+ if (!defined(node.translation) && !defined(node.rotation) && !defined(node.scale)) {
+ return [
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0
+ ];
+ }
+ },
+ rotation : function(node) {
+ if (defined(node.translation) || defined(node.scale)) {
+ return [0.0, 0.0, 0.0, 1.0];
+ }
+ },
+ scale : function(node) {
+ if (defined(node.translation) || defined(node.rotation)) {
+ return [1.0, 1.0, 1.0];
+ }
+ },
+ translation : function(node) {
+ if (defined(node.rotation) || defined(node.scale)) {
+ return [0.0, 0.0, 0.0];
+ }
+ }
+ }
+ ],
+ programs : [
+ {
+ attributes : []
+ }
+ ],
+ samplers : [
+ {
+ magFilter: WebGLConstants.LINEAR,
+ minFilter : WebGLConstants.NEAREST_MIPMAP_LINEAR,
+ wrapS : WebGLConstants.REPEAT,
+ wrapT : WebGLConstants.REPEAT
+ }
+ ],
+ scenes : [
+ {
+ nodes : []
+ }
+ ],
+ shaders : [],
+ skins : [
+ {
+ bindShapeMatrix : [
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0
+ ]
+ }
+ ],
+ techniques : [
+ {
+ parameters: {},
+ attributes: {},
+ uniforms: {},
+ states: {
+ enable: []
+ }
+ }
+ ],
+ textures : [
+ {
+ format: WebGLConstants.RGBA,
+ internalFormat: WebGLConstants.RGBA,
+ target: WebGLConstants.TEXTURE_2D,
+ type: WebGLConstants.UNSIGNED_BYTE
+ }
+ ],
+ extensionsUsed : [],
+ extensionsRequired : []
+ };
+
+ function addDefaultsFromTemplate(object, template) {
+ for (var id in template) {
+ if (template.hasOwnProperty(id)) {
+ var templateValue = template[id];
+ if (typeof templateValue === 'function') {
+ templateValue = templateValue(object);
+ }
+ if (defined(templateValue)) {
+ if (typeof templateValue === 'object') {
+ if (Array.isArray(templateValue)) {
+ var arrayValue = defaultValue(object[id], []);
+ if (templateValue.length > 0) {
+ var arrayTemplate = templateValue[0];
+ if (typeof arrayTemplate === 'object') {
+ var arrayValueLength = arrayValue.length;
+ for (var i = 0; i < arrayValueLength; i++) {
+ addDefaultsFromTemplate(arrayValue[i], arrayTemplate);
+ }
+ } else {
+ arrayValue = defaultValue(object[id], templateValue);
+ }
+ }
+ object[id] = arrayValue;
+ } else {
+ var applyTemplate = templateValue['*'];
+ object[id] = defaultValue(object[id], {});
+ var objectValue = object[id];
+ if (defined(applyTemplate)) {
+ for (var subId in objectValue) {
+ if (objectValue.hasOwnProperty(subId) && subId !== 'extras') {
+ var subObject = objectValue[subId];
+ addDefaultsFromTemplate(subObject, applyTemplate);
+ }
+ }
+ } else {
+ addDefaultsFromTemplate(objectValue, templateValue);
+ }
+ }
+ } else {
+ object[id] = defaultValue(object[id], templateValue);
+ }
+ }
+ }
+ }
+ }
+
+ var defaultMaterial = {
+ values : {
+ emission : [
+ 0.5, 0.5, 0.5, 1.0
+ ]
+ }
+ };
+
+ var defaultTechnique = {
+ attributes : {
+ a_position : 'position'
+ },
+ parameters : {
+ modelViewMatrix : {
+ semantic : 'MODELVIEW',
+ type : WebGLConstants.FLOAT_MAT4
+ },
+ projectionMatrix : {
+ semantic : 'PROJECTION',
+ type : WebGLConstants.FLOAT_MAT4
+ },
+ emission : {
+ type : WebGLConstants.FLOAT_VEC4,
+ value : [
+ 0.5, 0.5, 0.5, 1.0
+ ]
+ },
+ position : {
+ semantic: 'POSITION',
+ type: WebGLConstants.FLOAT_VEC3
+ }
+ },
+ states : {
+ enable : [
+ WebGLConstants.CULL_FACE,
+ WebGLConstants.DEPTH_TEST
+ ]
+ },
+ uniforms : {
+ u_modelViewMatrix : 'modelViewMatrix',
+ u_projectionMatrix : 'projectionMatrix',
+ u_emission : 'emission'
+ }
+ };
+
+ var defaultProgram = {
+ attributes : [
+ 'a_position'
+ ]
+ };
+
+ var defaultVertexShader = {
+ type : WebGLConstants.VERTEX_SHADER,
+ extras : {
+ _pipeline : {
+ extension : '.vert',
+ source : '' +
+ 'precision highp float;\n' +
+ '\n' +
+ 'uniform mat4 u_modelViewMatrix;\n' +
+ 'uniform mat4 u_projectionMatrix;\n' +
+ '\n' +
+ 'attribute vec3 a_position;\n' +
+ '\n' +
+ 'void main (void)\n' +
+ '{\n' +
+ ' gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);\n' +
+ '}\n'
+ }
+ }
+ };
+
+ var defaultFragmentShader = {
+ type : WebGLConstants.FRAGMENT_SHADER,
+ extras : {
+ _pipeline : {
+ extension: '.frag',
+ source : '' +
+ 'precision highp float;\n' +
+ '\n' +
+ 'uniform vec4 u_emission;\n' +
+ '\n' +
+ 'void main(void)\n' +
+ '{\n' +
+ ' gl_FragColor = u_emission;\n' +
+ '}\n'
+ }
+ }
+ };
+
+ function addDefaultMaterial(gltf) {
+ var materials = gltf.materials;
+ var meshes = gltf.meshes;
+
+ var defaultMaterialId;
+
+ var meshesLength = meshes.length;
+ for (var meshId = 0; meshId < meshesLength; meshId++) {
+ var mesh = meshes[meshId];
+ var primitives = mesh.primitives;
+ var primitivesLength = primitives.length;
+ for (var j = 0; j < primitivesLength; j++) {
+ var primitive = primitives[j];
+ if (!defined(primitive.material)) {
+ if (!defined(defaultMaterialId)) {
+ defaultMaterialId = addToArray(materials, clone(defaultMaterial, true));
+ }
+ primitive.material = defaultMaterialId;
+ }
+ }
+ }
+ }
+
+ function addDefaultTechnique(gltf) {
+ var materials = gltf.materials;
+ var techniques = gltf.techniques;
+ var programs = gltf.programs;
+ var shaders = gltf.shaders;
+
+ var defaultTechniqueId;
+
+ var materialsLength = materials.length;
+ for (var materialId = 0; materialId < materialsLength; materialId++) {
+ var material = materials[materialId];
+ var techniqueId = material.technique;
+ var extensions = defaultValue(material.extensions, {});
+ var materialsCommon = extensions.KHR_materials_common;
+ if (!defined(techniqueId)) {
+ if (!defined(defaultTechniqueId) && !defined(materialsCommon)) {
+ var technique = clone(defaultTechnique, true);
+ defaultTechniqueId = addToArray(techniques, technique);
+
+ var program = clone(defaultProgram, true);
+ technique.program = addToArray(programs, program);
+
+ var vertexShader = clone(defaultVertexShader, true);
+ program.vertexShader = addToArray(shaders, vertexShader);
+
+ var fragmentShader = clone(defaultFragmentShader, true);
+ program.fragmentShader = addToArray(shaders, fragmentShader);
+ }
+ material.technique = defaultTechniqueId;
+ }
+
+ }
+ }
+
+ function addDefaultByteOffsets(gltf) {
+ var accessors = gltf.accessors;
+
+ var accessorLength = accessors.length;
+ for (var i = 0; i < accessorLength; i++) {
+ var accessor = accessors[i];
+ if (!defined(accessor.byteOffset)) {
+ accessor.byteOffset = 0;
+ }
+ }
+
+ var bufferViews = gltf.bufferViews;
+
+ var bufferViewsLength = bufferViews.length;
+ for (var j = 0; j < bufferViewsLength; j++) {
+ var bufferView = bufferViews[j];
+ if (!defined(bufferView.byteOffset)) {
+ bufferView.byteOffset = 0;
+ }
+ }
+ }
+
+ function selectDefaultScene(gltf) {
+ var scenes = gltf.scenes;
+
+ if (!defined(gltf.scene)) {
+ var scenesLength = scenes.length;
+ for (var sceneId = 0; sceneId < scenesLength; sceneId++) {
+ gltf.scene = sceneId;
+ break;
+ }
+ }
+ }
+
+ function optimizeForCesium(gltf) {
+ // Give the diffuse uniform a semantic to support color replacement in 3D Tiles
+ var techniques = gltf.techniques;
+ var techniquesLength = techniques.length;
+ for (var techniqueId = 0; techniqueId < techniquesLength; techniqueId++) {
+ var technique = techniques[techniqueId];
+ var parameters = technique.parameters;
+ if (defined(parameters.diffuse)) {
+ parameters.diffuse.semantic = '_3DTILESDIFFUSE';
+ }
+ }
+ }
+
+ function inferBufferViewTargets(gltf) {
+ // If bufferView elements are missing targets, we can infer their type from their use
+ var needsTarget = {};
+ var shouldTraverse = 0;
+ ForEach.bufferView(gltf, function(bufferView, bufferViewId) {
+ if (!defined(bufferView.target)) {
+ needsTarget[bufferViewId] = true;
+ shouldTraverse++;
+ }
+ });
+ if (shouldTraverse > 0) {
+ var accessors = gltf.accessors;
+ var bufferViews = gltf.bufferViews;
+ ForEach.mesh(gltf, function (mesh) {
+ ForEach.meshPrimitive(mesh, function (primitive) {
+ var indices = primitive.indices;
+ if (defined(indices)) {
+ var accessor = accessors[indices];
+ var bufferViewId = accessor.bufferView;
+ if (needsTarget[bufferViewId]) {
+ var bufferView = bufferViews[bufferViewId];
+ if (defined(bufferView)) {
+ bufferView.target = WebGLConstants.ELEMENT_ARRAY_BUFFER;
+ needsTarget[bufferViewId] = false;
+ shouldTraverse--;
+ }
+ }
+ }
+ ForEach.meshPrimitiveAttribute(primitive, function (accessorId) {
+ var accessor = accessors[accessorId];
+ var bufferViewId = accessor.bufferView;
+ if (needsTarget[bufferViewId]) {
+ var bufferView = bufferViews[bufferViewId];
+ if (defined(bufferView)) {
+ bufferView.target = WebGLConstants.ARRAY_BUFFER;
+ needsTarget[bufferViewId] = false;
+ shouldTraverse--;
+ }
+ }
+ });
+ ForEach.meshPrimitiveTargetAttribute(primitive, function (targetAttribute) {
+ var bufferViewId = accessors[targetAttribute].bufferView;
+ if (needsTarget[bufferViewId]) {
+ var bufferView = bufferViews[bufferViewId];
+ if (defined(bufferView)) {
+ bufferView.target = WebGLConstants.ARRAY_BUFFER;
+ needsTarget[bufferViewId] = false;
+ shouldTraverse--;
+ }
+ }
+ });
+ });
+ if (shouldTraverse === 0) {
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * Adds default glTF values if they don't exist.
+ *
+ * The glTF asset must be initialized for the pipeline.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {Object} [options] An object with the following properties:
+ * @param {Boolean} [options.optimizeForCesium] Optimize the defaults for Cesium. Uses the Cesium sun as the default light source.
+ * @returns {Object} The modified glTF.
+ *
+ * @see addPipelineExtras
+ * @see loadGltfUris
+ */
+ function addDefaults(gltf, options) {
+ options = defaultValue(options, {});
+ addDefaultsFromTemplate(gltf, gltfTemplate);
+ addDefaultMaterial(gltf);
+ addDefaultTechnique(gltf);
+ addDefaultByteOffsets(gltf);
+ selectDefaultScene(gltf);
+ inferBufferViewTargets(gltf);
+ if (options.optimizeForCesium) {
+ optimizeForCesium(gltf);
+ }
+ return gltf;
+ }
+
+ return addDefaults;
+});
diff --git a/Source/ThirdParty/GltfPipeline/addExtensionsRequired.js b/Source/ThirdParty/GltfPipeline/addExtensionsRequired.js
new file mode 100644
index 000000000000..49ec2de50fa6
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/addExtensionsRequired.js
@@ -0,0 +1,28 @@
+define([
+ './addExtensionsUsed',
+ '../../Core/defined'
+ ], function(
+ addExtensionsUsed,
+ defined) {
+ 'use strict';
+
+ /**
+ * Adds an extension to gltf.extensionsRequired if it does not already exist.
+ * Initializes extensionsRequired if it is not defined.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {String} extension The extension to add.
+ */
+ function addExtensionsRequired(gltf, extension) {
+ var extensionsRequired = gltf.extensionsRequired;
+ if (!defined(extensionsRequired)) {
+ extensionsRequired = [];
+ gltf.extensionsRequired = extensionsRequired;
+ }
+ if (extensionsRequired.indexOf(extension) < 0) {
+ extensionsRequired.push(extension);
+ }
+ addExtensionsUsed(gltf, extension);
+ }
+ return addExtensionsRequired;
+});
diff --git a/Source/ThirdParty/GltfPipeline/addExtensionsUsed.js b/Source/ThirdParty/GltfPipeline/addExtensionsUsed.js
new file mode 100644
index 000000000000..6d8bed56db03
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/addExtensionsUsed.js
@@ -0,0 +1,25 @@
+define([
+ '../../Core/defined'
+ ], function(
+ defined) {
+ 'use strict';
+
+ /**
+ * Adds an extension to gltf.extensionsUsed if it does not already exist.
+ * Initializes extensionsUsed if it is not defined.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {String} extension The extension to add.
+ */
+ function addExtensionsUsed(gltf, extension) {
+ var extensionsUsed = gltf.extensionsUsed;
+ if (!defined(extensionsUsed)) {
+ extensionsUsed = [];
+ gltf.extensionsUsed = extensionsUsed;
+ }
+ if (extensionsUsed.indexOf(extension) < 0) {
+ extensionsUsed.push(extension);
+ }
+ }
+ return addExtensionsUsed;
+});
diff --git a/Source/ThirdParty/GltfPipeline/addPipelineExtras.js b/Source/ThirdParty/GltfPipeline/addPipelineExtras.js
new file mode 100644
index 000000000000..26770de6b816
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/addPipelineExtras.js
@@ -0,0 +1,63 @@
+define([
+ '../../Core/defaultValue',
+ '../../Core/defined'
+ ], function(
+ defaultValue,
+ defined) {
+ 'use strict';
+
+ // Objects with these ids should not have extras added
+ var exceptions = {
+ attributes: true,
+ uniforms: true,
+ extensions: true,
+ values: true,
+ samplers: true
+ };
+
+ /**
+ * Adds extras._pipeline to each object that can have extras in the glTF asset.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @returns {Object} The glTF asset with the added pipeline extras.
+ */
+ function addPipelineExtras(gltf) {
+ var objectStack = [];
+ for (var rootArrayId in gltf) {
+ if (gltf.hasOwnProperty(rootArrayId)) {
+ var rootArray = gltf[rootArrayId];
+ var rootArrayLength = rootArray.length;
+ for (var i = 0; i < rootArrayLength; i++) {
+ var rootObject = rootArray[i];
+ if (defined(rootObject) && typeof rootObject === 'object') {
+ rootObject.extras = defaultValue(rootObject.extras, {});
+ rootObject.extras._pipeline = defaultValue(rootObject.extras._pipeline, {});
+ objectStack.push(rootObject);
+ }
+ }
+ }
+ }
+ while (objectStack.length > 0) {
+ var object = objectStack.pop();
+ for (var propertyId in object) {
+ if (object.hasOwnProperty(propertyId)) {
+ var property = object[propertyId];
+ if (defined(property) && typeof property === 'object' && propertyId !== 'extras') {
+ objectStack.push(property);
+ if (!exceptions[propertyId] && !Array.isArray(property)) {
+ property.extras = defaultValue(property.extras, {});
+ property.extras._pipeline = defaultValue(property.extras._pipeline, {});
+ }
+ }
+ }
+ }
+ }
+ gltf.extras = defaultValue(gltf.extras, {});
+ gltf.extras._pipeline = defaultValue(gltf.extras._pipeline, {});
+ gltf.asset = defaultValue(gltf.asset, {});
+ gltf.asset.extras = defaultValue(gltf.asset.extras, {});
+ gltf.asset.extras._pipeline = defaultValue(gltf.asset.extras._pipeline, {});
+ return gltf;
+ }
+ return addPipelineExtras;
+});
diff --git a/Source/ThirdParty/GltfPipeline/addToArray.js b/Source/ThirdParty/GltfPipeline/addToArray.js
new file mode 100644
index 000000000000..8559cb54288b
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/addToArray.js
@@ -0,0 +1,9 @@
+define([], function() {
+ 'use strict';
+
+ function addToArray(array, element) {
+ array.push(element);
+ return array.length - 1;
+ }
+ return addToArray;
+});
diff --git a/Source/ThirdParty/GltfPipeline/byteLengthForComponentType.js b/Source/ThirdParty/GltfPipeline/byteLengthForComponentType.js
new file mode 100644
index 000000000000..409bc740ad48
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/byteLengthForComponentType.js
@@ -0,0 +1,34 @@
+define([
+ '../../Core/WebGLConstants'
+ ], function(
+ WebGLConstants) {
+ 'use strict';
+
+ /**
+ * Utility function for retrieving the byte length of a component type.
+ * As per the spec:
+ * 5120 (BYTE) : 1
+ * 5121 (UNSIGNED_BYTE) : 1
+ * 5122 (SHORT) : 2
+ * 5123 (UNSIGNED_SHORT) : 2
+ * 5126 (FLOAT) : 4
+ * 5125 (UNSIGNED_INT) : 4
+ *
+ * @param {Number} [componentType]
+ * @returns {Number} The byte length of the component type.
+ */
+ function byteLengthForComponentType(componentType) {
+ switch (componentType) {
+ case WebGLConstants.BYTE:
+ case WebGLConstants.UNSIGNED_BYTE:
+ return 1;
+ case WebGLConstants.SHORT:
+ case WebGLConstants.UNSIGNED_SHORT:
+ return 2;
+ case WebGLConstants.FLOAT:
+ case WebGLConstants.UNSIGNED_INT:
+ return 4;
+ }
+ }
+ return byteLengthForComponentType;
+});
diff --git a/Source/ThirdParty/GltfPipeline/findAccessorMinMax.js b/Source/ThirdParty/GltfPipeline/findAccessorMinMax.js
new file mode 100644
index 000000000000..3ebf53b24eb1
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/findAccessorMinMax.js
@@ -0,0 +1,63 @@
+define([
+ './getAccessorByteStride',
+ './numberOfComponentsForType',
+ '../../Core/arrayFill',
+ '../../Core/ComponentDatatype',
+ '../../Core/defined'
+ ], function(
+ getAccessorByteStride,
+ numberOfComponentsForType,
+ arrayFill,
+ ComponentDatatype,
+ defined) {
+ 'use strict';
+
+ /**
+ * Finds the min and max for an accessor in gltf.
+ *
+ * The glTF asset must be initialized for the pipeline.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {Object} accessor The accessor object from the glTF asset to read.
+ * @returns {{min: Array, max: Array}} min holding the array of minimum values and max holding the array of maximum values.
+ *
+ * @see addPipelineExtras
+ * @see loadGltfUris
+ */
+ function findAccessorMinMax(gltf, accessor) {
+ var bufferViews = gltf.bufferViews;
+ var buffers = gltf.buffers;
+ var bufferViewId = accessor.bufferView;
+ var numberOfComponents = numberOfComponentsForType(accessor.type);
+ var min = arrayFill(new Array(numberOfComponents), Number.POSITIVE_INFINITY);
+ var max = arrayFill(new Array(numberOfComponents), Number.NEGATIVE_INFINITY);
+ if (defined(bufferViewId) && defined(bufferViews) && bufferViews.hasOwnProperty(bufferViewId)) {
+ var bufferView = bufferViews[bufferViewId];
+ var bufferId = bufferView.buffer;
+ if (defined(bufferId) && defined(buffers) && buffers.hasOwnProperty(bufferId)) {
+ var buffer = buffers[bufferId];
+ var source = buffer.extras._pipeline.source;
+
+ var count = accessor.count;
+ var byteStride = getAccessorByteStride(gltf, accessor);
+ var byteOffset = accessor.byteOffset + bufferView.byteOffset;
+ var componentType = accessor.componentType;
+
+ for (var i = 0; i < count; i++) {
+ var typedArray = ComponentDatatype.createArrayBufferView(componentType, source.buffer, byteOffset + source.byteOffset, numberOfComponents);
+ for (var j = 0; j < numberOfComponents; j++) {
+ var value = typedArray[j];
+ min[j] = Math.min(min[j], value);
+ max[j] = Math.max(max[j], value);
+ }
+ byteOffset += byteStride;
+ }
+ }
+ }
+ return {
+ min : min,
+ max : max
+ };
+ }
+ return findAccessorMinMax;
+});
diff --git a/Source/ThirdParty/GltfPipeline/getAccessorByteStride.js b/Source/ThirdParty/GltfPipeline/getAccessorByteStride.js
new file mode 100644
index 000000000000..c9dbfe0f9fb4
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/getAccessorByteStride.js
@@ -0,0 +1,27 @@
+define([
+ './byteLengthForComponentType',
+ './numberOfComponentsForType',
+ '../../Core/defined'
+ ], function(
+ byteLengthForComponentType,
+ numberOfComponentsForType,
+ defined) {
+ 'use strict';
+
+ /**
+ * Returns the byte stride of the provided accessor.
+ * If the byteStride is 0, it is calculated based on type and componentType
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {Object} accessor The accessor.
+ * @returns {Number} The byte stride of the accessor.
+ */
+ function getAccessorByteStride(gltf, accessor) {
+ var bufferView = gltf.bufferViews[accessor.bufferView];
+ if (defined(bufferView.byteStride) && bufferView.byteStride > 0) {
+ return bufferView.byteStride;
+ }
+ return byteLengthForComponentType(accessor.componentType) * numberOfComponentsForType(accessor.type);
+ }
+ return getAccessorByteStride;
+});
diff --git a/Source/ThirdParty/GltfPipeline/getJointCountForMaterials.js b/Source/ThirdParty/GltfPipeline/getJointCountForMaterials.js
new file mode 100644
index 000000000000..ba299be639e7
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/getJointCountForMaterials.js
@@ -0,0 +1,44 @@
+define([
+ './ForEach',
+ '../../Core/defined'
+ ], function(
+ ForEach,
+ defined) {
+ 'use strict';
+
+ function getJointCountForMaterials(gltf) {
+ var meshes = gltf.meshes;
+ var jointCountForMaterialId = {};
+
+ var nodesForSkinId = {};
+ ForEach.node(gltf, function(node) {
+ if (defined(node.skin)) {
+ if (!defined(nodesForSkinId[node.skin])) {
+ nodesForSkinId[node.skin] = [];
+ }
+ nodesForSkinId[node.skin].push(node);
+ }
+ });
+
+ ForEach.skin(gltf, function(skin, skinId) {
+ var jointCount = skin.joints.length;
+ var meshPrimitiveFunction = function(primitive) {
+ jointCountForMaterialId[primitive.material] = jointCount;
+ };
+ var skinnedNodes = nodesForSkinId[skinId];
+ var skinnedNodesLength = skinnedNodes.length;
+ for (var i = 0; i < skinnedNodesLength; i++) {
+ var skinnedNode = skinnedNodes[i];
+ var meshId = skinnedNode.mesh;
+ if (defined(meshId)) {
+ var mesh = meshes[meshId];
+ ForEach.meshPrimitive(mesh, meshPrimitiveFunction);
+ }
+ }
+ });
+
+ return jointCountForMaterialId;
+ }
+
+ return getJointCountForMaterials;
+});
diff --git a/Source/ThirdParty/GltfPipeline/getUniqueId.js b/Source/ThirdParty/GltfPipeline/getUniqueId.js
new file mode 100644
index 000000000000..59f7a36cdf5b
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/getUniqueId.js
@@ -0,0 +1,31 @@
+define([
+ '../../Core/defined'
+ ], function(
+ defined) {
+ 'use strict';
+
+ /**
+ * Given a prefix for a new ID, checks the glTF asset for matching prefixes in top-level objects with IDs and returns a unique ID.
+ *
+ * @param {Object} gltf A javascript object containing a glTF asset.
+ * @param {String} prefix The string to try to use as the id.
+ * @returns {String} A unique id beginning with prefix.
+ */
+ function getUniqueId(gltf, prefix) {
+ var id = prefix;
+ var appendIndex = 0;
+ for (var topLevelGroupId in gltf) {
+ if (gltf.hasOwnProperty(topLevelGroupId)) {
+ var topLevelGroup = gltf[topLevelGroupId];
+ var match = topLevelGroup[id];
+ while (defined(match)) {
+ id = prefix + '_' + appendIndex;
+ match = topLevelGroup[id];
+ appendIndex++;
+ }
+ }
+ }
+ return id;
+ }
+ return getUniqueId;
+});
diff --git a/Source/ThirdParty/GltfPipeline/glslTypeToWebGLConstant.js b/Source/ThirdParty/GltfPipeline/glslTypeToWebGLConstant.js
new file mode 100644
index 000000000000..f05c65a0c871
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/glslTypeToWebGLConstant.js
@@ -0,0 +1,28 @@
+define([
+ '../../Core/WebGLConstants'
+ ], function(
+ WebGLConstants) {
+ 'use strict';
+
+ function glslTypeToWebGLConstant(glslType) {
+ switch (glslType) {
+ case 'float':
+ return WebGLConstants.FLOAT;
+ case 'vec2':
+ return WebGLConstants.FLOAT_VEC2;
+ case 'vec3':
+ return WebGLConstants.FLOAT_VEC3;
+ case 'vec4':
+ return WebGLConstants.FLOAT_VEC4;
+ case 'mat2':
+ return WebGLConstants.FLOAT_MAT2;
+ case 'mat3':
+ return WebGLConstants.FLOAT_MAT3;
+ case 'mat4':
+ return WebGLConstants.FLOAT_MAT4;
+ case 'sampler2D':
+ return WebGLConstants.SAMPLER_2D;
+ }
+ }
+ return glslTypeToWebGLConstant;
+});
diff --git a/Source/ThirdParty/GltfPipeline/numberOfComponentsForType.js b/Source/ThirdParty/GltfPipeline/numberOfComponentsForType.js
new file mode 100644
index 000000000000..f87c62123c3b
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/numberOfComponentsForType.js
@@ -0,0 +1,36 @@
+define([], function() {
+ 'use strict';
+
+ /**
+ * Utility function for retrieving the number of components in a given type.
+ * As per the spec:
+ * 'SCALAR' : 1
+ * 'VEC2' : 2
+ * 'VEC3' : 3
+ * 'VEC4' : 4
+ * 'MAT2' : 4
+ * 'MAT3' : 9
+ * 'MAT4' : 16
+ *
+ * @param {String} type glTF type
+ * @returns {Number} The number of components in that type.
+ */
+ function numberOfComponentsForType(type) {
+ switch (type) {
+ case 'SCALAR':
+ return 1;
+ case 'VEC2':
+ return 2;
+ case 'VEC3':
+ return 3;
+ case 'VEC4':
+ case 'MAT2':
+ return 4;
+ case 'MAT3':
+ return 9;
+ case 'MAT4':
+ return 16;
+ }
+ }
+ return numberOfComponentsForType;
+});
diff --git a/Source/ThirdParty/GltfPipeline/parseBinaryGltf.js b/Source/ThirdParty/GltfPipeline/parseBinaryGltf.js
new file mode 100644
index 000000000000..1fdc55138afb
--- /dev/null
+++ b/Source/ThirdParty/GltfPipeline/parseBinaryGltf.js
@@ -0,0 +1,124 @@
+define([
+ './addPipelineExtras',
+ './removeExtensionsUsed',
+ './updateVersion',
+ '../../Core/ComponentDatatype',
+ '../../Core/defined',
+ '../../Core/DeveloperError',
+ '../../Core/getMagic',
+ '../../Core/getStringFromTypedArray',
+ '../../Core/WebGLConstants'
+ ], function(
+ addPipelineExtras,
+ removeExtensionsUsed,
+ updateVersion,
+ ComponentDatatype,
+ defined,
+ DeveloperError,
+ getMagic,
+ getStringFromTypedArray,
+ WebGLConstants) {
+ 'use strict';
+
+ /**
+ * Parses a binary glTF buffer into glTF JSON.
+ *
+ * @param {Uint8Array} data The binary glTF data to parse.
+ * @returns {Object} The parsed binary glTF.
+ */
+ function parseBinaryGltf(data) {
+ var headerView = ComponentDatatype.createArrayBufferView(WebGLConstants.INT, data.buffer, data.byteOffset, 5);
+
+ // Check that the magic string is present
+ var magic = getMagic(data);
+ if (magic !== 'glTF') {
+ throw new DeveloperError('File is not valid binary glTF');
+ }
+
+ // Check that the version is 1 or 2
+ var version = headerView[1];
+ if (version !== 1 && version !== 2) {
+ throw new DeveloperError('Binary glTF version is not 1 or 2');
+ }
+
+ var gltf;
+ var buffers;
+ var length;
+ // Load binary glTF version 1
+ if (version === 1) {
+ length = headerView[2];
+ var contentLength = headerView[3];
+ var contentFormat = headerView[4];
+
+ // Check that the content format is 0, indicating that it is JSON
+ if (contentFormat !== 0) {
+ throw new DeveloperError('Binary glTF scene format is not JSON');
+ }
+
+ var jsonStart = 20;
+ var binaryStart = jsonStart + contentLength;
+
+ var contentString = getStringFromTypedArray(data, jsonStart, contentLength);
+ gltf = JSON.parse(contentString);
+
+ // Clone just the binary chunk so the underlying buffer can be freed
+ var binaryData = new Uint8Array(data.subarray(binaryStart, length));
+
+ buffers = gltf.buffers;
+ if (defined(buffers) && Object.keys(buffers).length > 0) {
+ var binaryGltfBuffer = buffers.binary_glTF;
+ // In some older models, the binary glTF buffer is named KHR_binary_glTF
+ if (!defined(binaryGltfBuffer)) {
+ binaryGltfBuffer = buffers.KHR_binary_glTF;
+ }
+ if (defined(binaryGltfBuffer)) {
+ binaryGltfBuffer.extras = {
+ _pipeline: {
+ source: binaryData
+ }
+ };
+ }
+ }
+ // Update to glTF 2.0
+ updateVersion(gltf);
+ // Remove the KHR_binary_glTF extension
+ removeExtensionsUsed(gltf, 'KHR_binary_glTF');
+ addPipelineExtras(gltf);
+ }
+
+ // Load binary glTF version 2
+ if (version === 2) {
+ length = headerView[2];
+ var byteOffset = 12;
+ var binaryBuffer;
+ while (byteOffset < length) {
+ var chunkHeaderView = ComponentDatatype.createArrayBufferView(WebGLConstants.INT, data.buffer, data.byteOffset + byteOffset, 2);
+ var chunkLength = chunkHeaderView[0];
+ var chunkType = chunkHeaderView[1];
+ byteOffset += 8;
+ var chunkBuffer = data.subarray(byteOffset, byteOffset + chunkLength);
+ byteOffset += chunkLength;
+ // Load JSON chunk
+ if (chunkType === 0x4E4F534A) {
+ var jsonString = getStringFromTypedArray(chunkBuffer);
+ gltf = JSON.parse(jsonString);
+ addPipelineExtras(gltf);
+ }
+ // Load Binary chunk
+ else if (chunkType === 0x004E4942) {
+ // Clone just the binary chunk so the underlying buffer can be freed
+ binaryBuffer = new Uint8Array(chunkBuffer);
+ }
+ }
+ if (defined(gltf) && defined(binaryBuffer)) {
+ buffers = gltf.buffers;
+ if (defined(buffers) && buffers.length > 0) {
+ var buffer = buffers[0];
+ buffer.extras._pipeline.source = binaryBuffer;
+ }
+ }
+ }
+ return gltf;
+ }
+ return parseBinaryGltf;
+});
diff --git a/Source/Scene/modelMaterialsCommon.js b/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js
similarity index 61%
rename from Source/Scene/modelMaterialsCommon.js
rename to Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js
index 08a004db888c..077430b9a32c 100644
--- a/Source/Scene/modelMaterialsCommon.js
+++ b/Source/ThirdParty/GltfPipeline/processModelMaterialsCommon.js
@@ -1,32 +1,115 @@
define([
- '../Core/defaultValue',
- '../Core/defined',
- '../Core/WebGLConstants'
+ './addToArray',
+ './ForEach',
+ './numberOfComponentsForType',
+ './techniqueParameterForSemantic',
+ './webGLConstantToGlslType',
+ './glslTypeToWebGLConstant',
+ '../../Core/clone',
+ '../../Core/defined',
+ '../../Core/defaultValue',
+ '../../Core/WebGLConstants'
], function(
- defaultValue,
+ addToArray,
+ ForEach,
+ numberOfComponentsForType,
+ techniqueParameterForSemantic,
+ webGLConstantToGlslType,
+ glslTypeToWebGLConstant,
+ clone,
defined,
+ defaultValue,
WebGLConstants) {
'use strict';
- function webGLConstantToGlslType(webGLValue) {
- switch(webGLValue) {
- case WebGLConstants.FLOAT:
- return 'float';
- case WebGLConstants.FLOAT_VEC2:
- return 'vec2';
- case WebGLConstants.FLOAT_VEC3:
- return 'vec3';
- case WebGLConstants.FLOAT_VEC4:
- return 'vec4';
- case WebGLConstants.FLOAT_MAT2:
- return 'mat2';
- case WebGLConstants.FLOAT_MAT3:
- return 'mat3';
- case WebGLConstants.FLOAT_MAT4:
- return 'mat4';
- case WebGLConstants.SAMPLER_2D:
- return 'sampler2D';
+ /**
+ * @private
+ */
+ function processModelMaterialsCommon(gltf, options) {
+ options = defaultValue(options, {});
+
+ if (!defined(gltf)) {
+ return undefined;
}
+
+ var hasExtension = false;
+ var extensionsRequired = gltf.extensionsRequired;
+ var extensionsUsed = gltf.extensionsUsed;
+ if (defined(extensionsUsed)) {
+ var index = extensionsUsed.indexOf('KHR_materials_common');
+ if (index >= 0) {
+ extensionsUsed.splice(index, 1);
+ hasExtension = true;
+ }
+ if (defined(extensionsRequired)) {
+ index = extensionsRequired.indexOf('KHR_materials_common');
+ if (index >= 0) {
+ extensionsRequired.splice(index, 1);
+ }
+ }
+ }
+
+ if (hasExtension) {
+ if (!defined(gltf.programs)) {
+ gltf.programs = [];
+ }
+ if (!defined(gltf.shaders)) {
+ gltf.shaders = [];
+ }
+ if (!defined(gltf.techniques)) {
+ gltf.techniques = [];
+ }
+ lightDefaults(gltf);
+
+ var lightParameters = generateLightParameters(gltf);
+
+ // Pre-processing to assign skinning info and address incompatibilities
+ splitIncompatibleSkins(gltf);
+
+ var techniques = {};
+ ForEach.material(gltf, function(material) {
+ if (defined(material.extensions) && defined(material.extensions.KHR_materials_common)) {
+ var khrMaterialsCommon = material.extensions.KHR_materials_common;
+ var techniqueKey = getTechniqueKey(khrMaterialsCommon);
+ var technique = techniques[techniqueKey];
+ if (!defined(technique)) {
+ technique = generateTechnique(gltf, khrMaterialsCommon, lightParameters, options);
+ techniques[techniqueKey] = technique;
+ }
+
+ // Take advantage of the fact that we generate techniques that use the
+ // same parameter names as the extension values.
+ material.values = {};
+ var values = khrMaterialsCommon.values;
+ for (var valueName in values) {
+ if (values.hasOwnProperty(valueName)) {
+ var value = values[valueName];
+ material.values[valueName] = value;
+ }
+ }
+
+ material.technique = technique;
+
+ delete material.extensions.KHR_materials_common;
+ if (Object.keys(material.extensions).length === 0) {
+ delete material.extensions;
+ }
+ }
+ });
+
+ if (defined(gltf.extensions)) {
+ delete gltf.extensions.KHR_materials_common;
+ if (Object.keys(gltf.extensions).length === 0) {
+ delete gltf.extensions;
+ }
+ }
+
+ // If any primitives have semantics that aren't declared in the generated
+ // shaders, we want to preserve them.
+ ensureSemanticExistence(gltf);
+ }
+
+ return gltf;
}
function generateLightParameters(gltf) {
@@ -55,7 +138,7 @@ define([
// Add light parameters to result
var lightCount = 0;
- for(var lightName in lights) {
+ for (var lightName in lights) {
if (lights.hasOwnProperty(lightName)) {
var light = lights[lightName];
var lightType = light.type;
@@ -65,7 +148,7 @@ define([
}
var lightBaseName = 'light' + lightCount.toString();
light.baseName = lightBaseName;
- switch(lightType) {
+ switch (lightType) {
case 'ambient':
var ambient = light.ambient;
result[lightBaseName + 'Color'] = {
@@ -75,14 +158,12 @@ define([
break;
case 'directional':
var directional = light.directional;
- result[lightBaseName + 'Color'] =
- {
+ result[lightBaseName + 'Color'] = {
type: WebGLConstants.FLOAT_VEC3,
value: directional.color
};
if (defined(light.node)) {
- result[lightBaseName + 'Transform'] =
- {
+ result[lightBaseName + 'Transform'] = {
node: light.node,
semantic: 'MODELVIEW',
type: WebGLConstants.FLOAT_MAT4
@@ -91,35 +172,30 @@ define([
break;
case 'point':
var point = light.point;
- result[lightBaseName + 'Color'] =
- {
+ result[lightBaseName + 'Color'] = {
type: WebGLConstants.FLOAT_VEC3,
value: point.color
};
if (defined(light.node)) {
- result[lightBaseName + 'Transform'] =
- {
+ result[lightBaseName + 'Transform'] = {
node: light.node,
semantic: 'MODELVIEW',
type: WebGLConstants.FLOAT_MAT4
};
}
- result[lightBaseName + 'Attenuation'] =
- {
+ result[lightBaseName + 'Attenuation'] = {
type: WebGLConstants.FLOAT_VEC3,
value: [point.constantAttenuation, point.linearAttenuation, point.quadraticAttenuation]
};
break;
case 'spot':
var spot = light.spot;
- result[lightBaseName + 'Color'] =
- {
+ result[lightBaseName + 'Color'] = {
type: WebGLConstants.FLOAT_VEC3,
value: spot.color
};
if (defined(light.node)) {
- result[lightBaseName + 'Transform'] =
- {
+ result[lightBaseName + 'Transform'] = {
node: light.node,
semantic: 'MODELVIEW',
type: WebGLConstants.FLOAT_MAT4
@@ -131,14 +207,12 @@ define([
useInFragment: true
};
}
- result[lightBaseName + 'Attenuation'] =
- {
+ result[lightBaseName + 'Attenuation'] = {
type: WebGLConstants.FLOAT_VEC3,
value: [spot.constantAttenuation, spot.linearAttenuation, spot.quadraticAttenuation]
};
- result[lightBaseName + 'FallOff'] =
- {
+ result[lightBaseName + 'FallOff'] = {
type: WebGLConstants.FLOAT_VEC2,
value: [spot.fallOffAngle, spot.fallOffExponent]
};
@@ -152,21 +226,11 @@ define([
return result;
}
- function getNextId(dictionary, baseName, startingCount) {
- var count = defaultValue(startingCount, 0);
- var nextId;
- do {
- nextId = baseName + (count++).toString();
- } while(defined(dictionary[nextId]));
-
- return nextId;
- }
-
- var techniqueCount = 0;
- var vertexShaderCount = 0;
- var fragmentShaderCount = 0;
- var programCount = 0;
function generateTechnique(gltf, khrMaterialsCommon, lightParameters, options) {
+ var optimizeForCesium = defaultValue(options.optimizeForCesium, false);
+ var hasCesiumRTCExtension = defined(gltf.extensions) && defined(gltf.extensions.CESIUM_RTC);
+ var addBatchIdToGeneratedShaders = defaultValue(options.addBatchIdToGeneratedShaders, false);
+
var techniques = gltf.techniques;
var shaders = gltf.shaders;
var programs = gltf.programs;
@@ -175,26 +239,31 @@ define([
if (defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common)) {
lights = gltf.extensions.KHR_materials_common.lights;
}
- var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0);
- var hasSkinning = (jointCount > 0);
var parameterValues = khrMaterialsCommon.values;
+ if (defined(khrMaterialsCommon.transparent)) {
+ parameterValues.transparent = khrMaterialsCommon.transparent;
+ }
+ if (defined(khrMaterialsCommon.doubleSided)) {
+ parameterValues.doubleSided = khrMaterialsCommon.doubleSided;
+ }
+ var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0);
+
+ var hasSkinning = jointCount > 0;
+ var skinningInfo = {};
+ if (hasSkinning) {
+ skinningInfo = khrMaterialsCommon.extras._pipeline.skinning;
+ }
var vertexShader = 'precision highp float;\n';
var fragmentShader = 'precision highp float;\n';
- // Generate IDs for our new objects
- var techniqueId = getNextId(techniques, 'technique', techniqueCount);
- var vertexShaderId = getNextId(shaders, 'vertexShader', vertexShaderCount);
- var fragmentShaderId = getNextId(shaders, 'fragmentShader', fragmentShaderCount);
- var programId = getNextId(programs, 'program', programCount);
-
var hasNormals = (lightingModel !== 'CONSTANT');
// Add techniques
var techniqueParameters = {
// Add matrices
modelViewMatrix: {
- semantic: options.useCesiumRTCMatrixInShaders ? 'CESIUM_RTC_MODELVIEW' : 'MODELVIEW',
+ semantic: hasCesiumRTCExtension ? 'CESIUM_RTC_MODELVIEW' : 'MODELVIEW',
type: WebGLConstants.FLOAT_MAT4
},
projectionMatrix: {
@@ -221,7 +290,7 @@ define([
// Add material parameters
var lowerCase;
var hasTexCoords = false;
- for(var name in parameterValues) {
+ for (var name in parameterValues) {
//generate shader parameters for KHR_materials_common attributes
//(including a check, because some boolean flags should not be used as shader parameters)
if (parameterValues.hasOwnProperty(name) && (name !== 'transparent') && (name !== 'doubleSided')) {
@@ -237,7 +306,7 @@ define([
}
// Give the diffuse uniform a semantic to support color replacement in 3D Tiles
- if (defined(techniqueParameters.diffuse)) {
+ if (defined(techniqueParameters.diffuse) && optimizeForCesium) {
techniqueParameters.diffuse.semantic = '_3DTILESDIFFUSE';
}
@@ -253,16 +322,15 @@ define([
// Generate uniforms object before attributes are added
var techniqueUniforms = {};
for (var paramName in techniqueParameters) {
- if (techniqueParameters.hasOwnProperty(paramName)) {
+ if (techniqueParameters.hasOwnProperty(paramName) && paramName !== 'extras') {
var param = techniqueParameters[paramName];
techniqueUniforms['u_' + paramName] = paramName;
- var arraySize = defined(param.count) ? '['+param.count+']' : '';
+ var arraySize = defined(param.count) ? '[' + param.count + ']' : '';
if (((param.type !== WebGLConstants.FLOAT_MAT3) && (param.type !== WebGLConstants.FLOAT_MAT4)) ||
param.useInFragment) {
fragmentShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n';
delete param.useInFragment;
- }
- else {
+ } else {
vertexShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n';
}
}
@@ -271,10 +339,34 @@ define([
// Add attributes with semantics
var vertexShaderMain = '';
if (hasSkinning) {
- vertexShaderMain += ' mat4 skinMat = a_weight.x * u_jointMatrix[int(a_joint.x)];\n';
- vertexShaderMain += ' skinMat += a_weight.y * u_jointMatrix[int(a_joint.y)];\n';
- vertexShaderMain += ' skinMat += a_weight.z * u_jointMatrix[int(a_joint.z)];\n';
- vertexShaderMain += ' skinMat += a_weight.w * u_jointMatrix[int(a_joint.w)];\n';
+ var i, j;
+ var numberOfComponents = numberOfComponentsForType(skinningInfo.type);
+ var matrix = false;
+ if (skinningInfo.type.indexOf('MAT') === 0) {
+ matrix = true;
+ numberOfComponents = Math.sqrt(numberOfComponents);
+ }
+ if (!matrix) {
+ for (i = 0; i < numberOfComponents; i++) {
+ if (i === 0) {
+ vertexShaderMain += ' mat4 skinMat = ';
+ } else {
+ vertexShaderMain += ' skinMat += ';
+ }
+ vertexShaderMain += 'a_weight[' + i + '] * u_jointMatrix[int(a_joint[' + i + '])];\n';
+ }
+ } else {
+ for (i = 0; i < numberOfComponents; i++) {
+ for (j = 0; j < numberOfComponents; j++) {
+ if (i === 0 && j === 0) {
+ vertexShaderMain += ' mat4 skinMat = ';
+ } else {
+ vertexShaderMain += ' skinMat += ';
+ }
+ vertexShaderMain += 'a_weight[' + i + '][' + j + '] * u_jointMatrix[int(a_joint[' + i + '][' + j + '])];\n';
+ }
+ }
+ }
}
// Add position always
@@ -289,8 +381,7 @@ define([
vertexShader += 'varying vec3 v_positionEC;\n';
if (hasSkinning) {
vertexShaderMain += ' vec4 pos = u_modelViewMatrix * skinMat * vec4(a_position,1.0);\n';
- }
- else {
+ } else {
vertexShaderMain += ' vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);\n';
}
vertexShaderMain += ' v_positionEC = pos.xyz;\n';
@@ -308,8 +399,7 @@ define([
vertexShader += 'varying vec3 v_normal;\n';
if (hasSkinning) {
vertexShaderMain += ' v_normal = u_normalMatrix * mat3(skinMat) * a_normal;\n';
- }
- else {
+ } else {
vertexShaderMain += ' v_normal = u_normalMatrix * a_normal;\n';
}
@@ -335,21 +425,24 @@ define([
if (hasSkinning) {
techniqueAttributes.a_joint = 'joint';
+ var attributeType = getShaderVariable(skinningInfo.type);
+ var webGLConstant = glslTypeToWebGLConstant(attributeType);
+
techniqueParameters.joint = {
semantic: 'JOINT',
- type: WebGLConstants.FLOAT_VEC4
+ type: webGLConstant
};
techniqueAttributes.a_weight = 'weight';
techniqueParameters.weight = {
semantic: 'WEIGHT',
- type: WebGLConstants.FLOAT_VEC4
+ type: webGLConstant
};
- vertexShader += 'attribute vec4 a_joint;\n';
- vertexShader += 'attribute vec4 a_weight;\n';
+ vertexShader += 'attribute ' + attributeType + ' a_joint;\n';
+ vertexShader += 'attribute ' + attributeType + ' a_weight;\n';
}
- if (options.addBatchIdToGeneratedShaders) {
+ if (addBatchIdToGeneratedShaders) {
techniqueAttributes.a_batchId = 'batchId';
techniqueParameters.batchId = {
semantic: '_BATCHID',
@@ -359,7 +452,7 @@ define([
}
var hasSpecular = hasNormals && ((lightingModel === 'BLINN') || (lightingModel === 'PHONG')) &&
- defined(techniqueParameters.specular) && defined(techniqueParameters.shininess);
+ defined(techniqueParameters.specular) && defined(techniqueParameters.shininess);
// Generate lighting code blocks
var hasNonAmbientLights = false;
@@ -374,11 +467,10 @@ define([
var lightColorName = 'u_' + lightBaseName + 'Color';
var varyingDirectionName;
var varyingPositionName;
- if(lightType === 'ambient') {
+ if (lightType === 'ambient') {
hasAmbientLights = true;
fragmentLightingBlock += ' ambientLight += ' + lightColorName + ';\n';
- }
- else if (hasNormals) {
+ } else if (hasNormals) {
hasNonAmbientLights = true;
varyingDirectionName = 'v_' + lightBaseName + 'Direction';
varyingPositionName = 'v_' + lightBaseName + 'Position';
@@ -404,8 +496,7 @@ define([
fragmentLightingBlock += ' float attenuation = 1.0 / (u_' + lightBaseName + 'Attenuation.x + ';
fragmentLightingBlock += '(u_' + lightBaseName + 'Attenuation.y * range) + ';
fragmentLightingBlock += '(u_' + lightBaseName + 'Attenuation.z * range * range));\n';
- }
- else {
+ } else {
fragmentLightingBlock += ' float attenuation = 1.0;\n';
}
@@ -427,8 +518,7 @@ define([
if (lightingModel === 'BLINN') {
fragmentLightingBlock += ' vec3 h = normalize(l + viewDir);\n';
fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess)) * attenuation;\n';
- }
- else { // PHONG
+ } else { // PHONG
fragmentLightingBlock += ' vec3 reflectDir = reflect(-l, normal);\n';
fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)) * attenuation;\n';
}
@@ -445,15 +535,18 @@ define([
}
if (!hasNonAmbientLights && (lightingModel !== 'CONSTANT')) {
- fragmentLightingBlock += ' vec3 l = normalize(czm_sunDirectionEC);\n';
+ if (optimizeForCesium) {
+ fragmentLightingBlock += ' vec3 l = normalize(czm_sunDirectionEC);\n';
+ } else {
+ fragmentLightingBlock += ' vec3 l = vec3(0.0, 0.0, 1.0);\n';
+ }
fragmentLightingBlock += ' diffuseLight += vec3(1.0, 1.0, 1.0) * max(dot(normal,l), 0.);\n';
if (hasSpecular) {
if (lightingModel === 'BLINN') {
fragmentLightingBlock += ' vec3 h = normalize(l + viewDir);\n';
fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess));\n';
- }
- else { // PHONG
+ } else { // PHONG
fragmentLightingBlock += ' vec3 reflectDir = reflect(-l, normal);\n';
fragmentLightingBlock += ' float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));\n';
}
@@ -483,8 +576,7 @@ define([
if (defined(techniqueParameters.diffuse)) {
if (techniqueParameters.diffuse.type === WebGLConstants.SAMPLER_2D) {
fragmentShader += ' vec4 diffuse = texture2D(u_diffuse, ' + v_texcoord + ');\n';
- }
- else {
+ } else {
fragmentShader += ' vec4 diffuse = u_diffuse;\n';
}
fragmentShader += ' vec3 diffuseLight = vec3(0.0, 0.0, 0.0);\n';
@@ -494,8 +586,7 @@ define([
if (hasSpecular) {
if (techniqueParameters.specular.type === WebGLConstants.SAMPLER_2D) {
fragmentShader += ' vec3 specular = texture2D(u_specular, ' + v_texcoord + ').rgb;\n';
- }
- else {
+ } else {
fragmentShader += ' vec3 specular = u_specular.rgb;\n';
}
fragmentShader += ' vec3 specularLight = vec3(0.0, 0.0, 0.0);\n';
@@ -503,24 +594,22 @@ define([
}
if (defined(techniqueParameters.transparency)) {
- finalColorComputation = ' gl_FragColor = vec4(color * diffuse.a, diffuse.a * u_transparency);\n';
- }
- else {
+ finalColorComputation = ' gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency);\n';
+ } else {
finalColorComputation = ' gl_FragColor = vec4(color * diffuse.a, diffuse.a);\n';
}
- }
- else if (defined(techniqueParameters.transparency)) {
- finalColorComputation = ' gl_FragColor = vec4(color, u_transparency);\n';
- }
- else {
- finalColorComputation = ' gl_FragColor = vec4(color, 1.0);\n';
+ } else {
+ if (defined(techniqueParameters.transparency)) {
+ finalColorComputation = ' gl_FragColor = vec4(color * u_transparency, u_transparency);\n';
+ } else {
+ finalColorComputation = ' gl_FragColor = vec4(color, 1.0);\n';
+ }
}
if (defined(techniqueParameters.emission)) {
if (techniqueParameters.emission.type === WebGLConstants.SAMPLER_2D) {
fragmentShader += ' vec3 emission = texture2D(u_emission, ' + v_texcoord + ').rgb;\n';
- }
- else {
+ } else {
fragmentShader += ' vec3 emission = u_emission.rgb;\n';
}
colorCreationBlock += ' color += emission;\n';
@@ -530,12 +619,10 @@ define([
if (defined(techniqueParameters.ambient)) {
if (techniqueParameters.ambient.type === WebGLConstants.SAMPLER_2D) {
fragmentShader += ' vec3 ambient = texture2D(u_ambient, ' + v_texcoord + ').rgb;\n';
- }
- else {
+ } else {
fragmentShader += ' vec3 ambient = u_ambient.rgb;\n';
}
- }
- else {
+ } else {
fragmentShader += ' vec3 ambient = diffuse.rgb;\n';
}
colorCreationBlock += ' color += ambient * ambientLight;\n';
@@ -551,14 +638,14 @@ define([
fragmentShader += '}\n';
var techniqueStates;
- if (khrMaterialsCommon.transparent) {
+ if (parameterValues.transparent) {
techniqueStates = {
enable: [
WebGLConstants.DEPTH_TEST,
WebGLConstants.BLEND
],
- depthMask: false,
functions: {
+ depthMask : [false],
blendEquationSeparate: [
WebGLConstants.FUNC_ADD,
WebGLConstants.FUNC_ADD
@@ -571,15 +658,13 @@ define([
]
}
};
- }
- else if (khrMaterialsCommon.doubleSided) {
+ } else if (khrMaterialsCommon.doubleSided) {
techniqueStates = {
enable: [
WebGLConstants.DEPTH_TEST
]
};
- }
- else { // Not transparent or double sided
+ } else { // Not transparent or double sided
techniqueStates = {
enable: [
WebGLConstants.CULL_FACE,
@@ -587,62 +672,69 @@ define([
]
};
}
- techniques[techniqueId] = {
- attributes: techniqueAttributes,
- parameters: techniqueParameters,
- program: programId,
- states: techniqueStates,
- uniforms: techniqueUniforms
- };
// Add shaders
- shaders[vertexShaderId] = {
+ var vertexShaderId = addToArray(shaders, {
type: WebGLConstants.VERTEX_SHADER,
- uri: '',
- extras: {
- source: vertexShader
+ extras: {
+ _pipeline: {
+ source: vertexShader,
+ extension: '.glsl'
+ }
}
- };
- shaders[fragmentShaderId] = {
+ });
+
+ var fragmentShaderId = addToArray(shaders, {
type: WebGLConstants.FRAGMENT_SHADER,
- uri: '',
extras: {
- source: fragmentShader
+ _pipeline: {
+ source: fragmentShader,
+ extension: '.glsl'
+ }
}
- };
+ });
// Add program
var programAttributes = Object.keys(techniqueAttributes);
- programs[programId] = {
+ var programId = addToArray(programs, {
attributes: programAttributes,
fragmentShader: fragmentShaderId,
vertexShader: vertexShaderId
- };
+ });
+
+ var techniqueId = addToArray(techniques, {
+ attributes: techniqueAttributes,
+ parameters: techniqueParameters,
+ program: programId,
+ states: techniqueStates,
+ uniforms: techniqueUniforms
+ });
return techniqueId;
}
- function getKHRMaterialsCommonValueType(paramName, paramValue)
- {
+ function getKHRMaterialsCommonValueType(paramName, paramValue) {
var value;
// Backwards compatibility for COLLADA2GLTF v1.0-draft when it encoding
// materials using KHR_materials_common with explicit type/value members
if (defined(paramValue.value)) {
value = paramValue.value;
+ } else if (defined(paramValue.index)) {
+ value = [paramValue.index];
} else {
value = paramValue;
}
- switch (paramName) {
+ switch (paramName) {
case 'ambient':
- return (value instanceof String || typeof value === 'string') ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
+ return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
case 'diffuse':
- return (value instanceof String || typeof value === 'string') ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
+ return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
case 'emission':
- return (value instanceof String || typeof value === 'string') ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
+ return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
case 'specular':
- return (value instanceof String || typeof value === 'string') ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
+ return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4;
case 'shininess':
return WebGLConstants.FLOAT;
case 'transparency':
@@ -664,116 +756,193 @@ define([
var values = khrMaterialsCommon.values;
var keys = Object.keys(values).sort();
var keysCount = keys.length;
- for (var i=0;i