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 "./GltfPipeline/addToArray.js"; import ForEach from "./GltfPipeline/ForEach.js"; import usesExtension from "./GltfPipeline/usesExtension.js"; import ModelUtility from "./ModelUtility.js"; /** * @private */ function processModelMaterialsCommon(gltf, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); if (!defined(gltf)) { return; } if (!usesExtension(gltf, "KHR_materials_common")) { return; } if (!usesExtension(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"); } const techniquesWebgl = gltf.extensions.KHR_techniques_webgl; lightDefaults(gltf); const lightParameters = generateLightParameters(gltf); const primitiveByMaterial = ModelUtility.splitIncompatibleMaterials(gltf); const techniques = {}; let generatedTechniques = false; ForEach.material(gltf, function (material, materialIndex) { if ( defined(material.extensions) && defined(material.extensions.KHR_materials_common) ) { const khrMaterialsCommon = material.extensions.KHR_materials_common; const primitiveInfo = primitiveByMaterial[materialIndex]; const techniqueKey = getTechniqueKey(khrMaterialsCommon, primitiveInfo); let technique = techniques[techniqueKey]; if (!defined(technique)) { technique = generateTechnique( gltf, techniquesWebgl, primitiveInfo, khrMaterialsCommon, lightParameters, options.addBatchIdToGeneratedShaders ); techniques[techniqueKey] = technique; generatedTechniques = true; } const materialValues = {}; const values = khrMaterialsCommon.values; let uniformName; for (const 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) { const result = {}; let 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 const nodes = gltf.nodes; for (const nodeName in nodes) { if (nodes.hasOwnProperty(nodeName)) { const node = nodes[nodeName]; if ( defined(node.extensions) && defined(node.extensions.KHR_materials_common) ) { const 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 let lightCount = 0; for (const lightName in lights) { if (lights.hasOwnProperty(lightName)) { const light = lights[lightName]; const lightType = light.type; if (lightType !== "ambient" && !defined(light.node)) { delete lights[lightName]; continue; } const lightBaseName = `light${lightCount.toString()}`; light.baseName = lightBaseName; let ambient; let directional; let point; let spot; switch (lightType) { case "ambient": ambient = light.ambient; result[`${lightBaseName}Color`] = { type: WebGLConstants.FLOAT_VEC3, value: ambient.color, }; break; case "directional": 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": 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": 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 ); const techniques = techniquesWebgl.techniques; const shaders = techniquesWebgl.shaders; const programs = techniquesWebgl.programs; const lightingModel = khrMaterialsCommon.technique.toUpperCase(); let lights; if ( defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common) ) { lights = gltf.extensions.KHR_materials_common.lights; } const parameterValues = khrMaterialsCommon.values; const jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); let skinningInfo; let hasSkinning = false; let hasVertexColors = false; if (defined(primitiveInfo)) { skinningInfo = primitiveInfo.skinning; hasSkinning = skinningInfo.skinned; hasVertexColors = primitiveInfo.hasVertexColors; } let vertexShader = "precision highp float;\n"; let fragmentShader = "precision highp float;\n"; const hasNormals = lightingModel !== "CONSTANT"; // Add techniques const techniqueUniforms = { u_modelViewMatrix: { semantic: usesExtension(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 let uniformName; let hasTexCoords = false; for (const 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" ) { const 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 (const 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)) { const uniform = techniqueUniforms[uniformName]; const 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 let 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 const 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 let 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"; } const hasSpecular = hasNormals && (lightingModel === "BLINN" || lightingModel === "PHONG") && defined(techniqueUniforms.u_specular) && defined(techniqueUniforms.u_shininess) && techniqueUniforms.u_shininess > 0.0; // Generate lighting code blocks let hasNonAmbientLights = false; let hasAmbientLights = false; let fragmentLightingBlock = ""; for (const lightName in lights) { if (lights.hasOwnProperty(lightName)) { const light = lights[lightName]; const lightType = light.type.toLowerCase(); const lightBaseName = light.baseName; fragmentLightingBlock += " {\n"; const lightColorName = `u_${lightBaseName}Color`; if (lightType === "ambient") { hasAmbientLights = true; fragmentLightingBlock += ` ambientLight += ${lightColorName};\n`; } else if (hasNormals) { hasNonAmbientLights = true; const varyingDirectionName = `v_${lightBaseName}Direction`; const 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"; const 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"; let 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"; } } let 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 const vertexShaderId = addToArray(shaders, { type: WebGLConstants.VERTEX_SHADER, extras: { _pipeline: { source: vertexShader, extension: ".glsl", }, }, }); const fragmentShaderId = addToArray(shaders, { type: WebGLConstants.FRAGMENT_SHADER, extras: { _pipeline: { source: fragmentShader, extension: ".glsl", }, }, }); // Add program const programId = addToArray(programs, { fragmentShader: fragmentShaderId, vertexShader: vertexShaderId, }); const techniqueId = addToArray(techniques, { attributes: techniqueAttributes, program: programId, uniforms: techniqueUniforms, }); return techniqueId; } function getKHRMaterialsCommonValueType(paramName, paramValue) { let 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) { let techniqueKey = ""; techniqueKey += `technique:${khrMaterialsCommon.technique};`; const values = khrMaterialsCommon.values; const keys = Object.keys(values).sort(); const keysCount = keys.length; for (let i = 0; i < keysCount; ++i) { const name = keys[i]; if (values.hasOwnProperty(name)) { techniqueKey += `${name}:${getKHRMaterialsCommonValueType( name, values[name] )}`; techniqueKey += ";"; } } const jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); techniqueKey += `${jointCount.toString()};`; if (defined(primitiveInfo)) { const skinningInfo = primitiveInfo.skinning; if (jointCount > 0) { techniqueKey += `${skinningInfo.type};`; } techniqueKey += primitiveInfo.hasVertexColors; } return techniqueKey; } function lightDefaults(gltf) { const khrMaterialsCommon = gltf.extensions.KHR_materials_common; if (!defined(khrMaterialsCommon) || !defined(khrMaterialsCommon.lights)) { return; } const lights = khrMaterialsCommon.lights; const lightsLength = lights.length; for (let lightId = 0; lightId < lightsLength; lightId++) { const light = lights[lightId]; if (light.type === "ambient") { if (!defined(light.ambient)) { light.ambient = {}; } const 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 = {}; } const 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 = {}; } const 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 = {}; } const 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;