diff options
Diffstat (limited to 'o3d/samples/o3djs/material.js')
-rw-r--r-- | o3d/samples/o3djs/material.js | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/o3d/samples/o3djs/material.js b/o3d/samples/o3djs/material.js new file mode 100644 index 0000000..8e0f00a --- /dev/null +++ b/o3d/samples/o3djs/material.js @@ -0,0 +1,461 @@ +/* + * 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 + // <transparency> and <transparent> 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. 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, + 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. + * + * <pre> + * <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> + * </pre> + * + * @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; +}; + +/** + * 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; + effect.createUniformParameters(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 + * + * <pre> + * var params = o3djs.material.createStandardParams(pack); + * param.lightColor.value = [1, 0, 0, 1]; // red + * param.lightWorldPos.value = [1000, 2000, 3000]; // set light position. + * </pre> + * + * 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 + * + * <pre> + * var params = o3djs.material.createAndBindStandardParams(pack); + * param.lightColor.value = [1, 0, 0, 1]; // red + * param.lightWorldPos.value = [1000, 2000, 3000]; // set light position. + * </pre> + * + * @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; +}; + |