/*
 * 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 object containing string constants and functions which are specific to
 * the o3d shading language.  When setLanguage gets called the properties of
 * this object get coppied into the o3djs.effect namespace and then get used
 * in shader generation code.
 * @namespace
 */
o3djs.effect.o3d = {
  FLOAT2: 'float2',
  FLOAT3: 'float3',
  FLOAT4: 'float4',
  MATRIX4: 'float4x4',
  MATRIX3: 'float3x3',
  MOD: 'fmod',
  ATTRIBUTE: '  ',
  ATTRIBUTE_PREFIX: 'input.',
  VARYING: '  ',
  VARYING_DECLARATION_PREFIX: '',
  VERTEX_VARYING_PREFIX: 'output.',
  PIXEL_VARYING_PREFIX: 'input.',
  TEXTURE: 'tex',
  BEGIN_IN_STRUCT: 'struct InVertex {\n',
  BEGIN_OUT_STRUCT: 'struct OutVertex {\n',
  END_STRUCT: '};\n'
};


/**
 * An object containing string constants and functions which are specific to
 * the o3d shading language.  When setLanguage gets called the properties of
 * this object get coppied into the o3djs.effect namespace and then get used
 * in shader generation code.
 * @namespace
 */
o3djs.effect.glsl = {
    FLOAT2: 'vec2',
    FLOAT3: 'vec3',
    FLOAT4: 'vec4',
    MATRIX4: 'mat4',
    MATRIX3: 'mat3',
    MOD: 'mod',
    ATTRIBUTE: 'attribute ',
    ATTRIBUTE_PREFIX: '',
    VARYING: 'varying ',
    VARYING_DECLARATION_PREFIX: 'v_',
    VERTEX_VARYING_PREFIX: 'v_',
    PIXEL_VARYING_PREFIX: 'v_',
    TEXTURE: 'texture',
    BEGIN_IN_STRUCT: '',
    BEGIN_OUT_STRUCT: '',
    END_STRUCT: '',
    // Only used in GLSL version of getAttributeName_.
    semanticNameMap: {
      'POSITION'  : 'position',
      'NORMAL'    : 'normal',
      'TANGENT'   : 'tangent',
      'BINORMAL'  : 'binormal',
      'COLOR'     : 'color',
      'TEXCOORD0' : 'texCoord0',
      'TEXCOORD1' : 'texCoord1',
      'TEXCOORD2' : 'texCoord2',
      'TEXCOORD3' : 'texCoord3',
      'TEXCOORD4' : 'texCoord4',
      'TEXCOORD5' : 'texCoord5',
      'TEXCOORD6' : 'texCoord6',
      'TEXCOORD7' : 'texCoord7'
    }
};


/**
 * The string that goes between the stream name and the semicolon to indicate
 * the semantic.
 * @param {string} name Name of the semantic.
 * @return {string}
 */
o3djs.effect.glsl.semanticSuffix = function(name) {
  return '';
};


/**
 * The string that goes between the stream name and the semicolon to indicate
 * the semantic.
 * @param {string} name Name of the semantic.
 * @return {string}
 */
o3djs.effect.o3d.semanticSuffix = function(name) {
  return ' : ' + name;
};


/**
 * Attribute variables in GLSL need to be named by their semantic in
 * order for the implementation to hook them up correctly.
 * @private
 */
o3djs.effect.glsl.getAttributeName_ = function(name, semantic) {
  var p = o3djs.effect;
  return p.semanticNameMap[semantic];
};


/**
 * This passes through the name in the Cg implementation.
 * @private
 */
o3djs.effect.o3d.getAttributeName_ = function(name, semantic) {
  return name;
};


/**
 * Generates code to multiply two things.
 * @param {string} a One multiplicand.
 * @param {string} b The other multiplicand.
 * @return {string}
 */
o3djs.effect.glsl.mul = function(a, b) {
  return '(' + b + ' * ' + a + ')';
};


/**
 * Generates code to multiply two things.
 * @param {string} a One multiplicand.
 * @param {string} b The other multiplicand.
 * @return {string}
 */
o3djs.effect.o3d.mul = function(a, b) {
  return 'mul(' + a + ', ' + b + ')';
};


/**
 * Generates code for some utility functions
 * (functions defined in cg but not glsl).
 * @return {string} The code for the utility functions.
 */
o3djs.effect.glsl.utilityFunctions = function() {
  return 'vec4 lit(float l ,float h, float m) {\n' +
         '  return vec4(1.0,\n' +
         '              max(l, 0.0),\n' +
         '              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,\n' +
         '              1.0);\n' +
         '}\n';
};


/**
 * Generates code for some utility functions
 * (functions defined in cg but not glsl).
 * @return {string} The code for the utility functions.
 */
o3djs.effect.o3d.utilityFunctions = function() {
  return '';
}


/**
 * The string that starts the vertex shader main function.
 * @return {string} The effect code for the start of the main.
 */
o3djs.effect.glsl.beginVertexShaderMain = function() {
    return 'void main() {\n';
};

/**
 * The string that starts the vertex shader main function.
 * @return {string} The effect code for the start of the main.
 */
o3djs.effect.o3d.beginVertexShaderMain = function() {
  return 'OutVertex vertexShaderFunction(InVertex input) {\n' +
         '  OutVertex output;\n';
};

/**
 * The string that ends the vertex main function.
 * @return {string}
 */
o3djs.effect.glsl.endVertexShaderMain = function() {
  return '  gl_Position = ' + o3djs.effect.VERTEX_VARYING_PREFIX +
      'position;\n}\n';
};

/**
 * The string that ends the vertex main function.
 * @return {string}
 */
o3djs.effect.o3d.endVertexShaderMain = function() {
  return '  return output;\n}\n';
};


/**
 * The string that goes infront of the pixel shader main.
 * @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.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The header.
 */
o3djs.effect.glsl.pixelShaderHeader =
    function(material, diffuse, specular, bumpSampler) {
  return '\n// #o3d SplitMarker\n';
};


/**
 * The string that goes infront of the pixel shader main.
 * @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.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The header.
 */
o3djs.effect.o3d.pixelShaderHeader =
    function(material, diffuse, specular, bumpSampler) {
  return '';
};


/**
 * Repeats the declarations for the varying parameters if necessary.
 * @param {string} opt_decls The declarations if you know them already.
 * @return {string} Code for the parameter declarations.
 */
o3djs.effect.glsl.repeatVaryingDecls = function(opt_decls) {
  return (opt_decls ||
          o3djs.effect.varying_decls_ ||
          o3djs.buildVaryingDecls()) +
      '\n';
};

/**
 * Repeats the declarations for the varying parameters if necessary.
 * @param {string} opt_decls The declarations if you know them already.
 * @return {string} Code for the parameter declarations.
 */
o3djs.effect.o3d.repeatVaryingDecls = function(opt_decls) {
  return '';
};


/**
 * The string that goes infront of the pixel shader main.
 * @return {string} The effect code for the start of the main.
 */
o3djs.effect.glsl.beginPixelShaderMain = function() {
  return 'void main() {\n';
};


/**
 * The string that goes infront of the pixel shader main.
 * @return {string} The effect code for the start of the main.
 */
o3djs.effect.o3d.beginPixelShaderMain = function() {
  return 'float4 pixelShaderFunction(OutVertex input) : COLOR {\n';
};


/**
 * The string that goes at the end of the pixel shader main.
 * @param {string} color The code for the color to return.
 * @return {string} The effect code for the end of the main.
 */
o3djs.effect.o3d.endPixelShaderMain = function(color) {
  return '  return ' + color + ';\n}\n';
};


/**
 * The string that goes at the end of the pixel shader main.
 * @param {string} color The code for the color to return.
 * @return {string} The effect code for the end of the main.
 */
o3djs.effect.glsl.endPixelShaderMain = function(color) {
  return '  gl_FragColor = ' + color + ';\n}\n';
};


/**
 * The vertex and fragment shader entry point in the format that
 * o3d parses.
 * @return {string}
 */
o3djs.effect.o3d.entryPoints = function() {
  return '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
    '// #o3d PixelShaderEntryPoint pixelShaderFunction\n';
};


/**
 * The vertex and fragment shader entry points.  In glsl, this is unnecessary.
 * @return {string}
 */
o3djs.effect.glsl.entryPoints = function() {
  return '';
};

o3djs.effect.glsl.matrixLoadOrder =
o3djs.effect.o3d.matrixLoadOrder = function() {
  return '// #o3d MatrixLoadOrder RowMajor\n';
};


/**
 * Sets the shader language used.  Passing 'glsl' will cause all generated
 * shader code to be in glsl.  Passing anything else will result in the
 * default o3d hlsl/cg based shader language.
 * @param {string} language Shader language to use.
 */
o3djs.effect.setLanguage = function(language) {
  var language_namespace = o3djs.effect.o3d;
  if (language == 'glsl') {
    language_namespace = o3djs.effect.glsl;
  }
  for (var f in o3djs.effect.glsl) {
    o3djs.effect[f] = language_namespace[f];
  }

  o3djs.effect.TWO_COLOR_CHECKER_FXSTRING =
      o3djs.effect.buildCheckerShaderString();
}


/**
 * Builds the vertex attribute 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.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The code for the declarations.
 */
o3djs.effect.buildAttributeDecls =
    function(material, diffuse, specular, bumpSampler) {
  var str = o3djs.effect.BEGIN_IN_STRUCT +
            o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT4 + ' ' + 'position' +
            o3djs.effect.semanticSuffix('POSITION') + ';\n';
  if (diffuse || specular) {
    str += o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT3 + ' ' + 'normal' +
    o3djs.effect.semanticSuffix('NORMAL') + ';\n';
  }
  str += o3djs.effect.buildTexCoords(material, false) +
         o3djs.effect.buildBumpInputCoords(bumpSampler) +
         o3djs.effect.END_STRUCT;
  return str;
};


/**
 * Caches the varying parameter declarations to be repeated in the case that
 * we're in glsl and need to declare the varying parameters in both shaders.
 * @type {string}
 */
o3djs.effect.varying_decls_ = '';


/**
 * Builds the varying parameter 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.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The code for the declarations.
 */
o3djs.effect.buildVaryingDecls =
    function(material, diffuse, specular, bumpSampler) {
  var p = o3djs.effect;
  var str = p.BEGIN_OUT_STRUCT +
      p.VARYING + p.FLOAT4 + ' ' +
      p.VARYING_DECLARATION_PREFIX + 'position' +
      p.semanticSuffix('POSITION') + ';\n' +
      p.buildTexCoords(material, true) +
      p.buildBumpOutputCoords(bumpSampler);
  if (diffuse || specular) {
    str += p.VARYING + p.FLOAT3 + ' ' +
        p.VARYING_DECLARATION_PREFIX + 'normal' +
        p.semanticSuffix('TEXCOORD' +
           p.interpolant_++ + '') + ';\n' +
        p.VARYING + p.FLOAT3 + ' ' +
        p.VARYING_DECLARATION_PREFIX + 'surfaceToLight' +
        p.semanticSuffix(
            'TEXCOORD' + p.interpolant_++ + '') + ';\n';
  }
  if (specular) {
    str += p.VARYING + p.FLOAT3 + ' ' +
        p.VARYING_DECLARATION_PREFIX + 'surfaceToView' +
        p.semanticSuffix(
            'TEXCOORD' + p.interpolant_++ + '') + ';\n';
  }
  str += p.END_STRUCT;
  p.varying_decls_ = str;
  return str;
};


/**
 * An integer value which keeps track of the next available interpolant.
 * @type {number}
 * @private
 */
o3djs.effect.interpolant_ = 0;

/**
 * Builds the texture coordinate declaration for a given color input
 * (usually emissive, ambient, 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 {boolean} varying Whether these vertex declarations should
 *     be written as varying values.
 * @param {string} name The name of the color input.
 * @return {string} The code for the texture coordinate declaration.
 */
o3djs.effect.buildTexCoord = function(material, varying, name) {
  var p = o3djs.effect;
  // In the GLSL version we need to name the incoming attributes by
  // the semantic name in order for them to get hooked up correctly.
  if (material.getParam(name + 'Sampler')) {
    if (varying) {
      return '  ' + p.VARYING + p.FLOAT2 + ' ' +
          p.VARYING_DECLARATION_PREFIX + name + 'UV' +
          p.semanticSuffix(
              'TEXCOORD' + p.interpolant_++ + '') + ';\n';
    } else {
      var desiredName = name + 'UV';
      var semantic = 'TEXCOORD' + p.interpolant_++;
      var outputName = p.getAttributeName_(desiredName, semantic);
      if (p.semanticNameMap) {
        p.nameToSemanticMap_[desiredName] = semantic;
      }
      return '  ' + p.ATTRIBUTE + p.FLOAT2 + ' ' + outputName +
          p.semanticSuffix(semantic) + ';\n';
    }
  } else {
    return '';
  }
};

/**
 * Builds all the texture coordinate declarations for a vertex attribute
 * declaration.
 * @param {!o3d.Material} material The material to inspect.
 * @param {boolean} varying Whether these vertex declarations should
 *     be written as varying values.
 * @return {string} The code for the texture coordinate declarations.
 */
o3djs.effect.buildTexCoords = function(material, varying) {
  var p = o3djs.effect;
  p.interpolant_ = 0;
  if (!varying) {
    p.nameToSemanticMap_ = {};
  }
  return p.buildTexCoord(material, varying, 'emissive') +
         p.buildTexCoord(material, varying, 'ambient') +
         p.buildTexCoord(material, varying, 'diffuse') +
         p.buildTexCoord(material, varying, '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.
 */
o3djs.effect.buildUVPassthrough = function(material, name) {
  var p = o3djs.effect;
  if (material.getParam(name + 'Sampler')) {
    var sourceName = name + 'UV';
    var destName = sourceName;
    var semantic = p.nameToSemanticMap_[sourceName];
    if (semantic) {
      sourceName = p.getAttributeName_(sourceName, semantic);
    }
    return '  ' + p.VERTEX_VARYING_PREFIX + destName + ' = ' +
        p.ATTRIBUTE_PREFIX + sourceName + ';\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.
 */
o3djs.effect.buildUVPassthroughs = function(material) {
  var p = o3djs.effect;
  // TODO(petersont): in the GLSL implementation we need to generate
  // the code for these attributes before we can pass their values
  // through, because in this implementation their names must be their
  // semantics (i.e., "texCoord4") rather than these chosen names.
  // Currently bumpUV is the only one which does not obey this rule.
  return p.buildUVPassthrough(material, 'emissive') +
         p.buildUVPassthrough(material, 'ambient') +
         p.buildUVPassthrough(material, 'diffuse') +
         p.buildUVPassthrough(material, 'specular') +
         p.buildUVPassthrough(material, 'bump');
};


/**
 * Builds bump input coords if needed.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The code for bump input coords.
 */
o3djs.effect.buildBumpInputCoords = function(bumpSampler) {
  var p = o3djs.effect;
  return bumpSampler ?
      ('  ' + p.FLOAT3 + ' tangent' +
          p.semanticSuffix('TANGENT') + ';\n' +
       '  ' + p.FLOAT3 + ' binormal' +
          p.semanticSuffix('BINORMAL') + ';\n' +
       '  ' + p.FLOAT2 + ' bumpUV' +
          p.semanticSuffix(
              'TEXCOORD' + p.interpolant_++) + ';\n') : '';
};


/**
 * Builds bump output coords if needed.
 * @param {boolean} bumpSampler Whether there is a bump sampler.
 * @return {string} The code for bump input coords.
 */
o3djs.effect.buildBumpOutputCoords = function(bumpSampler) {
  var p = o3djs.effect;
  return bumpSampler ?
      ('  ' + p.FLOAT3 + ' tangent' +
          p.semanticSuffix(
              'TEXCOORD' + p.interpolant_++) + ';\n' +
       '  ' + p.FLOAT3 + ' binormal' +
          p.semanticSuffix('TEXCOORD' +
              p.interpolant_++) + ';\n' +
       '  ' + p.FLOAT2 + ' bumpUV' +
          p.semanticSuffix(
              'TEXCOORD' + p.interpolant_++) + ';\n') : '';
};


/**
 * Builds vertex and fragment shader string for a 2-color checker effect.
 * @return {string} The effect code for the shader, ready to be parsed.
 */
o3djs.effect.buildCheckerShaderString = function() {
  var p = o3djs.effect;
  var varyingDecls = p.BEGIN_OUT_STRUCT +
    p.VARYING + p.FLOAT4 + ' ' +
    p.VERTEX_VARYING_PREFIX + 'position' +
    p.semanticSuffix('POSITION') + ';\n' +
    p.VARYING + p.FLOAT2 + ' ' +
    p.VERTEX_VARYING_PREFIX + 'texCoord' +
    p.semanticSuffix('TEXCOORD0') + ';\n' +
    p.VARYING + p.FLOAT3 + ' ' +
    p.VERTEX_VARYING_PREFIX + 'normal' +
    p.semanticSuffix('TEXCOORD1') + ';\n' +
    p.VARYING + p.FLOAT3 + ' ' +
    p.VERTEX_VARYING_PREFIX + 'worldPosition' +
    p.semanticSuffix('TEXCOORD2') + ';\n' +
    p.END_STRUCT;

  return 'uniform ' + p.MATRIX4 + ' worldViewProjection' +
  p.semanticSuffix('WORLDVIEWPROJECTION') + ';\n' +
    'uniform ' + p.MATRIX4 + ' worldInverseTranspose' +
    p.semanticSuffix('WORLDINVERSETRANSPOSE') + ';\n' +
    'uniform ' + p.MATRIX4 + ' world' +
    p.semanticSuffix('WORLD') + ';\n' +
    '\n' +
    p.BEGIN_IN_STRUCT +
    p.ATTRIBUTE + p.FLOAT4 + ' position' +
    p.semanticSuffix('POSITION') + ';\n' +
    p.ATTRIBUTE + p.FLOAT3 + ' normal' +
    p.semanticSuffix('NORMAL') + ';\n' +
    p.ATTRIBUTE + p.FLOAT2 + ' texCoord0' +
    p.semanticSuffix('TEXCOORD0') + ';\n' +
    p.END_STRUCT +
    '\n' +
    varyingDecls +
    '\n' +
    p.beginVertexShaderMain() +
    '  ' + p.VERTEX_VARYING_PREFIX + 'position = ' +
    p.mul(p.ATTRIBUTE_PREFIX + 'position',
        'worldViewProjection') + ';\n' +
    '  ' + p.VERTEX_VARYING_PREFIX + 'normal = ' +
    p.mul(p.FLOAT4 + '(' +
    p.ATTRIBUTE_PREFIX + 'normal, 0.0)',
        'worldInverseTranspose') + '.xyz;\n' +
    '  ' + p.VERTEX_VARYING_PREFIX + 'worldPosition = ' +
        p.mul(p.ATTRIBUTE_PREFIX + 'position', 'world') +
    '.xyz;\n' +
    '  ' + p.VERTEX_VARYING_PREFIX + 'texCoord = ' +
    p.ATTRIBUTE_PREFIX + 'texCoord0;\n' +
    p.endVertexShaderMain() +
    '\n' +
    p.pixelShaderHeader() +
    'uniform ' + p.FLOAT4 + ' color1;\n' +
    'uniform ' + p.FLOAT4 + ' color2;\n' +
    'uniform float checkSize;\n' +
    'uniform ' + p.FLOAT3 + ' lightWorldPos;\n' +
    'uniform ' + p.FLOAT3 + ' lightColor;\n' +
    '\n' +
    p.repeatVaryingDecls(varyingDecls) +
    p.FLOAT4 + ' checker(' + p.FLOAT2 + ' uv) {\n' +
    '  float fmodResult = ' + p.MOD + '(' +
    '    floor(checkSize * uv.x) + \n' +
    '    floor(checkSize * uv.y), 2.0);\n' +
    '  return (fmodResult < 1.0) ? color1 : color2;\n' +
    '}\n\n' +
    p.beginPixelShaderMain() +
    '  ' + p.FLOAT3 + ' surfaceToLight = \n' +
    '      normalize(lightWorldPos - ' +
    p.PIXEL_VARYING_PREFIX + 'worldPosition);\n' +
    '  ' + p.FLOAT3 + ' worldNormal = normalize(' +
    p.PIXEL_VARYING_PREFIX + 'normal);\n' +
    '  ' + p.FLOAT4 + ' check = checker(' +
    p.PIXEL_VARYING_PREFIX + 'texCoord);\n' +
    '  float directionalIntensity = \n' +
    '      clamp(dot(worldNormal, surfaceToLight), 0.0, 1.0);\n' +
    '  ' + p.FLOAT4 +
    ' outColor = directionalIntensity * check;\n' +
    p.endPixelShaderMain(
        p.FLOAT4 + '(outColor.rgb, check.a)') +
    '\n' + p.entryPoints() +
    p.matrixLoadOrder();
};



/**
 * 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.<string>}
 */
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 p = o3djs.effect;
  var lightingType = p.getColladaLightingType(material);
  if (!p.isColladaLightingType(lightingType)) {
    throw 'not a collada standard material';
  }
  var colladaSamplers = p.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 p = o3djs.effect;
  var effect = pack.getObjects(url, 'o3d.Effect')[0];
  if (!effect) {
    effect = pack.createObject('Effect');
    p.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 p = o3djs.effect;
  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 buildCommonVertexUniforms = function() {
    return 'uniform ' + p.MATRIX4 + ' worldViewProjection' +
        p.semanticSuffix('WORLDVIEWPROJECTION') + ';\n' +
        'uniform ' + p.FLOAT3 + ' lightWorldPos;\n';
  };

  /**
   * Builds uniform variables common to all standard lighting types.
   * @return {string} The effect code for the common shader uniforms.
   */
  var buildCommonPixelUniforms = function() {
    return 'uniform ' + p.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 ' + p.MATRIX4 + ' world' +
        p.semanticSuffix('WORLD') + ';\n' +
        'uniform ' + p.MATRIX4 +
        ' viewInverse' + p.semanticSuffix('VIEWINVERSE') + ';\n' +
        'uniform ' + p.MATRIX4 + ' worldInverseTranspose' +
        p.semanticSuffix('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.<string>} 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 'uniform sampler' + type + ' ' + name + 'Sampler;\n'
    } else if (opt_addColorParam) {
      descriptions.push(name + 'Color');
      return 'uniform ' + p.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 '  ' + p.FLOAT4 + ' ' + name + ' = ' + p.TEXTURE + type +
             '(' + name + 'Sampler, ' +
             p.PIXEL_VARYING_PREFIX + name + 'UV);\n'
    } else {
      return '';
    }
  };

  /**
   * Builds vertex and fragment shader string for the Constant lighting type.
   * @param {!o3d.Material} material The material for which to build
   *     shaders.
   * @param {!Array.<string>} 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 buildCommonVertexUniforms() +
           buildVertexDecls(material, false, false) +
           p.beginVertexShaderMain() +
           positionVertexShaderCode() +
           p.buildUVPassthroughs(material) +
           p.endVertexShaderMain() +
           p.pixelShaderHeader(material, false, false, bumpSampler) +
           buildCommonPixelUniforms() +
           p.repeatVaryingDecls() +
           buildColorParam(material, descriptions, 'emissive') +
           p.beginPixelShaderMain() +
           getColorParam(material, 'emissive') +
           p.endPixelShaderMain('emissive') +
           p.entryPoints() +
           p.matrixLoadOrder();
  };

  /**
   * Builds vertex and fragment shader string for the Lambert lighting type.
   * @param {!o3d.Material} material The material for which to build
   *     shaders.
   * @param {!Array.<string>} 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 buildCommonVertexUniforms() +
           buildLightingUniforms() +
           buildVertexDecls(material, true, false) +
           p.beginVertexShaderMain() +
           p.buildUVPassthroughs(material) +
           positionVertexShaderCode() +
           normalVertexShaderCode() +
           surfaceToLightVertexShaderCode() +
           bumpVertexShaderCode() +
           p.endVertexShaderMain() +
           p.pixelShaderHeader(material, true, false) +
           buildCommonPixelUniforms() +
           p.repeatVaryingDecls() +
           buildColorParam(material, descriptions, 'emissive') +
           buildColorParam(material, descriptions, 'ambient') +
           buildColorParam(material, descriptions, 'diffuse') +
           buildColorParam(material, descriptions, 'bump', false) +
           p.utilityFunctions() +
           p.beginPixelShaderMain() +
           getColorParam(material, 'emissive') +
           getColorParam(material, 'ambient') +
           getColorParam(material, 'diffuse') +
           getNormalShaderCode() +
           '  ' + p.FLOAT3 + ' surfaceToLight = normalize(' +
           p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' +
           '  ' + p.FLOAT4 +
           ' litR = lit(dot(normal, surfaceToLight), 0.0, 0.0);\n' +
           p.endPixelShaderMain(p.FLOAT4 +
           '((emissive +\n' +
           '      lightColor *' +
           ' (ambient * diffuse + diffuse * litR.y)).rgb,\n' +
           '          diffuse.a)') +
           p.entryPoints() +
           p.matrixLoadOrder();
  };

  /**
   * Builds vertex and fragment shader string for the Blinn lighting type.
   * @param {!o3d.Material} material The material for which to build
   *     shaders.
   * @param {!Array.<string>} 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('phong');
    return buildCommonVertexUniforms() +
        buildLightingUniforms() +
        buildVertexDecls(material, true, true) +
        p.beginVertexShaderMain() +
        p.buildUVPassthroughs(material) +
        positionVertexShaderCode() +
        normalVertexShaderCode() +
        surfaceToLightVertexShaderCode() +
        surfaceToViewVertexShaderCode() +
        bumpVertexShaderCode() +
        p.endVertexShaderMain() +
        p.pixelShaderHeader(material, true, true) +
        buildCommonPixelUniforms() +
        p.repeatVaryingDecls() +
        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' +
        p.utilityFunctions() +
        p.beginPixelShaderMain() +
        getColorParam(material, 'emissive') +
        getColorParam(material, 'ambient') +
        getColorParam(material, 'diffuse') +
        getColorParam(material, 'specular') +
        getNormalShaderCode() +
        '  ' + p.FLOAT3 + ' surfaceToLight = normalize(' +
        p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' +
        '  ' + p.FLOAT3 + ' surfaceToView = normalize(' +
        p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' +
        '  ' + p.FLOAT3 +
        ' halfVector = normalize(surfaceToLight + ' +
        p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' +
        '  ' + p.FLOAT4 +
        ' litR = lit(dot(normal, surfaceToLight), \n' +
        '                    dot(normal, halfVector), shininess);\n' +
        p.endPixelShaderMain( p.FLOAT4 +
        '((emissive +\n' +
        '  lightColor *' +
        ' (ambient * diffuse + diffuse * litR.y +\n' +
        '                        + specular * litR.z *' +
        ' specularFactor)).rgb,\n' +
        '      diffuse.a)') +
        p.entryPoints() +
        p.matrixLoadOrder();
  };

  /**
   * Builds vertex and fragment shader string for the Phong lighting type.
   * @param {!o3d.Material} material The material for which to build
   *     shaders.
   * @param {!Array.<string>} 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 buildCommonVertexUniforms() +
        buildLightingUniforms() +
        buildVertexDecls(material, true, true) +
        p.beginVertexShaderMain() +
        p.buildUVPassthroughs(material) +
        positionVertexShaderCode() +
        normalVertexShaderCode() +
        surfaceToLightVertexShaderCode() +
        surfaceToViewVertexShaderCode() +
        bumpVertexShaderCode() +
        p.endVertexShaderMain() +
        p.pixelShaderHeader(material, true, true) +
        buildCommonPixelUniforms() +
        p.repeatVaryingDecls() +
        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' +
        p.utilityFunctions() +
        p.beginPixelShaderMain() +
        getColorParam(material, 'emissive') +
        getColorParam(material, 'ambient') +
        getColorParam(material, 'diffuse') +
        getColorParam(material, 'specular') +
        getNormalShaderCode() +
        '  ' + p.FLOAT3 + ' surfaceToLight = normalize(' +
        p.PIXEL_VARYING_PREFIX + 'surfaceToLight);\n' +
        '  ' + p.FLOAT3 + ' surfaceToView = normalize(' +
        p.PIXEL_VARYING_PREFIX + 'surfaceToView);\n' +
        '  ' + p.FLOAT3 +
        ' halfVector = normalize(surfaceToLight + surfaceToView);\n' +
        '  ' + p.FLOAT4 +
        ' litR = lit(dot(normal, surfaceToLight), \n' +
        '                    dot(normal, halfVector), shininess);\n' +
        p.endPixelShaderMain(p.FLOAT4 +
        '((emissive +\n' +
        '  lightColor * (ambient * diffuse + diffuse * litR.y +\n' +
        '                        + specular * litR.z *' +
        ' specularFactor)).rgb,\n' +
        '      diffuse.a)') +
        p.entryPoints() +
        p.matrixLoadOrder();
  };

  /**
   * Builds the position code for the vertex shader.
   * @return {string} The code for the vertex shader.
   */
  var positionVertexShaderCode = function() {
    return '  ' + p.VERTEX_VARYING_PREFIX + 'position = ' +
        p.mul(p.ATTRIBUTE_PREFIX +
        'position', 'worldViewProjection') + ';\n';
  };

  /**
   * Builds the normal code for the vertex shader.
   * @return {string} The code for the vertex shader.
   */
  var normalVertexShaderCode = function() {
    return '  ' + p.VERTEX_VARYING_PREFIX + 'normal = ' +
        p.mul(p.FLOAT4 + '(' +
        p.ATTRIBUTE_PREFIX +
        'normal, 0)', '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 '  ' + p.VERTEX_VARYING_PREFIX +
        'surfaceToLight = lightWorldPos - \n' +
           '                          ' +
           p.mul(p.ATTRIBUTE_PREFIX + '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 '  ' + p.VERTEX_VARYING_PREFIX +
        'surfaceToView = (viewInverse[3] - ' +
         p.mul(p.ATTRIBUTE_PREFIX + 'position', 'world') + ').xyz;\n';
  };

  /**
   * Builds the normal map part of the vertex shader.
   * @param {boolean} opt_bumpSampler Whether there is a bump
   *     sampler. Default = false.
   * @return {string} The code for normal mapping in the vertex shader.
   */
  var bumpVertexShaderCode = function(opt_bumpSampler) {
    return bumpSampler ?
        ('  ' + p.VERTEX_VARYING_PREFIX + 'binormal = ' +
         p.mul(p.FLOAT4 + '(' +
         p.ATTRIBUTE_PREFIX + 'binormal, 0)',
             'worldInverseTranspose') + '.xyz;\n' +
         '  ' + p.VERTEX_VARYING_PREFIX + 'tangent = ' +
         p.mul(p.FLOAT4 +
         '(' + p.ATTRIBUTE_PREFIX + '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 ?
        (p.MATRIX3 + ' tangentToWorld = ' + p.MATRIX3 +
            '(' + p.ATTRIBUTE_PREFIX + 'tangent,\n' +
         '                                   ' +
         p.ATTRIBUTE_PREFIX + 'binormal,\n' +
         '                                   ' +
         p.ATTRIBUTE_PREFIX + 'normal);\n' +
         p.FLOAT3 + ' tangentNormal = tex2D(bumpSampler, ' +
         p.ATTRIBUTE_PREFIX + 'bumpUV.xy).xyz -\n' +
         '                       ' + p.FLOAT3 +
         '(0.5, 0.5, 0.5);\n' + p.FLOAT3 + ' normal = ' +
         p.mul('tangentNormal', 'tangentToWorld') + ';\n' +
         'normal = normalize(' + p.PIXEL_VARYING_PREFIX +
         'normal);\n') : '  ' + p.FLOAT3 + ' normal = normalize(' +
         p.PIXEL_VARYING_PREFIX + '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) {
    return p.buildAttributeDecls(
        material, diffuse, specular, bumpSampler) +
        p.buildVaryingDecls(
            material, diffuse, specular, bumpSampler);
  };


  // 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;
};


// For compatability with o3d code, the default language is o3d shading
// language.
o3djs.effect.setLanguage('o3d');