import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import WebGLConstants from "../Core/WebGLConstants.js"; import webGLConstantToGlslType from "../Core/webGLConstantToGlslType.js"; import addToArray from "../ThirdParty/GltfPipeline/addToArray.js"; import ForEach from "../ThirdParty/GltfPipeline/ForEach.js"; import hasExtension from "../ThirdParty/GltfPipeline/hasExtension.js"; import ModelUtility from "./ModelUtility.js"; /** * @private */ function processModelMaterialsCommon(gltf, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); if (!defined(gltf)) { return; } if (!hasExtension(gltf, "KHR_materials_common")) { return; } if (!hasExtension(gltf, "KHR_techniques_webgl")) { if (!defined(gltf.extensions)) { gltf.extensions = {}; } gltf.extensions.KHR_techniques_webgl = { programs: [], shaders: [], techniques: [], }; gltf.extensionsUsed.push("KHR_techniques_webgl"); gltf.extensionsRequired.push("KHR_techniques_webgl"); } var techniquesWebgl = gltf.extensions.KHR_techniques_webgl; lightDefaults(gltf); var lightParameters = generateLightParameters(gltf); var primitiveByMaterial = ModelUtility.splitIncompatibleMaterials(gltf); var techniques = {}; var generatedTechniques = false; ForEach.material(gltf, function (material, materialIndex) { if ( defined(material.extensions) && defined(material.extensions.KHR_materials_common) ) { var khrMaterialsCommon = material.extensions.KHR_materials_common; var primitiveInfo = primitiveByMaterial[materialIndex]; var techniqueKey = getTechniqueKey(khrMaterialsCommon, primitiveInfo); var technique = techniques[techniqueKey]; if (!defined(technique)) { technique = generateTechnique( gltf, techniquesWebgl, primitiveInfo, khrMaterialsCommon, lightParameters, options.addBatchIdToGeneratedShaders ); techniques[techniqueKey] = technique; generatedTechniques = true; } var materialValues = {}; var values = khrMaterialsCommon.values; var uniformName; for (var valueName in values) { if ( values.hasOwnProperty(valueName) && valueName !== "transparent" && valueName !== "doubleSided" ) { uniformName = "u_" + valueName.toLowerCase(); materialValues[uniformName] = values[valueName]; } } material.extensions.KHR_techniques_webgl = { technique: technique, values: materialValues, }; material.alphaMode = "OPAQUE"; if (khrMaterialsCommon.transparent) { material.alphaMode = "BLEND"; } if (khrMaterialsCommon.doubleSided) { material.doubleSided = true; } } }); if (!generatedTechniques) { return gltf; } // If any primitives have semantics that aren't declared in the generated // shaders, we want to preserve them. ModelUtility.ensureSemanticExistence(gltf); return gltf; } function generateLightParameters(gltf) { var result = {}; var lights; if ( defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common) ) { lights = gltf.extensions.KHR_materials_common.lights; } if (defined(lights)) { // Figure out which node references the light var nodes = gltf.nodes; for (var nodeName in nodes) { if (nodes.hasOwnProperty(nodeName)) { var node = nodes[nodeName]; if ( defined(node.extensions) && defined(node.extensions.KHR_materials_common) ) { var nodeLightId = node.extensions.KHR_materials_common.light; if (defined(nodeLightId) && defined(lights[nodeLightId])) { lights[nodeLightId].node = nodeName; } delete node.extensions.KHR_materials_common; } } } // Add light parameters to result var lightCount = 0; for (var lightName in lights) { if (lights.hasOwnProperty(lightName)) { var light = lights[lightName]; var lightType = light.type; if (lightType !== "ambient" && !defined(light.node)) { delete lights[lightName]; continue; } var lightBaseName = "light" + lightCount.toString(); light.baseName = lightBaseName; switch (lightType) { case "ambient": var ambient = light.ambient; result[lightBaseName + "Color"] = { type: WebGLConstants.FLOAT_VEC3, value: ambient.color, }; break; case "directional": var directional = light.directional; result[lightBaseName + "Color"] = { type: WebGLConstants.FLOAT_VEC3, value: directional.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants.FLOAT_MAT4, }; } break; case "point": var point = light.point; result[lightBaseName + "Color"] = { type: WebGLConstants.FLOAT_VEC3, value: point.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants.FLOAT_MAT4, }; } result[lightBaseName + "Attenuation"] = { type: WebGLConstants.FLOAT_VEC3, value: [ point.constantAttenuation, point.linearAttenuation, point.quadraticAttenuation, ], }; break; case "spot": var spot = light.spot; result[lightBaseName + "Color"] = { type: WebGLConstants.FLOAT_VEC3, value: spot.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants.FLOAT_MAT4, }; result[lightBaseName + "InverseTransform"] = { node: light.node, semantic: "MODELVIEWINVERSE", type: WebGLConstants.FLOAT_MAT4, useInFragment: true, }; } result[lightBaseName + "Attenuation"] = { type: WebGLConstants.FLOAT_VEC3, value: [ spot.constantAttenuation, spot.linearAttenuation, spot.quadraticAttenuation, ], }; result[lightBaseName + "FallOff"] = { type: WebGLConstants.FLOAT_VEC2, value: [spot.fallOffAngle, spot.fallOffExponent], }; break; } ++lightCount; } } } return result; } function generateTechnique( gltf, techniquesWebgl, primitiveInfo, khrMaterialsCommon, lightParameters, addBatchIdToGeneratedShaders ) { if (!defined(khrMaterialsCommon)) { khrMaterialsCommon = {}; } addBatchIdToGeneratedShaders = defaultValue( addBatchIdToGeneratedShaders, false ); var techniques = techniquesWebgl.techniques; var shaders = techniquesWebgl.shaders; var programs = techniquesWebgl.programs; var lightingModel = khrMaterialsCommon.technique.toUpperCase(); var lights; if ( defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common) ) { lights = gltf.extensions.KHR_materials_common.lights; } var parameterValues = khrMaterialsCommon.values; var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); var skinningInfo; var hasSkinning = false; var hasVertexColors = false; if (defined(primitiveInfo)) { skinningInfo = primitiveInfo.skinning; hasSkinning = skinningInfo.skinned; hasVertexColors = primitiveInfo.hasVertexColors; } var vertexShader = "precision highp float;\n"; var fragmentShader = "precision highp float;\n"; var hasNormals = lightingModel !== "CONSTANT"; // Add techniques var techniqueUniforms = { u_modelViewMatrix: { semantic: hasExtension(gltf, "CESIUM_RTC") ? "CESIUM_RTC_MODELVIEW" : "MODELVIEW", type: WebGLConstants.FLOAT_MAT4, }, u_projectionMatrix: { semantic: "PROJECTION", type: WebGLConstants.FLOAT_MAT4, }, }; if (hasNormals) { techniqueUniforms.u_normalMatrix = { semantic: "MODELVIEWINVERSETRANSPOSE", type: WebGLConstants.FLOAT_MAT3, }; } if (hasSkinning) { techniqueUniforms.u_jointMatrix = { count: jointCount, semantic: "JOINTMATRIX", type: WebGLConstants.FLOAT_MAT4, }; } // Add material values var uniformName; var hasTexCoords = false; 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" ) { var uniformType = getKHRMaterialsCommonValueType( name, parameterValues[name] ); uniformName = "u_" + name.toLowerCase(); if (!hasTexCoords && uniformType === WebGLConstants.SAMPLER_2D) { hasTexCoords = true; } techniqueUniforms[uniformName] = { type: uniformType, }; } } // Give the diffuse uniform a semantic to support color replacement in 3D Tiles if (defined(techniqueUniforms.u_diffuse)) { techniqueUniforms.u_diffuse.semantic = "_3DTILESDIFFUSE"; } // Copy light parameters into technique parameters if (defined(lightParameters)) { for (var lightParamName in lightParameters) { if (lightParameters.hasOwnProperty(lightParamName)) { uniformName = "u_" + lightParamName; techniqueUniforms[uniformName] = lightParameters[lightParamName]; } } } // Add uniforms to shaders for (uniformName in techniqueUniforms) { if (techniqueUniforms.hasOwnProperty(uniformName)) { var uniform = techniqueUniforms[uniformName]; var arraySize = defined(uniform.count) ? "[" + uniform.count + "]" : ""; if ( (uniform.type !== WebGLConstants.FLOAT_MAT3 && uniform.type !== WebGLConstants.FLOAT_MAT4) || uniform.useInFragment ) { fragmentShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; delete uniform.useInFragment; } else { vertexShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; } } } // Add attributes with semantics var vertexShaderMain = ""; if (hasSkinning) { vertexShaderMain += " mat4 skinMatrix =\n" + " a_weight.x * u_jointMatrix[int(a_joint.x)] +\n" + " a_weight.y * u_jointMatrix[int(a_joint.y)] +\n" + " a_weight.z * u_jointMatrix[int(a_joint.z)] +\n" + " a_weight.w * u_jointMatrix[int(a_joint.w)];\n"; } // Add position always var techniqueAttributes = { a_position: { semantic: "POSITION", }, }; vertexShader += "attribute vec3 a_position;\n"; vertexShader += "varying vec3 v_positionEC;\n"; if (hasSkinning) { vertexShaderMain += " vec4 pos = u_modelViewMatrix * skinMatrix * vec4(a_position,1.0);\n"; } else { vertexShaderMain += " vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);\n"; } vertexShaderMain += " v_positionEC = pos.xyz;\n"; vertexShaderMain += " gl_Position = u_projectionMatrix * pos;\n"; fragmentShader += "varying vec3 v_positionEC;\n"; // Add normal if we don't have constant lighting if (hasNormals) { techniqueAttributes.a_normal = { semantic: "NORMAL", }; vertexShader += "attribute vec3 a_normal;\n"; vertexShader += "varying vec3 v_normal;\n"; if (hasSkinning) { vertexShaderMain += " v_normal = u_normalMatrix * mat3(skinMatrix) * a_normal;\n"; } else { vertexShaderMain += " v_normal = u_normalMatrix * a_normal;\n"; } fragmentShader += "varying vec3 v_normal;\n"; } // Add texture coordinates if the material uses them var v_texcoord; if (hasTexCoords) { techniqueAttributes.a_texcoord_0 = { semantic: "TEXCOORD_0", }; v_texcoord = "v_texcoord_0"; vertexShader += "attribute vec2 a_texcoord_0;\n"; vertexShader += "varying vec2 " + v_texcoord + ";\n"; vertexShaderMain += " " + v_texcoord + " = a_texcoord_0;\n"; fragmentShader += "varying vec2 " + v_texcoord + ";\n"; } if (hasSkinning) { techniqueAttributes.a_joint = { semantic: "JOINTS_0", }; techniqueAttributes.a_weight = { semantic: "WEIGHTS_0", }; vertexShader += "attribute vec4 a_joint;\n"; vertexShader += "attribute vec4 a_weight;\n"; } if (hasVertexColors) { techniqueAttributes.a_vertexColor = { semantic: "COLOR_0", }; vertexShader += "attribute vec4 a_vertexColor;\n"; vertexShader += "varying vec4 v_vertexColor;\n"; vertexShaderMain += " v_vertexColor = a_vertexColor;\n"; fragmentShader += "varying vec4 v_vertexColor;\n"; } if (addBatchIdToGeneratedShaders) { techniqueAttributes.a_batchId = { semantic: "_BATCHID", }; vertexShader += "attribute float a_batchId;\n"; } var hasSpecular = hasNormals && (lightingModel === "BLINN" || lightingModel === "PHONG") && defined(techniqueUniforms.u_specular) && defined(techniqueUniforms.u_shininess) && techniqueUniforms.u_shininess > 0.0; // Generate lighting code blocks var hasNonAmbientLights = false; var hasAmbientLights = false; var fragmentLightingBlock = ""; for (var lightName in lights) { if (lights.hasOwnProperty(lightName)) { var light = lights[lightName]; var lightType = light.type.toLowerCase(); var lightBaseName = light.baseName; fragmentLightingBlock += " {\n"; var lightColorName = "u_" + lightBaseName + "Color"; var varyingDirectionName; var varyingPositionName; if (lightType === "ambient") { hasAmbientLights = true; fragmentLightingBlock += " ambientLight += " + lightColorName + ";\n"; } else if (hasNormals) { hasNonAmbientLights = true; varyingDirectionName = "v_" + lightBaseName + "Direction"; varyingPositionName = "v_" + lightBaseName + "Position"; if (lightType !== "point") { vertexShader += "varying vec3 " + varyingDirectionName + ";\n"; fragmentShader += "varying vec3 " + varyingDirectionName + ";\n"; vertexShaderMain += " " + varyingDirectionName + " = mat3(u_" + lightBaseName + "Transform) * vec3(0.,0.,1.);\n"; if (lightType === "directional") { fragmentLightingBlock += " vec3 l = normalize(" + varyingDirectionName + ");\n"; } } if (lightType !== "directional") { vertexShader += "varying vec3 " + varyingPositionName + ";\n"; fragmentShader += "varying vec3 " + varyingPositionName + ";\n"; vertexShaderMain += " " + varyingPositionName + " = u_" + lightBaseName + "Transform[3].xyz;\n"; fragmentLightingBlock += " vec3 VP = " + varyingPositionName + " - v_positionEC;\n"; fragmentLightingBlock += " vec3 l = normalize(VP);\n"; fragmentLightingBlock += " float range = length(VP);\n"; fragmentLightingBlock += " float attenuation = 1.0 / (u_" + lightBaseName + "Attenuation.x + "; fragmentLightingBlock += "(u_" + lightBaseName + "Attenuation.y * range) + "; fragmentLightingBlock += "(u_" + lightBaseName + "Attenuation.z * range * range));\n"; } else { fragmentLightingBlock += " float attenuation = 1.0;\n"; } if (lightType === "spot") { fragmentLightingBlock += " float spotDot = dot(l, normalize(" + varyingDirectionName + "));\n"; fragmentLightingBlock += " if (spotDot < cos(u_" + lightBaseName + "FallOff.x * 0.5))\n"; fragmentLightingBlock += " {\n"; fragmentLightingBlock += " attenuation = 0.0;\n"; fragmentLightingBlock += " }\n"; fragmentLightingBlock += " else\n"; fragmentLightingBlock += " {\n"; fragmentLightingBlock += " attenuation *= max(0.0, pow(spotDot, u_" + lightBaseName + "FallOff.y));\n"; fragmentLightingBlock += " }\n"; } fragmentLightingBlock += " diffuseLight += " + lightColorName + "* max(dot(normal,l), 0.) * attenuation;\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)) * attenuation;\n"; } else { // PHONG fragmentLightingBlock += " vec3 reflectDir = reflect(-l, normal);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)) * attenuation;\n"; } fragmentLightingBlock += " specularLight += " + lightColorName + " * specularIntensity;\n"; } } fragmentLightingBlock += " }\n"; } } if (!hasAmbientLights) { // Add an ambient light if we don't have one fragmentLightingBlock += " ambientLight += vec3(0.2, 0.2, 0.2);\n"; } if (!hasNonAmbientLights && lightingModel !== "CONSTANT") { fragmentShader += "#ifdef USE_CUSTOM_LIGHT_COLOR \n"; fragmentShader += "uniform vec3 gltf_lightColor; \n"; fragmentShader += "#endif \n"; fragmentLightingBlock += "#ifndef USE_CUSTOM_LIGHT_COLOR \n"; fragmentLightingBlock += " vec3 lightColor = czm_lightColor;\n"; fragmentLightingBlock += "#else \n"; fragmentLightingBlock += " vec3 lightColor = gltf_lightColor;\n"; fragmentLightingBlock += "#endif \n"; fragmentLightingBlock += " vec3 l = normalize(czm_lightDirectionEC);\n"; var minimumLighting = "0.2"; // Use strings instead of values as 0.0 -> 0 when stringified fragmentLightingBlock += " diffuseLight += lightColor * max(dot(normal,l), " + minimumLighting + ");\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 fragmentLightingBlock += " vec3 reflectDir = reflect(-l, normal);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));\n"; } fragmentLightingBlock += " specularLight += lightColor * specularIntensity;\n"; } } vertexShader += "void main(void) {\n"; vertexShader += vertexShaderMain; vertexShader += "}\n"; fragmentShader += "void main(void) {\n"; var colorCreationBlock = " vec3 color = vec3(0.0, 0.0, 0.0);\n"; if (hasNormals) { fragmentShader += " vec3 normal = normalize(v_normal);\n"; if (khrMaterialsCommon.doubleSided) { fragmentShader += " if (czm_backFacing())\n"; fragmentShader += " {\n"; fragmentShader += " normal = -normal;\n"; fragmentShader += " }\n"; } } var finalColorComputation; if (lightingModel !== "CONSTANT") { if (defined(techniqueUniforms.u_diffuse)) { if (techniqueUniforms.u_diffuse.type === WebGLConstants.SAMPLER_2D) { fragmentShader += " vec4 diffuse = texture2D(u_diffuse, " + v_texcoord + ");\n"; } else { fragmentShader += " vec4 diffuse = u_diffuse;\n"; } fragmentShader += " vec3 diffuseLight = vec3(0.0, 0.0, 0.0);\n"; colorCreationBlock += " color += diffuse.rgb * diffuseLight;\n"; } if (hasSpecular) { if (techniqueUniforms.u_specular.type === WebGLConstants.SAMPLER_2D) { fragmentShader += " vec3 specular = texture2D(u_specular, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 specular = u_specular.rgb;\n"; } fragmentShader += " vec3 specularLight = vec3(0.0, 0.0, 0.0);\n"; colorCreationBlock += " color += specular * specularLight;\n"; } if (defined(techniqueUniforms.u_transparency)) { 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(techniqueUniforms.u_transparency)) { finalColorComputation = " gl_FragColor = vec4(color * u_transparency, u_transparency);\n"; } else { finalColorComputation = " gl_FragColor = vec4(color, 1.0);\n"; } if (hasVertexColors) { colorCreationBlock += " color *= v_vertexColor.rgb;\n"; } if (defined(techniqueUniforms.u_emission)) { if (techniqueUniforms.u_emission.type === WebGLConstants.SAMPLER_2D) { fragmentShader += " vec3 emission = texture2D(u_emission, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 emission = u_emission.rgb;\n"; } colorCreationBlock += " color += emission;\n"; } if (defined(techniqueUniforms.u_ambient) || lightingModel !== "CONSTANT") { if (defined(techniqueUniforms.u_ambient)) { if (techniqueUniforms.u_ambient.type === WebGLConstants.SAMPLER_2D) { fragmentShader += " vec3 ambient = texture2D(u_ambient, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 ambient = u_ambient.rgb;\n"; } } else { fragmentShader += " vec3 ambient = diffuse.rgb;\n"; } colorCreationBlock += " color += ambient * ambientLight;\n"; } fragmentShader += " vec3 viewDir = -normalize(v_positionEC);\n"; fragmentShader += " vec3 ambientLight = vec3(0.0, 0.0, 0.0);\n"; // Add in light computations fragmentShader += fragmentLightingBlock; fragmentShader += colorCreationBlock; fragmentShader += finalColorComputation; fragmentShader += "}\n"; // Add shaders var vertexShaderId = addToArray(shaders, { type: WebGLConstants.VERTEX_SHADER, extras: { _pipeline: { source: vertexShader, extension: ".glsl", }, }, }); var fragmentShaderId = addToArray(shaders, { type: WebGLConstants.FRAGMENT_SHADER, extras: { _pipeline: { source: fragmentShader, extension: ".glsl", }, }, }); // Add program var programId = addToArray(programs, { fragmentShader: fragmentShaderId, vertexShader: vertexShaderId, }); var techniqueId = addToArray(techniques, { attributes: techniqueAttributes, program: programId, uniforms: techniqueUniforms, }); return techniqueId; } 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) { case "ambient": return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4; case "diffuse": return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4; case "emission": return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4; case "specular": return value.length === 1 ? WebGLConstants.SAMPLER_2D : WebGLConstants.FLOAT_VEC4; case "shininess": return WebGLConstants.FLOAT; case "transparency": return WebGLConstants.FLOAT; // these two are usually not used directly within shaders, // they are just added here for completeness case "transparent": return WebGLConstants.BOOL; case "doubleSided": return WebGLConstants.BOOL; } } function getTechniqueKey(khrMaterialsCommon, primitiveInfo) { var techniqueKey = ""; techniqueKey += "technique:" + khrMaterialsCommon.technique + ";"; var values = khrMaterialsCommon.values; var keys = Object.keys(values).sort(); var keysCount = keys.length; for (var i = 0; i < keysCount; ++i) { var name = keys[i]; if (values.hasOwnProperty(name)) { techniqueKey += name + ":" + getKHRMaterialsCommonValueType(name, values[name]); techniqueKey += ";"; } } var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); techniqueKey += jointCount.toString() + ";"; if (defined(primitiveInfo)) { var skinningInfo = primitiveInfo.skinning; if (jointCount > 0) { techniqueKey += skinningInfo.type + ";"; } techniqueKey += primitiveInfo.hasVertexColors; } return techniqueKey; } function lightDefaults(gltf) { var khrMaterialsCommon = gltf.extensions.KHR_materials_common; if (!defined(khrMaterialsCommon) || !defined(khrMaterialsCommon.lights)) { return; } var lights = khrMaterialsCommon.lights; var lightsLength = lights.length; for (var lightId = 0; lightId < lightsLength; lightId++) { var light = lights[lightId]; if (light.type === "ambient") { if (!defined(light.ambient)) { light.ambient = {}; } var ambientLight = light.ambient; if (!defined(ambientLight.color)) { ambientLight.color = [1.0, 1.0, 1.0]; } } else if (light.type === "directional") { if (!defined(light.directional)) { light.directional = {}; } var directionalLight = light.directional; if (!defined(directionalLight.color)) { directionalLight.color = [1.0, 1.0, 1.0]; } } else if (light.type === "point") { if (!defined(light.point)) { light.point = {}; } var pointLight = light.point; if (!defined(pointLight.color)) { pointLight.color = [1.0, 1.0, 1.0]; } pointLight.constantAttenuation = defaultValue( pointLight.constantAttenuation, 1.0 ); pointLight.linearAttenuation = defaultValue( pointLight.linearAttenuation, 0.0 ); pointLight.quadraticAttenuation = defaultValue( pointLight.quadraticAttenuation, 0.0 ); } else if (light.type === "spot") { if (!defined(light.spot)) { light.spot = {}; } var spotLight = light.spot; if (!defined(spotLight.color)) { spotLight.color = [1.0, 1.0, 1.0]; } spotLight.constantAttenuation = defaultValue( spotLight.constantAttenuation, 1.0 ); spotLight.fallOffAngle = defaultValue(spotLight.fallOffAngle, 3.14159265); spotLight.fallOffExponent = defaultValue(spotLight.fallOffExponent, 0.0); spotLight.linearAttenuation = defaultValue( spotLight.linearAttenuation, 0.0 ); spotLight.quadraticAttenuation = defaultValue( spotLight.quadraticAttenuation, 0.0 ); } } } export default processModelMaterialsCommon;