/* * 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 for helping setup * materials for o3d. It puts them in the "material" 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.material'); o3djs.require('o3djs.math'); o3djs.require('o3djs.effect'); /** * A Module for materials. * @namespace */ o3djs.material = o3djs.material || {}; /** * Checks a material's params by name to see if it possibly has non 1.0 alpha. * Given a name, checks for a ParamTexture called 'nameTexture' and if that * fails, checks for a ParamFloat4 'name'. * @private * @param {!o3d.Material} material Materal to check. * @param {string} name name of color params to check. * @return {{found: boolean, nonOneAlpha: boolean}} found is true if one of * the params was found, nonOneAlpha is true if that param had non 1.0 * alpha. */ o3djs.material.hasNonOneAlpha_ = function(material, name) { var found = false; var nonOneAlpha = false; var texture = null; var samplerParam = material.getParam(name + 'Sampler'); if (samplerParam && samplerParam.isAClassName('o3d.ParamSampler')) { found = true; var sampler = samplerParam.value; if (sampler) { texture = sampler.texture; } } else { var textureParam = material.getParam(name + 'Texture'); if (textureParam && textureParam.isAClassName('o3d.ParamTexture')) { found = true; texture = textureParam.value; } } if (texture && !texture.alphaIsOne) { nonOneAlpha = true; } if (!found) { var colorParam = material.getParam(name); if (colorParam && colorParam.isAClassName('o3d.ParamFloat4')) { found = true; // TODO: this check does not work. We need to check for the // and elements or something. // if (colorParam.value[3] < 1) { // nonOneAlpha = true; // } } } return {found: found, nonOneAlpha: nonOneAlpha}; }; /** * Prepares a material by setting their drawList and possibly creating * an standard effect if one does not already exist. * * This function is very specific to our sample importer. It expects that if * no Effect exists on a material that certain extra Params have been created * on the Material to give us instructions on what to Effects to create. * * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createView. * @param {!o3d.Material} material to prepare. * @param {string} opt_effectType type of effect to create ('phong', * 'lambert', 'constant'). * * @see o3djs.material.attachStandardEffect */ o3djs.material.prepareMaterial = function(pack, viewInfo, material, opt_effectType) { // Assume we want the performance list var drawList = viewInfo.performanceDrawList; // First check if we have a tag telling us that it is or is not // transparent if (!material.drawList) { var param = material.getParam('collada.transparent'); if (param && param.className == 'o3d.ParamBoolean') { material.drawList = param.value ? viewInfo.zOrderedDrawList : viewInfo.performanceDrawList; } } // If the material has no effect, try to build shaders for it. if (!material.effect) { // If the user didn't pass an effect type in see if one was stored there // by our importer. if (!opt_effectType) { // Retrieve the lightingType parameter from the material, if any. var lightingType = o3djs.effect.getColladaLightingType(material); if (lightingType) { opt_effectType = lightingType; } } if (opt_effectType) { o3djs.material.attachStandardEffect(pack, material, viewInfo, opt_effectType); // For collada common profile stuff guess what drawList to use. Note: We // can only do this for collada common profile stuff because we supply // the shaders and therefore now the inputs and how they are used. // For other shaders you've got to do this stuff yourself. On top of // that this is a total guess. Just because a texture has no alpha // it does not follow that you don't want it in the zOrderedDrawList. // That is application specific. Here we are just making a guess and // hoping that it covers most cases. if (material.drawList == null) { // Check the common profile params. var result = o3djs.material.hasNonOneAlpha_(material, 'diffuse'); if (!result.found) { result = o3djs.material.hasNonOneAlpha_(material, 'emissive'); } if (result.nonOneAlpha) { drawList = viewInfo.zOrderedDrawList; } } } } if (!material.drawList) { material.drawList = drawList; } }; /** * Prepares all the materials in the given pack by setting their drawList and * if they don't have an Effect, creating one for them. * * This function is very specific to our sample importer. It expects that if * no Effect exists on a material that certain extra Params have been created * on the Material to give us instructions on what to Effects to create. * * @param {!o3d.Pack} pack Pack to prepare. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createView. * @param {!o3d.Pack} opt_effectPack Pack to create effects in. If this * is not specifed the pack to prepare above will be used. * * @see o3djs.material.prepareMaterial */ o3djs.material.prepareMaterials = function(pack, viewInfo, opt_effectPack) { var materials = pack.getObjectsByClassName('o3d.Material'); for (var mm = 0; mm < materials.length; mm++) { o3djs.material.prepareMaterial(opt_effectPack || pack, viewInfo, materials[mm]); } }; /** * Builds a standard effect for a given material. * If the material already has an effect, none is created. * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3d.Material} material The material for which to create an * effect. * @param {string} effectType Type of effect to create ('phong', 'lambert', * 'constant'). * * @see o3djs.effect.attachStandardShader */ o3djs.material.attachStandardEffectEx = function(pack, material, effectType) { if (!material.effect) { if (!o3djs.effect.attachStandardShader(pack, material, [0, 0, 0], effectType)) { throw 'Could not attach a standard effect'; } } }; /** * Builds a standard effect for a given material. The position of the * default light is set to the view position. If the material already has * an effect, none is created. * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3d.Material} material The material for which to create an * effect. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createView. * @param {string} effectType Type of effect to create ('phong', 'lambert', * 'constant'). * * @see o3djs.effect.attachStandardShader */ o3djs.material.attachStandardEffect = function(pack, material, viewInfo, effectType) { if (!material.effect) { var lightPos = o3djs.math.matrix4.getTranslation( o3djs.math.inverse(viewInfo.drawContext.view)); if (!o3djs.effect.attachStandardShader(pack, material, lightPos, // TODO(gman): remove this effectType)) { throw 'Could not attach a standard effect'; } } }; /** * Prepares all the materials in the given pack by setting their * drawList. * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3d.DrawList} drawList DrawList to assign to materials. */ o3djs.material.setDrawListOnMaterials = function(pack, drawList) { var materials = pack.getObjectsByClassName('o3d.Material'); for (var mm = 0; mm < materials.length; mm++) { var material = materials[mm]; // TODO: look at flags on the material left by the importer // to decide which draw list to use. material.drawList = drawList; } }; /** * This function creates a basic material for when you just want to get * something on the screen quickly without having to manually setup shaders. * You can call this function something like. * *
 * <html><body>
 * <script type="text/javascript" src="o3djs/all.js">
 * </script>
 * <script>
 * window.onload = init;
 *
 * function init() {
 *   o3djs.base.makeClients(initStep2);
 * }
 *
 * function initStep2(clientElements) {
 *   var clientElement = clientElements[0];
 *   var client = clientElement.client;
 *   var pack = client.createPack();
 *   var viewInfo = o3djs.rendergraph.createBasicView(
 *       pack,
 *       client.root,
 *       client.renderGraphRoot);
 *   var material = o3djs.material.createBasicMaterial(
 *       pack,
 *       viewInfo,
 *       [1, 0, 0, 1]);  // red
 *   var shape = o3djs.primitives.createCube(pack, material, 10);
 *   var transform = pack.createObject('Transform');
 *   transform.parent = client.root;
 *   transform.addShape(shape);
 *   o3djs.camera.fitContextToScene(client.root,
 *                                  client.width,
 *                                  client.height,
 *                                  viewInfo.drawContext);
 * }
 * </script>
 * <div id="o3d" style="width: 600px; height: 600px"></div>
 * </body></html>
 * 
* * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createBasicView. * @param {(!o3djs.math.Vector4|!o3d.Texture)} colorOrTexture Either a color in * the format [r, g, b, a] or an O3D texture. * @param {boolean} opt_transparent Whether or not the material is transparent. * Defaults to false. * @return {!o3d.Material} The created material. */ o3djs.material.createBasicMaterial = function(pack, viewInfo, colorOrTexture, opt_transparent) { var material = pack.createObject('Material'); material.drawList = opt_transparent ? viewInfo.zOrderedDrawList : viewInfo.performanceDrawList; // If it has a length assume it's a color, otherwise assume it's a texture. if (colorOrTexture.length) { material.createParam('diffuse', 'ParamFloat4').value = colorOrTexture; } else { var paramSampler = material.createParam('diffuseSampler', 'ParamSampler'); var sampler = pack.createObject('Sampler'); paramSampler.value = sampler; sampler.texture = colorOrTexture; } // Create some suitable defaults for the material to save the user having // to know all this stuff right off the bat. material.createParam('emissive', 'ParamFloat4').value = [0, 0, 0, 1]; material.createParam('ambient', 'ParamFloat4').value = [0, 0, 0, 1]; material.createParam('specular', 'ParamFloat4').value = [1, 1, 1, 1]; material.createParam('shininess', 'ParamFloat').value = 50; material.createParam('specularFactor', 'ParamFloat').value = 1; material.createParam('lightColor', 'ParamFloat4').value = [1, 1, 1, 1]; var lightPositionParam = material.createParam('lightWorldPos', 'ParamFloat3'); o3djs.material.attachStandardEffect(pack, material, viewInfo, 'phong'); // We have to set the light position after calling attachStandardEffect // because attachStandardEffect sets it based on the view. lightPositionParam.value = [1000, 2000, 3000]; return material; }; /** * This function creates a constant material. No lighting. It is especially * useful for debugging shapes and 2d UI elements. * * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3d.DrawList} drawList The DrawList for the material. * @param {(!o3djs.math.Vector4|!o3d.Texture)} colorOrTexture Either a color in * the format [r, g, b, a] or an O3D texture. * @return {!o3d.Material} The created material. */ o3djs.material.createConstantMaterialEx = function(pack, drawList, colorOrTexture) { var material = pack.createObject('Material'); material.drawList = drawList; // If it has a length assume it's a color, otherwise assume it's a texture. if (colorOrTexture.length) { material.createParam('emissive', 'ParamFloat4').value = colorOrTexture; } else { var paramSampler = material.createParam('emissiveSampler', 'ParamSampler'); var sampler = pack.createObject('Sampler'); paramSampler.value = sampler; sampler.texture = colorOrTexture; } o3djs.material.attachStandardEffectEx(pack, material, 'constant'); return material; }; /** * This function creates a constant material. No lighting. It is especially * useful for debugging shapes and 2d UI elements. * * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createBasicView. * @param {(!o3djs.math.Vector4|!o3d.Texture)} colorOrTexture Either a color in * the format [r, g, b, a] or an O3D texture. * @param {boolean} opt_transparent Whether or not the material is transparent. * Defaults to false. * @return {!o3d.Material} The created material. */ o3djs.material.createConstantMaterial = function(pack, viewInfo, colorOrTexture, opt_transparent) { return o3djs.material.createConstantMaterialEx( pack, opt_transparent ? viewInfo.zOrderedDrawList : viewInfo.performanceDrawList, colorOrTexture) }; /** * This function creates 2 color procedureal texture material. * * @see o3djs.material.createBasicMaterial * * @param {!o3d.Pack} pack Pack to manage created objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from * o3djs.rendergraph.createBasicView. * @param {!o3djs.math.Vector4} opt_color1 a color in the format [r, g, b, a]. * Defaults to a medium blue-green. * @param {!o3djs.math.Vector4} opt_color2 a color in the format [r, g, b, a]. * Defaults to a light blue-green. * @param {boolean} opt_transparent Whether or not the material is transparent. * Defaults to false. * @param {number} opt_checkSize Defaults to 10 units. * @return {!o3d.Material} The created material. */ o3djs.material.createCheckerMaterial = function(pack, viewInfo, opt_color1, opt_color2, opt_transparent, opt_checkSize) { opt_color1 = opt_color1 || [0.4, 0.5, 0.5, 1]; opt_color2 = opt_color2 || [0.6, 0.8, 0.8, 1]; opt_checkSize = opt_checkSize || 10; var effect = o3djs.effect.createCheckerEffect(pack); var material = pack.createObject('Material'); material.effect = effect; material.drawList = opt_transparent ? viewInfo.zOrderedDrawList : viewInfo.performanceDrawList; o3djs.effect.createUniformParameters(pack, effect, material); material.getParam('color1').value = opt_color1; material.getParam('color2').value = opt_color2; material.getParam('checkSize').value = opt_checkSize; return material; }; /** * Creates a material for an effect loaded from a file. * If the effect has already been loaded in the pack it will be reused. * @param {!o3d.Pack} pack Pack to create effect in. * @param {string} url Url for effect file. * @param {!o3d.DrawList} drawList DrawList to assign effect to. * @return {!o3d.Material} The material. */ o3djs.material.createMaterialFromFile = function(pack, url, drawList) { var effect = o3djs.effect.createEffectFromFile(pack, url); var material = pack.createObject('Material'); material.effect = effect; material.drawList = drawList; o3djs.effect.createUniformParameters(pack, effect, material); return material; }; /** * Binds params to all materials in a pack by name. * @param {!o3d.Material} material Material to bind params on. * @param {!Object} params A object where each property is the name of a param * and its value is that param. */ o3djs.material.bindParamsOnMaterial = function(material, params) { for (var paramName in params) { var sourceParam = params[paramName]; var param = material.getParam(paramName); if (param && sourceParam.isAClassName(param.className)) { param.bind(sourceParam); } } }; /** * Binds params to all materials in a pack by name. * @param {!o3d.Pack} pack Pack with materials to bind. * @param {!Object} params A object where each property is the name of a param * and its value is that param. */ o3djs.material.bindParams = function(pack, params) { var materials = pack.getObjectsByClassName('o3d.Material'); for (var mm = 0; mm < materials.length; ++mm) { o3djs.material.bindParamsOnMaterial(materials[mm], params); } }; /** * Creates params from a param spec. * @param {!o3d.Pack} pack Pack to create params in. * @param {!Object} paramSpec An object where each property is the name of a * param and its value is the type of param. * @return {!Object} params A object where each property is the name of a param * and its value is that param. */ o3djs.material.createParams = function(pack, paramSpec) { var paramObject = pack.createObject('ParamObject'); var params = { }; for (var paramName in paramSpec) { params[paramName] = paramObject.createParam(paramName, paramSpec[paramName]); } return params; }; /** * Creates the global params need by the shaders built in effect.js * * The params currently created are 'lightColor' which is a ParamFloat4 and * 'lightWorldPos' which is a ParamFloat3. You can set their values like this * *
 * var params = o3djs.material.createStandardParams(pack);
 * param.lightColor.value = [1, 0, 0, 1];  // red
 * param.lightWorldPos.value = [1000, 2000, 3000];  // set light position.
 * 
* * Note: This function just creates the params. It does not connect them to * anything. See o3djs.material.createAndBindStandardParams, * o3djs.material.createParams and o3djs.material.bindParams * * @see o3djs.material.createAndBindStandardParams * @see o3djs.material.createParams * @see o3djs.material.bindParams * * @param {!o3d.Pack} pack Pack to create params in. * @return {!Object} params A object where each property is the name of a param * and its value is that param. */ o3djs.material.createStandardParams = function(pack) { var paramSpec = { 'lightColor': 'ParamFloat4', 'lightWorldPos': 'ParamFloat3'}; return o3djs.material.createParams(pack, paramSpec); }; /** * Creates the global params need by the shaders built in effect.js then binds * all the matching params on materials in pack to these global params. * * The params currently created are 'lightColor' which is a ParamFloat4 and * 'lightWorldPos' which is a ParamFloat3. You can set their values like this * *
 * var params = o3djs.material.createAndBindStandardParams(pack);
 * param.lightColor.value = [1, 0, 0, 1];  // red
 * param.lightWorldPos.value = [1000, 2000, 3000];  // set light position.
 * 
* * @see o3djs.material.createParams * @see o3djs.material.bindParams * * @param {!o3d.Pack} pack Pack to create params in. * @return {!Object} params A object where each property is the name of a param * and its value is that param. */ o3djs.material.createAndBindStandardParams = function(pack) { var params = o3djs.material.createStandardParams(pack); o3djs.material.bindParams(pack, params); return params; };