cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
524 lines (496 loc) • 19.2 kB
JavaScript
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);
}
}
}
}
}
function getAnimatedNodes(gltf) {
var nodes = {};
ForEach.animation(gltf, function(animation) {
ForEach.animationChannel(animation, function(channel) {
var target = channel.target;
var nodeId = target.node;
var path = target.path;
// Ignore animations that target 'weights'
if (path === 'translation' || path === 'rotation' || path === 'scale') {
nodes[nodeId] = true;
}
});
});
return nodes;
}
function addDefaultTransformToAnimatedNodes(gltf) {
var animatedNodes = getAnimatedNodes(gltf);
ForEach.node(gltf, function(node, id) {
if (defined(animatedNodes[id])) {
delete node.matrix;
node.translation = defaultValue(node.translation, [0.0, 0.0, 0.0]);
node.rotation = defaultValue(node.rotation, [0.0, 0.0, 0.0, 1.0]);
node.scale = defaultValue(node.scale, [1.0, 1.0, 1.0]);
}
});
}
var defaultMaterial = {
values : {
emission : [
0.5, 0.5, 0.5, 1.0
]
},
extras : {
_pipeline: {}
}
};
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) {
if (defined(gltf.scenes) && !defined(gltf.scene)) {
gltf.scene = 0;
}
}
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);
addDefaultTransformToAnimatedNodes(gltf);
addDefaultMaterial(gltf);
addDefaultTechnique(gltf);
addDefaultByteOffsets(gltf);
selectDefaultScene(gltf);
inferBufferViewTargets(gltf);
if (options.optimizeForCesium) {
optimizeForCesium(gltf);
}
return gltf;
}
return addDefaults;
});