/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @fileoverview This file contains various functions related to effects. * It puts them in the "effect" module on the o3djs object. * * Note: This library is only a sample. It is not meant to be some official * library. It is provided only as example code. * */ o3djs.provide('o3djs.effect'); o3djs.require('o3djs.io'); /** * A Module for dealing with effects. * @namespace */ o3djs.effect = o3djs.effect || {}; /** * The name of standard 2 color checker effect. * @type {string} */ o3djs.effect.TWO_COLOR_CHECKER_EFFECT_NAME = 'o3djs.effect.twoColorCheckerEffect'; /** * An effect string for a 2 color checker effect. * @type {string} */ o3djs.effect.TWO_COLOR_CHECKER_FXSTRING = '' + 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + 'float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\n' + 'float4x4 world : WORLD;\n' + 'float4 color1;\n' + 'float4 color2;\n' + 'float checkSize;\n' + 'float3 lightWorldPos;\n' + 'float3 lightColor;\n' + '\n' + 'struct VertexShaderInput {\n' + ' float4 position : POSITION;\n' + ' float4 normal : NORMAL;\n' + ' float2 texcoord : TEXCOORD0;\n' + '};\n' + '\n' + 'struct PixelShaderInput {\n' + ' float4 position : POSITION;\n' + ' float2 texcoord : TEXCOORD0;\n' + ' float3 normal : TEXCOORD1;\n' + ' float3 worldPosition : TEXCOORD2;\n' + '};\n' + '\n' + 'float4 checker(float2 uv) {\n' + ' float fmodResult = fmod(floor(checkSize * uv.x) + \n' + ' floor(checkSize * uv.y), 2.0);\n' + ' return (fmodResult < 1) ? color1 : color2;\n' + '}\n' + '\n' + 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + ' PixelShaderInput output;\n' + '\n' + ' output.position = mul(input.position, worldViewProjection);\n' + ' output.normal = mul(input.normal, worldInverseTranspose).xyz;\n' + ' output.worldPosition = mul(input.position, world).xyz;\n' + ' output.texcoord = input.texcoord;\n' + ' return output;\n' + '}\n' + '\n' + 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' + ' float3 surfaceToLight = \n' + ' normalize(lightWorldPos - input.worldPosition);\n' + ' float3 worldNormal = normalize(input.normal);\n' + ' float4 check = checker(input.texcoord);\n' + ' float4 directionalIntensity = \n' + ' saturate(dot(worldNormal, surfaceToLight));\n' + ' float4 outColor = directionalIntensity * check;\n' + ' return float4(outColor.rgb, check.a);\n' + '}\n' + '\n' + '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + '// #o3d MatrixLoadOrder RowMajor\n'; /** * The name of the parameter on a material if it's a collada standard * material. * * NOTE: This parameter is just a string attached to a material. It has no * meaning to the plugin, it is passed from the conditioner to the * javascript libraries so that they can build collada like effects. * * @type {string} */ o3djs.effect.COLLADA_LIGHTING_TYPE_PARAM_NAME = 'collada.lightingType'; /** * The collada standard lighting types. * @type {!Object} */ o3djs.effect.COLLADA_LIGHTING_TYPES = {phong: 1, lambert: 1, blinn: 1, constant: 1}; /** * The FCollada standard materials sampler parameter name prefixes. * @type {!Array.} */ o3djs.effect.COLLADA_SAMPLER_PARAMETER_PREFIXES = ['emissive', 'ambient', 'diffuse', 'specular', 'bump']; /** * Check if lighting type is a collada lighting type. * @param {string} lightingType Lighting type to check. * @return {boolean} true if it's a collada lighting type. */ o3djs.effect.isColladaLightingType = function(lightingType) { return o3djs.effect.COLLADA_LIGHTING_TYPES[lightingType.toLowerCase()] == 1; }; /** * Returns the collada lighting type of a collada standard material. * @param {!o3d.Material} material Material to get lighting type from. * @return {string} The lighting type or "" if it's not a collada standard * material. */ o3djs.effect.getColladaLightingType = function(material) { var lightingTypeParam = material.getParam( o3djs.effect.COLLADA_LIGHTING_TYPE_PARAM_NAME); if (lightingTypeParam) { var lightingType = lightingTypeParam.value.toLowerCase(); if (o3djs.effect.isColladaLightingType(lightingType)) { return lightingType; } } return ''; }; /** * Get the number of TEXCOORD streams needed by this material. * @param {!o3d.Material} material The material MUST be a standard * collada material. * @return {number} The number oc TEXCOORD streams needed. */ o3djs.effect.getNumTexCoordStreamsNeeded = function(material) { var lightingType = o3djs.effect.getColladaLightingType(material); if (!o3djs.effect.isColladaLightingType(lightingType)) { throw 'not a collada standard material'; } var colladaSamplers = o3djs.effect.COLLADA_SAMPLER_PARAMETER_PREFIXES; var numTexCoordStreamsNeeded = 0 for (var cc = 0; cc < colladaSamplers.length; ++cc) { var samplerPrefix = colladaSamplers[cc]; var samplerParam = material.getParam(samplerPrefix + 'Sampler'); if (samplerParam) { ++numTexCoordStreamsNeeded; } } return numTexCoordStreamsNeeded; }; /** * Loads shader source from an external file and creates shaders for an effect. * @param {!o3d.Effect} effect The effect to create the shaders in. * @param {string} url The url of the shader source. */ o3djs.effect.loadEffect = function(effect, url) { var fxString = o3djs.io.loadTextFileSynchronous(url); effect.loadFromFXString(fxString); }; /** * Creates an effect from a file. * If the effect already exists in the pack that effect will be returned. * @param {!o3d.Pack} pack Pack to create effect in. * @param {string} url Url for effect file. * @return {!o3d.Effect} The effect. */ o3djs.effect.createEffectFromFile = function(pack, url) { var effect = pack.getObjects(url, 'o3d.Effect')[0]; if (!effect) { effect = pack.createObject('Effect'); o3djs.effect.loadEffect(effect, url); effect.name = url; } return effect; }; /** * Builds a shader string for a given standard COLLADA material type. * * @param {!o3d.Material} material Material for which to build the shader. * @param {string} effectType Type of effect to create ('phong', 'lambert', * 'constant'). * @return {{description: string, shader: string}} A description and the shader * string. */ o3djs.effect.buildStandardShaderString = function(material, effectType) { var bumpSampler = material.getParam('bumpSampler'); var bumpUVInterpolant; /** * Extracts the texture type from a texture param. * @param {!o3d.ParamTexture} textureParam The texture parameter to * inspect. * @return {string} The texture type (1D, 2D, 3D or CUBE). */ var getTextureType = function(textureParam) { var texture = textureParam.value; if (!texture) return '2D'; // No texture value, have to make a guess. switch (texture.className) { case 'o3d.Texture1D' : return '1D'; case 'o3d.Texture2D' : return '2D'; case 'o3d.Texture3D' : return '3D'; case 'o3d.TextureCUBE' : return 'CUBE'; default : return '2D'; } } /** * Extracts the sampler type from a sampler param. It does it by inspecting * the texture associated with the sampler. * @param {!o3d.ParamTexture} samplerParam The texture parameter to * inspect. * @return {string} The texture type (1D, 2D, 3D or CUBE). */ var getSamplerType = function(samplerParam) { var sampler = samplerParam.value; if (!sampler) return '2D'; var textureParam = sampler.getParam('Texture'); if (textureParam) return getTextureType(textureParam); else return '2D'; }; /** * Builds uniform variables common to all standard lighting types. * @return {string} The effect code for the common shader uniforms. */ var buildCommonUniforms = function() { return 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + 'uniform float3 lightWorldPos;\n' + 'uniform float4 lightColor;\n' }; /** * Builds uniform variables common to lambert, phong and blinn lighting types. * @return {string} The effect code for the common shader uniforms. */ var buildLightingUniforms = function() { return 'uniform float4x4 world : WORLD;\n' + 'uniform float4x4 viewInverse : VIEWINVERSE;\n' + 'uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\n'; }; /** * Builds uniform parameters for a given color input. If the material * has a sampler parameter, a sampler uniform is created, otherwise a * float4 color value is created. * @param {!o3d.Material} material The material to inspect. * @param {!Array.} descriptions Array to add descriptions too. * @param {string} name The name of the parameter to look for. Usually * emissive, ambient, diffuse or specular. * @param {boolean} opt_addColorParam Whether to add a color param if no * sampler exists. Default = true. * @return {string} The effect code for the uniform parameter. */ var buildColorParam = function(material, descriptions, name, opt_addColorParam) { if (opt_addColorParam === undefined) { opt_addColorParam = true; } var samplerParam = material.getParam(name + 'Sampler'); if (samplerParam) { var type = getSamplerType(samplerParam); descriptions.push(name + type + 'Texture'); return 'sampler' + type + ' ' + name + 'Sampler;\n' } else if (opt_addColorParam) { descriptions.push(name + 'Color'); return 'uniform float4 ' + name + ';\n'; } else { return ''; } }; /** * Builds the effect code to retrieve a given color input. If the material * has a sampler parameter of that name, a texture lookup is done. Otherwise * it's a no-op, since the value is retrieved directly from the color uniform * of that name. * @param {!o3d.Material} material The material to inspect. * @param {string} name The name of the parameter to look for. Usually * emissive, ambient, diffuse or specular. * @return {string} The effect code for the uniform parameter retrieval. */ var getColorParam = function(material, name) { var samplerParam = material.getParam(name + 'Sampler'); if (samplerParam) { var type = getSamplerType(samplerParam); return ' float4 ' + name + ' = tex' + type + '(' + name + 'Sampler, input.' + name + 'UV);\n' } else { return ''; } }; /** * Builds the vertex and fragment shader entry point in the format that * o3d can parse. * @return {string} The effect code for the entry points. */ var buildEntryPoints = function() { return ' // #o3d VertexShaderEntryPoint vertexShaderFunction\n' + ' // #o3d PixelShaderEntryPoint pixelShaderFunction\n' + ' // #o3d MatrixLoadOrder RowMajor\n'; }; /** * Builds vertex and fragment shader string for the Constant lighting type. * @param {!o3d.Material} material The material for which to build * shaders. * @param {!Array.} descriptions Array to add descriptions too. * @return {string} The effect code for the shader, ready to be parsed. */ var buildConstantShaderString = function(material, descriptions) { descriptions.push('constant'); return buildCommonUniforms() + buildColorParam(material, descriptions, 'emissive') + buildVertexDecls(material, false, false) + 'OutVertex vertexShaderFunction(InVertex input) {\n' + ' OutVertex output;\n' + positionVertexShaderCode() + buildUVPassthroughs(material) + ' return output;\n' + '}\n' + 'float4 pixelShaderFunction(OutVertex input) : COLOR {\n' + getColorParam(material, 'emissive') + ' return emissive;\n' + '}\n' + '\n' + buildEntryPoints(); }; /** * Builds vertex and fragment shader string for the Lambert lighting type. * @param {!o3d.Material} material The material for which to build * shaders. * @param {!Array.} descriptions Array to add descriptions too. * @return {string} The effect code for the shader, ready to be parsed. */ var buildLambertShaderString = function(material, descriptions) { descriptions.push('lambert'); return buildCommonUniforms() + buildLightingUniforms() + buildColorParam(material, descriptions, 'emissive') + buildColorParam(material, descriptions, 'ambient') + buildColorParam(material, descriptions, 'diffuse') + buildColorParam(material, descriptions, 'bump', false) + buildVertexDecls(material, true, false) + 'OutVertex vertexShaderFunction(InVertex input) {\n' + ' OutVertex output;\n' + buildUVPassthroughs(material) + positionVertexShaderCode() + normalVertexShaderCode() + surfaceToLightVertexShaderCode() + bumpVertexShaderCode() + ' return output;\n' + '}\n' + 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' + '{\n' + getColorParam(material, 'emissive') + getColorParam(material, 'ambient') + getColorParam(material, 'diffuse') + getNormalShaderCode() + ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' + ' float4 litR = lit(dot(normal, surfaceToLight), 0, 0);\n' + ' return float4((emissive +\n' + ' lightColor * (ambient * diffuse + diffuse * litR.y)).rgb,' + ' diffuse.a);\n' + '}\n' + '\n' + buildEntryPoints(); }; /** * Builds vertex and fragment shader string for the Blinn lighting type. * @param {!o3d.Material} material The material for which to build * shaders. * @param {!Array.} descriptions Array to add descriptions too. * @return {string} The effect code for the shader, ready to be parsed. * TODO: This is actually just a copy of the Phong code. * Change to Blinn. */ var buildBlinnShaderString = function(material, descriptions) { descriptions.push('blinn'); return buildCommonUniforms() + buildLightingUniforms() + buildColorParam(material, descriptions, 'emissive') + buildColorParam(material, descriptions, 'ambient') + buildColorParam(material, descriptions, 'diffuse') + buildColorParam(material, descriptions, 'specular') + buildColorParam(material, descriptions, 'bump', false) + 'uniform float shininess;\n' + 'uniform float specularFactor;\n' + buildVertexDecls(material, true, true) + 'OutVertex vertexShaderFunction(InVertex input) {\n' + ' OutVertex output;\n' + buildUVPassthroughs(material) + positionVertexShaderCode() + normalVertexShaderCode() + surfaceToLightVertexShaderCode() + surfaceToViewVertexShaderCode() + bumpVertexShaderCode() + ' return output;\n' + '}\n' + 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' + '{\n' + getColorParam(material, 'emissive') + getColorParam(material, 'ambient') + getColorParam(material, 'diffuse') + getColorParam(material, 'specular') + getNormalShaderCode() + ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' + ' float3 surfaceToView = normalize(input.surfaceToView);\n' + ' float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' + ' float4 litR = lit(dot(normal, surfaceToLight), \n' + ' dot(normal, halfVector), shininess);\n' + ' return float4((emissive +\n' + ' lightColor * (ambient * diffuse + diffuse * litR.y +\n' + ' + specular * litR.z * specularFactor)).rgb,' + ' diffuse.a);\n' + '}\n' + '\n' + buildEntryPoints(); }; /** * Builds vertex and fragment shader string for the Phong lighting type. * @param {!o3d.Material} material The material for which to build * shaders. * @param {!Array.} descriptions Array to add descriptions too. * @return {string} The effect code for the shader, ready to be parsed. */ var buildPhongShaderString = function(material, descriptions) { descriptions.push('phong'); return buildCommonUniforms() + buildLightingUniforms() + buildColorParam(material, descriptions, 'emissive') + buildColorParam(material, descriptions, 'ambient') + buildColorParam(material, descriptions, 'diffuse') + buildColorParam(material, descriptions, 'specular') + buildColorParam(material, descriptions, 'bump', false) + 'uniform float shininess;\n' + 'uniform float specularFactor;\n' + buildVertexDecls(material, true, true) + 'OutVertex vertexShaderFunction(InVertex input) {\n' + ' OutVertex output;\n' + buildUVPassthroughs(material) + positionVertexShaderCode() + normalVertexShaderCode() + surfaceToLightVertexShaderCode() + surfaceToViewVertexShaderCode() + bumpVertexShaderCode() + ' return output;\n' + '}\n' + 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' + '{\n' + getColorParam(material, 'emissive') + getColorParam(material, 'ambient') + getColorParam(material, 'diffuse') + getColorParam(material, 'specular') + getNormalShaderCode() + ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' + ' float3 surfaceToView = normalize(input.surfaceToView);\n' + ' float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' + ' float4 litR = lit(dot(normal, surfaceToLight), \n' + ' dot(normal, halfVector), shininess);\n' + ' return float4((emissive +\n' + ' lightColor * (ambient * diffuse + diffuse * litR.y +\n' + ' + specular * litR.z * specularFactor)).rgb,' + ' diffuse.a);\n' + '}\n' + '\n' + buildEntryPoints(); }; // An integer value which keeps track of the next available interpolant. var interpolant; /** * Builds the texture coordinate declaration for a given color input * (usually emissive, anmbient, diffuse or specular). If the color * input does not have a sampler, no TEXCOORD declaration is built. * @param {!o3d.Material} material The material to inspect. * @param {string} name The name of the color input. * @return {string} The code for the texture coordinate declaration. */ var buildTexCoord = function(material, name) { if (material.getParam(name + 'Sampler')) { return ' float2 ' + name + 'UV : TEXCOORD' + interpolant++ + ';\n'; } else { return ''; } }; /** * Builds all the texture coordinate declarations for a vertex attribute * declaration. * @param {!o3d.Material} material The material to inspect. * @return {string} The code for the texture coordinate declarations. */ var buildTexCoords = function(material) { interpolant = 0; return buildTexCoord(material, 'emissive') + buildTexCoord(material, 'ambient') + buildTexCoord(material, 'diffuse') + buildTexCoord(material, 'specular'); }; /** * Builds the texture coordinate passthrough statement for a given * color input (usually emissive, ambient, diffuse or specular). These * assigments are used in the vertex shader to pass the texcoords to be * interpolated to the rasterizer. If the color input does not have * a sampler, no code is generated. * @param {!o3d.Material} material The material to inspect. * @param {string} name The name of the color input. * @return {string} The code for the texture coordinate passthrough statement. */ var buildUVPassthrough = function(material, name) { if (material.getParam(name + 'Sampler')) { return ' output.' + name + 'UV = input.' + name + 'UV;\n'; } else { return ''; } }; /** * Builds all the texture coordinate passthrough statements for the * vertex shader. * @param {!o3d.Material} material The material to inspect. * @return {string} The code for the texture coordinate passthrough * statements. */ var buildUVPassthroughs = function(material) { return buildUVPassthrough(material, 'emissive') + buildUVPassthrough(material, 'ambient') + buildUVPassthrough(material, 'diffuse') + buildUVPassthrough(material, 'specular') + buildUVPassthrough(material, 'bump'); }; /** * Builds bump input coords if needed. * @return {string} The code for bump input coords. */ var buildBumpInputCoords = function() { return bumpSampler ? (' float3 tangent : TANGENT;\n' + ' float3 binormal : BINORMAL;\n' + ' float2 bumpUV : TEXCOORD' + interpolant++ + ';\n') : ''; }; /** * Builds bump output coords if needed. * @return {string} The code for bump input coords. */ var buildBumpOutputCoords = function() { return bumpSampler ? (' float3 tangent : TEXCOORD' + interpolant++ + ';\n' + ' float3 binormal : TEXCOORD' + interpolant++ + ';\n' + ' float2 bumpUV : TEXCOORD' + interpolant++ + ';\n') : ''; }; /** * Builds the position code for the vertex shader. * @return {string} The code for the vertex shader. */ var positionVertexShaderCode = function() { return ' output.position = mul(input.position, worldViewProjection);\n'; }; /** * Builds the normal code for the vertex shader. * @return {string} The code for the vertex shader. */ var normalVertexShaderCode = function() { return ' output.normal = mul(float4(input.normal, 0),\n' + ' worldInverseTranspose).xyz;\n'; }; /** * Builds the surface to light code for the vertex shader. * @return {string} The code for the vertex shader. */ var surfaceToLightVertexShaderCode = function() { return ' output.surfaceToLight = lightWorldPos - \n' + ' mul(input.position, world).xyz;\n'; }; /** * Builds the surface to view code for the vertex shader. * @return {string} The code for the vertex shader. */ var surfaceToViewVertexShaderCode = function() { return ' output.surfaceToView = (viewInverse[3] - mul(input.position,\n' + ' world)).xyz;\n'; }; /** * Builds the normal map part of the vertex shader. * @return {string} The code for normal mapping in the vertex shader. */ var bumpVertexShaderCode = function() { return bumpSampler ? (' output.binormal = ' + 'mul(float4(input.binormal, 0), worldInverseTranspose).xyz;\n' + ' output.tangent = ' + 'mul(float4(input.tangent, 0), worldInverseTranspose).xyz;\n') : ''; }; /** * Builds the normal calculation of the pixel shader. * @return {string} The code for normal computation in the pixel shader. */ var getNormalShaderCode = function() { return bumpSampler ? ('float3x3 tangentToWorld = float3x3(input.tangent,\n' + ' input.binormal,\n' + ' input.normal);\n' + 'float3 tangentNormal = tex2D(bumpSampler, input.bumpUV.xy).xyz -\n' + ' float3(0.5, 0.5, 0.5);\n' + 'float3 normal = mul(tangentNormal, tangentToWorld);\n' + 'normal = normalize(normal);\n') : ' float3 normal = normalize(input.normal);\n'; }; /** * Builds the vertex declarations for a given material. * @param {!o3d.Material} material The material to inspect. * @param {boolean} diffuse Whether to include stuff for diffuse calculations. * @param {boolean} specular Whether to include stuff for diffuse * calculations. * @return {string} The code for the vertex declarations. */ var buildVertexDecls = function(material, diffuse, specular) { var str = 'struct InVertex {\n' + ' float4 position : POSITION;\n'; if (diffuse || specular) { str += ' float3 normal : NORMAL;\n'; } str += buildTexCoords(material) + buildBumpInputCoords() + '};\n' + 'struct OutVertex {\n' + ' float4 position : POSITION;\n' + buildTexCoords(material) + buildBumpOutputCoords(); if (diffuse || specular) { str += ' float3 normal : TEXCOORD' + interpolant++ + ';\n' + ' float3 surfaceToLight: TEXCOORD' + interpolant++ + ';\n'; } if (specular) { str += ' float3 surfaceToView : TEXCOORD' + interpolant++ + ';\n'; } str += '};\n' return str; }; // Create a shader string of the appropriate type, based on the // effectType. var str; var descriptions = []; if (effectType == 'phong') { str = buildPhongShaderString(material, descriptions); } else if (effectType == 'lambert') { str = buildLambertShaderString(material, descriptions); } else if (effectType == 'blinn') { str = buildBlinnShaderString(material, descriptions); } else if (effectType == 'constant') { str = buildConstantShaderString(material, descriptions); } else { throw ('unknown effect type "' + effectType + '"'); } return {description: descriptions.join('_'), shader: str}; }; /** * Gets or builds a shader for given standard COLLADA material type. * * Looks at the material passed in and assigns it an Effect that matches its * Params. If a suitable Effect already exists in pack it will use that Effect. * * @param {!o3d.Pack} pack Pack in which to create the new Effect. * @param {!o3d.Material} material Material for which to build the shader. * @param {string} effectType Type of effect to create ('phong', 'lambert', * 'constant'). * @return {o3d.Effect} The created effect. */ o3djs.effect.getStandardShader = function(pack, material, effectType) { var record = o3djs.effect.buildStandardShaderString(material, effectType); var effects = pack.getObjectsByClassName('o3d.Effect'); for (var ii = 0; ii < effects.length; ++ii) { if (effects[ii].name == record.description && effects[ii].source == record.shader) { return effects[ii]; } } var effect = pack.createObject('Effect'); if (effect) { effect.name = record.description; if (effect.loadFromFXString(record.shader)) { return effect; } pack.removeObject(effect); } return null; }; /** * Attaches a shader for a given standard COLLADA material type to the material. * * Looks at the material passed in and assigns it an Effect that matches its * Params. If a suitable Effect already exists in pack it will use that Effect. * * @param {!o3d.Pack} pack Pack in which to create the new Effect. * @param {!o3d.Material} material Material for which to build the shader. * @param {!o3djs.math.Vector3} lightPos Position of the default light. * @param {string} effectType Type of effect to create ('phong', 'lambert', * 'constant'). * @return {boolean} True on success. */ o3djs.effect.attachStandardShader = function(pack, material, lightPos, effectType) { var effect = o3djs.effect.getStandardShader(pack, material, effectType); if (effect) { material.effect = effect; effect.createUniformParameters(material); // Set a couple of the default parameters in the hopes that this will // help the user get something on the screen. We check to make sure they // are not connected to something otherwise we'll get an error. var param = material.getParam('lightWorldPos'); if (!param.inputConnection) { param.value = lightPos; } var param = material.getParam('lightColor'); if (!param.inputConnection) { param.value = [1, 1, 1, 1]; } return true; } else { return false; } }; /** * Creates the uniform parameters needed for an Effect on the given ParamObject. * @param {!o3d.Pack} pack Pack to create extra objects in like Samplers and * ParamArrays. * @param {!o3d.Effect} effect Effect. * @param {!o3d.ParamObject} paramObject ParamObject on which to create Params. */ o3djs.effect.createUniformParameters = function(pack, effect, paramObject) { effect.createUniformParameters(paramObject); var infos = effect.getParameterInfo(); for (var ii = 0; ii < infos.length; ++ii) { var info = infos[ii]; if (info.sasClassName.length == 0) { if (info.numElements > 0) { var paramArray = pack.createObject('ParamArray'); var param = paramObject.getParam(info.name); param.value = paramArray; paramArray.resize(info.numElements, info.className); if (info.className == 'o3d.ParamSampler') { for (var jj = 0; jj < info.numElements; ++jj) { var sampler = pack.createObject('Sampler'); paramArray.getParam(jj).value = sampler; } } } else if (info.className == 'o3d.ParamSampler') { var sampler = pack.createObject('Sampler'); var param = paramObject.getParam(info.name); param.value = sampler; } } } }; /** * Creates an effect that draws a 2 color procedural checker pattern. * @param {!o3d.Pack} pack The pack to create the effect in. If the pack * already has an effect with the same name that effect will be returned. * @return {!o3d.Effect} The effect. */ o3djs.effect.createCheckerEffect = function(pack) { var effects = pack.getObjects(o3djs.effect.TWO_COLOR_CHECKER_EFFECT_NAME, 'o3d.Effect'); if (effects.length > 0) { return effects[0]; } var effect = pack.createObject('Effect'); effect.loadFromFXString(o3djs.effect.TWO_COLOR_CHECKER_FXSTRING); effect.name = o3djs.effect.TWO_COLOR_CHECKER_EFFECT_NAME; return effect; };