diff options
author | luchen@google.com <luchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-14 01:12:11 +0000 |
---|---|---|
committer | luchen@google.com <luchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-14 01:12:11 +0000 |
commit | d89ea3ced6389f04190eaa5431d32f67bca0056d (patch) | |
tree | c66dcde04e8e73c5d00d7f6fba92d485101e2ecd /o3d | |
parent | 7f7506f4f67c880b033d723dd1b7c29eac30dfac (diff) | |
download | chromium_src-d89ea3ced6389f04190eaa5431d32f67bca0056d.zip chromium_src-d89ea3ced6389f04190eaa5431d32f67bca0056d.tar.gz chromium_src-d89ea3ced6389f04190eaa5431d32f67bca0056d.tar.bz2 |
Implemented error texture support in client and ParamArray class. Fixed bug in effect.js that broke checkerboard texture if running using CG shaders.
TODO: Still a bug with render surface sets; will file issue and tackle problem in separate CL.
Review URL: http://codereview.chromium.org/2803007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52248 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d')
-rw-r--r-- | o3d/samples/o3d-webgl-samples/convolution.html | 398 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl-samples/error-texture.html | 265 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/base.js | 1 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/client.js | 91 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/effect.js | 114 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param.js | 41 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param_array.js | 184 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param_object.js | 1 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/sampler.js | 37 | ||||
-rw-r--r-- | o3d/samples/o3djs/effect.js | 8 |
10 files changed, 1091 insertions, 49 deletions
diff --git a/o3d/samples/o3d-webgl-samples/convolution.html b/o3d/samples/o3d-webgl-samples/convolution.html new file mode 100644 index 0000000..bc09031 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/convolution.html @@ -0,0 +1,398 @@ +<!-- +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. +--> + +<!-- +This sample shows how to create a separable convolution shader using +render targets. The kernel here is a Gaussian blur, but the same code +could be used for any kernel. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +O3D: Convolution Shader Sample +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.camera'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.pack'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.scene'); + +// Events +// init() once the page has finished loading. +window.onload = init; +window.onunload = uninit; + +// constants. +var RENDER_TARGET_WIDTH = 512; +var RENDER_TARGET_HEIGHT = 512; + +// global variables +var g_o3d; +var g_client; +var g_math; +var g_pack; +var g_teapotRoot; +var g_renderGraphRoot; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing. + +/** + * Loads a scene into the transform graph. + * @param {!o3d.Pack} pack Pack to load scene into. + * @param {string} fileName filename of the scene. + * @param {!o3d.Transform} parent parent node in the transform graph to + * which to load the scene into. + * @param {!o3djs.rendergraph.ViewInfo} viewInfo whose view and projection will + * be set from the scene after it's loaded. + */ +function loadScene(pack, fileName, parent, viewInfo) { + // Get our full path to the scene. + var scenePath = o3djs.util.getCurrentURI() + fileName; + + // Load the scene given the full path, and call the callback function + // when its done loading. + o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback); + + /** + * Our callback is called once the scene has been loaded into memory from the + * web or locally. + * @param {!o3d.Pack} pack The pack that was passed in above. + * @param {!o3d.Transform} parent The parent that was passed in above. + * @param {*} exception null if loading succeeded. + */ + function callback(pack, parent, exception) { + if (exception) { + alert('Could not load: ' + fileName + '\n' + exception); + return; + } + // Get a CameraInfo (an object with a view and projection matrix) + // using our javascript library function + var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras( + parent, + RENDER_TARGET_WIDTH, + RENDER_TARGET_HEIGHT); + + // Copy the view and projection to the passed in viewInfo structure.. + viewInfo.drawContext.view = cameraInfo.view; + viewInfo.drawContext.projection = cameraInfo.projection; + + // Generate draw elements and setup material draw lists. + o3djs.pack.preparePack(pack, viewInfo); + + g_finished = true; // for selenium testing. + } +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D and loads the scene into the transform graph. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3d = clientElements[0]; + g_o3d = o3d.o3d; + g_math = o3djs.math; + g_client = o3d.client; + + // Creates a pack to manage our resources/assets. + g_pack = g_client.createPack(); + + // Create the texture required for the color render-target. + var texture1 = g_pack.createTexture2D(RENDER_TARGET_WIDTH, + RENDER_TARGET_HEIGHT, + g_o3d.Texture.XRGB8, 1, true); + + // Create the texture required for the color render-target. + var texture2 = g_pack.createTexture2D(RENDER_TARGET_WIDTH, + RENDER_TARGET_HEIGHT, + g_o3d.Texture.XRGB8, 1, true); + + g_teapotRoot = g_pack.createObject('Transform'); + + var renderGraphRoot = g_client.renderGraphRoot; + + var xSigma = 4.0, ySigma = 4.0; + var xKernel = buildKernel(xSigma); + var yKernel = buildKernel(ySigma); + + var renderSurfaceSet1 = createRenderSurfaceSet(texture1); + var renderSurfaceSet2 = createRenderSurfaceSet(texture2); + + // Create the render graph for the teapot view, drawing the teapot into + // texture1 (via renderSurfaceSet1). + var teapotViewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_teapotRoot, + renderSurfaceSet1, + [1, 1, 1, 1]); + + // Create a Y convolution pass that convolves texture1 into texture2, using + // the X kernel. + var renderNode1 = createConvolutionPass(texture1, + renderSurfaceSet2, + xKernel, + 1.0 / texture1.width, + 0.0); + + // Create a Y convolution pass that convolves texture2 into the framebuffer, + // using the Y kernel. + var renderNode2 = createConvolutionPass(texture2, + g_client.renderGraphRoot, + yKernel, + 0.0, + 1.0 / texture2.height); + + // Load the scene into the transform graph as a child g_teapotRoot + loadScene(g_pack, '../assets/teapot/scene.json', g_teapotRoot, + teapotViewInfo); + + // Set a render callback. + g_client.setRenderCallback(onRender); +} + +// We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize +// anyway. +function gauss(x, sigma) { + return Math.exp(- (x * x) / (2.0 * sigma * sigma)); +} + +function buildKernel(sigma) { + var kMaxKernelSize = 25; + var kernelSize = 2 * Math.ceil(sigma * 3.0) + 1; + if (kernelSize > kMaxKernelSize) { + kernelSize = kMaxKernelSize; + } + var halfWidth = (kernelSize - 1) * 0.5 + var values = new Array(kernelSize); + var sum = 0.0; + for (var i = 0; i < kernelSize; ++i) { + values[i] = gauss(i - halfWidth, sigma); + sum += values[i]; + } + // Now normalize the kernel. + for (var i = 0; i < kernelSize; ++i) { + values[i] /= sum; + } + return values; +} + +function createConvolutionMaterial(viewInfo, kernelSize) { + var convFXString = document.getElementById('convFX').value; + convFXString = convFXString.replace(/KERNEL_WIDTH/g, kernelSize); + var convEffect = g_pack.createObject('Effect'); + convEffect.loadFromFXString(convFXString); + + var convMaterial = g_pack.createObject('Material'); + convMaterial.drawList = viewInfo.performanceDrawList; + convMaterial.effect = convEffect; + convEffect.createUniformParameters(convMaterial); + return convMaterial; +} + +function createRenderSurfaceSet(texture) { + var renderSurface = texture.getRenderSurface(0); + + // Create the depth-stencil buffer required when rendering this pass. + var depthSurface = g_pack.createDepthStencilSurface(RENDER_TARGET_WIDTH, + RENDER_TARGET_HEIGHT); + + var renderSurfaceSet = g_pack.createObject('RenderSurfaceSet'); + renderSurfaceSet.renderSurface = renderSurface; + renderSurfaceSet.renderDepthStencilSurface = depthSurface; + renderSurfaceSet.parent = g_client.renderGraphRoot; + return renderSurfaceSet; +} + +function createConvolutionPass(srcTexture, renderGraphRoot, kernel, x, y) { + // Create a root Transform for the convolution scene. + var root = g_pack.createObject('Transform'); + + // Create a basic view for the convolution scene. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + root, + renderGraphRoot, + [1, 1, 1, 1]); + + var material = createConvolutionMaterial(viewInfo, kernel.length); + var quadShape = o3djs.primitives.createPlane(g_pack, + material, + 2.0, + 2.0, + 1, + 1); + + // Attach the quad to the root of the convolution graph. + root.addShape(quadShape); + + // Rotate the view so we're looking at the XZ plane (where our quad is) + // Point the camera along the -Y axis + var target = [0, -1, 0]; + // Put the camera at the origin. + var eye = [0, 0, 0]; + // Define the up-vector as +Z + var up = [0, 0, 1]; + viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up); + + // Create an orthographic projection. + viewInfo.drawContext.projection = + g_math.matrix4.orthographic(-1, 1, -1, 1, -1, 1); + + // Generate draw elements and setup material draw lists for the + // convolution scene. + o3djs.pack.preparePack(g_pack, viewInfo); + + setConvolutionParameters(material, srcTexture, kernel, kernel.length, x, y); + return renderGraphRoot; +} + +function setConvolutionParameters(material, texture, kernel, kernelSize, + xIncrement, yIncrement) { + var imageParam = material.getParam('image'); + var kernelParam = material.getParam('kernel'); + var imageIncrement = material.getParam('imageIncrement'); + var sampler = g_pack.createObject('Sampler'); + sampler.texture = texture; + sampler.addressModeU = g_o3d.Sampler.CLAMP; + sampler.addressModeV = g_o3d.Sampler.CLAMP; + sampler.minFilter = g_o3d.Sampler.POINT; + sampler.magFilter = g_o3d.Sampler.POINT; + sampler.mipFilter = g_o3d.Sampler.NONE; + imageParam.value = sampler; + imageIncrement.value = [xIncrement, yIncrement]; + var paramArray = g_pack.createObject('ParamArray'); + var halfWidth = (kernelSize - 1) * 0.5; + for (var i = 0; i < kernelSize; ++i) { + var element = paramArray.createParam(i, 'ParamFloat'); + element.value = kernel[i]; + } + kernelParam.value = paramArray; +} + +/** + * Called every frame. + * @param {o3d.RenderEvent} renderEvent Rendering Information. + */ +function onRender(renderEvent) { + var elapsedTime = renderEvent.elapsedTime; + g_clock += elapsedTime * g_timeMult; + + g_teapotRoot.identity(); + g_teapotRoot.rotateX(g_clock); + g_teapotRoot.rotateY(g_clock * 1.3); +} + +/** + * Cleanup before exiting. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} +</script> +</head> +<body> +<h1>Convolution Shader Example</h1> +<p>This sample shows how to do 2D image processing using render targets. This +sample uses a convolution shader to do a 2D Gaussian blur, but the +same code could be used for any separable convolution kernel.</p> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 512px; height: 512px;"></div> +<!-- End of O3D plugin --> +<!-- + We embed the code for our effect inside this hidden textarea. + Effects contain the functions that define + the vertex and pixel shaders used by our shape. +--> +<!-- Don't render the textarea --> +<div style="display:none"> +<textarea id="convFX" name="convFX" cols="80" rows="20"> +uniform mat4 worldViewProjection; +uniform vec2 imageIncrement; + +attribute vec4 position; +attribute vec2 texCoord0; + +varying vec2 v_imageCoord; + +void main() { + gl_Position = worldViewProjection * position; + + // Offset image coords by half of kernel width, in image texels + v_imageCoord = texCoord0 - + (float(KERNEL_WIDTH - 1) / 2.0) * imageIncrement; +} + +// #o3d SplitMarker + +uniform sampler2D image; +uniform float kernel[ KERNEL_WIDTH ]; +uniform vec2 imageIncrement; + +varying vec2 v_imageCoord; + +void main() { + vec2 imageCoordTmp = v_imageCoord; + vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < KERNEL_WIDTH; ++i) { + sum += texture2D(image, imageCoordTmp) * kernel[i]; + imageCoordTmp += imageIncrement; + } + gl_FragColor = sum; +} + +// #o3d MatrixLoadOrder RowMajor +</textarea> +</div> +</body> + +</html> diff --git a/o3d/samples/o3d-webgl-samples/error-texture.html b/o3d/samples/o3d-webgl-samples/error-texture.html new file mode 100644 index 0000000..b415f44 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/error-texture.html @@ -0,0 +1,265 @@ +<!-- +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. +--> + +<!-- +This sample attempts to show what the error texture is, how to set it and +how turning it off will generate helpful error information. +--> +<html> +<head> +<title>Error Texture</title> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.effect'); + +// Events +// Run the init() once the page has finished loading. +// and unload() when the page is unloaded. +window.onload = init; +window.onunload = unload; + +// global variables +var g_o3d; +var g_math; +var g_client; +var g_pack; +var g_viewInfo; +var g_texture; +var g_errorMsgElement; + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D, loads the effect, and sets up some quads. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initialize global variables and libraries. + var o3dElement = clientElements[0]; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + g_client = o3dElement.client; + + g_errorMsgElement = + document.getElementById('errorMsg'); + + // Turn of the error callback that o3djs.base.init setup. + g_client.clearErrorCallback(); + + // Let us render on demand. + g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND; + + // Create a pack to manage our resources/assets + g_pack = g_client.createPack(); + + // Create the render graph for a view. + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + var clientWidth = g_client.width; + var clientHeight = g_client.height; + g_viewInfo.drawContext.projection = g_math.matrix4.orthographic( + -clientWidth * 0.5, + clientWidth * 0.5, + -clientHeight * 0.5, + clientHeight * 0.5, + 0.001, + 1000); + + g_viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 500, 0], // eye + [0, 0, 0], // target + [0, 0, -1]); // up + + // Create and load the effect. + var effect = g_pack.createObject('Effect'); + o3djs.effect.loadEffect(effect, '../shaders/texture-only-glsl.shader'); + + // Create a Material for the effect. + var myMaterial = g_pack.createObject('Material'); + + // Set the material's drawList + myMaterial.drawList = g_viewInfo.zOrderedDrawList; + + // Apply our effect to this material. + myMaterial.effect = effect; + + // Creates a quad using the effect. + var shape = o3djs.primitives.createPlane(g_pack, + myMaterial, + 1, + 1, + 1, + 1); + + var pixels = []; + for (var y = 0; y < 32; ++y) { + for (var x = 0; x < 32; ++x) { + var offset = (y * 32 + x) * 3; // rgb + var u = x / 32 * Math.PI * 0.5; + var v = y / 32 * Math.PI * 0.5; + pixels[offset + 0] = 0; // red + pixels[offset + 1] = Math.floor(y / 8) % 2; // green + pixels[offset + 2] = Math.floor(x / 8) % 2; // blue + } + } + var texture = g_pack.createTexture2D(32, 32, g_o3d.Texture.XRGB8, 1, false); + texture.set(0, pixels); + + // display the quad 4 times with situations + // by overriding the sampler on each instance. + for (var s = 0; s < 4; ++s) { + // create a transform for an instance + var transform = g_pack.createObject('Transform'); + transform.translate((s - 1.5) * 140, 0, 0); + transform.scale(128, 1, 128), + transform.parent = g_client.root; + transform.addShape(shape); + + // case 0: Correct Texture. + // case 1: ParamSampler and Sampler but no Texture + // case 2: ParamSampler but no Sampler, + // case 3: No ParamSampler. + if (s <= 2) { + // Create a ParamSampler on the transform with the same name as in + // the effect so this param will be used instead of the one on the + // material. + var samplerParam = transform.createParam('texSampler0', 'ParamSampler'); + + if (s <= 1) { + var sampler = g_pack.createObject('Sampler'); + sampler.name = "s2d"; + samplerParam.value = sampler; + sampler.addressModeU = g_o3d.Sampler.CLAMP; + sampler.addressModeV = g_o3d.Sampler.CLAMP; + if (s == 0) { + sampler.texture = texture; + } + } + } + } + + g_client.setPostRenderCallback(onRender); + + // Render once now that things are setup. + render(); +} + +function setToUserTexture() { + var pixels = []; + for (var y = 0; y < 32; ++y) { + for (var x = 0; x < 32; ++x) { + var offset = (y * 32 + x) * 3; // rgb + var u = x / 32 * Math.PI * 0.5; + var v = y / 32 * Math.PI * 0.5; + pixels[offset + 0] = Math.cos(u); // red + pixels[offset + 1] = Math.sin(v); // green + pixels[offset + 2] = Math.sin(u); // blue + } + } + var texture = g_pack.createTexture2D(32, 32, g_o3d.Texture.XRGB8, 1, false); + texture.set(0, pixels); + + g_client.setErrorTexture(texture); + // Render once now that things are setup. + render(); +} + +function setToNoTexture() { + g_client.setErrorTexture(null); + render(); + +} + +function hide0() { + var child = g_client.root.children[1]; + child.visible = !child.visible; + render(); +} + +function hide1() { + var child = g_client.root.children[2]; + child.visible = !child.visible; + render(); +} + +function reportError(msg) { + g_errorMsgElement.innerHTML = g_client.lastError; + g_client.clearLastError(); + g_client.clearErrorCallback(); +} + +function render() { + // Render once now that things are setup. + g_client.setErrorCallback(reportError); + g_client.render(); +} + +function onRender() { +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + g_client.cleanup(); +} +</script> +</head> +<body> +<h1>Error Texture.</h1> +<br/> +Demonstrates how missing textures are handled. +<div> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 800px; height: 600px;"></div> +<!-- End of O3D plugin --> +</div> +<br/> +<input type="button" value="User Texture" onClick="setToUserTexture()"/> +<input type="button" value="No Texture" onClick="setToNoTexture()"/> +<input type="button" value="hide 0" onClick="hide0()"/> +<input type="button" value="hide 1" onClick="hide1()"/> +<table><tr><td>Error: </td><td id="errorMsg">-</td></tr></table> +</html> diff --git a/o3d/samples/o3d-webgl/base.js b/o3d/samples/o3d-webgl/base.js index ba23128..ce46219 100644 --- a/o3d/samples/o3d-webgl/base.js +++ b/o3d/samples/o3d-webgl/base.js @@ -234,6 +234,7 @@ o3d.include('object_base'); o3d.include('named_object_base'); o3d.include('named_object'); o3d.include('param_object'); +o3d.include('param_array'); o3d.include('param'); o3d.include('event'); o3d.include('raw_data'); diff --git a/o3d/samples/o3d-webgl/client.js b/o3d/samples/o3d-webgl/client.js index 798ffe3..ebb28f4 100644 --- a/o3d/samples/o3d-webgl/client.js +++ b/o3d/samples/o3d-webgl/client.js @@ -652,6 +652,31 @@ o3d.Client.prototype.initWithCanvas = function(canvas) { height: canvas.height}; o3d.State.createDefaultState_(gl).push_(); + // Create the default error texture. + var defaultTexture = new o3d.Texture2D(); + defaultTexture.gl = this.gl; + defaultTexture.init_(8, 8, o3d.Texture.ARGB8, 1, false); + var r = [1, 0, 0, 1]; + var Y = [1, 1, 0, 1]; + var error = [r, r, r, r, r, r, r, r, + r, r, Y, Y, Y, Y, r, r, + r, Y, r, r, r, Y, Y, r, + r, Y, r, r, Y, r, Y, r, + r, Y, r, Y, r, r, Y, r, + r, Y, Y, r, r, r, Y, r, + r, r, Y, Y, Y, Y, r, r, + r, r, r, r, r, r, r, r]; + var pixels = []; + for (var i = 0; i < error.length; i++) { + for (var j = 0; j < 4; j++) { + pixels[i * 4 + j] = error[i][j]; + } + } + defaultTexture.set(0, pixels); + defaultTexture.name = 'DefaultTexture'; + this.fallback_error_texture_ = defaultTexture; + this.error_texture_ = defaultTexture; + return true; }; @@ -917,7 +942,7 @@ o3d.Client.prototype.clearEventCallback = */ o3d.Client.prototype.setErrorTexture = function(texture) { - o3d.notImplemented(); + this.error_texture_ = texture; }; @@ -978,14 +1003,32 @@ o3d.Client.prototype.setErrorCallback = function(error_callback) { // Other code expects to not see a null error callback. if (error_callback) { - this.error_callback = error_callback; + this.error_callback = this.wrapErrorCallback_(error_callback); } else { - this.error_callback = function(string) {}; + this.error_callback = function(string) { + this.last_error_ = string; + }; } }; /** + * Wraps a callback function, saving the error string so that the + * lastError variable can access it. + * + * @param {function} error_callback User-defined error callback. + * @return {function} Wrapped error callback. + * @private + */ +o3d.Client.prototype.wrapErrorCallback_ = function(error_callback) { + return function(string) { + this.last_error_ = string; + error_callback(string); + } +} + + +/** * Clears the Error callback * * NOTE: The client takes ownership of the ErrorCallback you @@ -1102,7 +1145,6 @@ o3d.Client.prototype.getState_ = function(name) { o3d.Client.prototype.renderer_init_status = 0; - /** * Gets / Sets the cursor's shape. * @@ -1112,12 +1154,49 @@ o3d.Client.prototype.cursor = null; /** + * The current error texture. + * + * @type {o3d.Texture} + * @private + */ +o3d.Client.prototype.error_texture_ = null; + + +/** + * The fallback error texture. Should only be initialized once per client and + * is read-only. + * + * @type {!o3d.Texture} + * @private + */ +o3d.Client.prototype.fallback_error_texture_ = null; + + +/** * The last error reported by the plugin. * * @type {string} + * @private */ o3d.Client.prototype.last_error_ = ''; +o3d.Client.prototype.__defineGetter__('lastError', + function() { + return this.last_error_; + } +); +/** + * Returns true if missing textures, samplers or ParamSamplers should be + * reported by calling the error callback. We assume that if the user + * explicitly sets the error texture to null, then they want such errors to + * trigger the error callback. + * + * @return {boolean} + * @private + */ +o3d.Client.prototype.reportErrors_ = function() { + return (this.error_texture_ == null); +} /** @@ -1146,7 +1225,7 @@ o3d.Client.prototype.objects = []; * Clears the error returned in lastError. */ o3d.Client.prototype.clearLastError = function () { - o3d.notImplemented(); + this.last_error_ = ''; }; @@ -1190,5 +1269,3 @@ o3d.Client.prototype.clientInfo = null; * @type {Element} */ o3d.Client.prototype.canvas = null; - - diff --git a/o3d/samples/o3d-webgl/effect.js b/o3d/samples/o3d-webgl/effect.js index 4d10b72..93932b0 100644 --- a/o3d/samples/o3d-webgl/effect.js +++ b/o3d/samples/o3d-webgl/effect.js @@ -62,9 +62,9 @@ o3d.EffectParameterInfo = /** * The semantic of the parameter. This is always in UPPERCASE. - * @type {o3d.Stream.Semantic} + * @type {string} */ - this.semantic = semantic || o3d.Stream.UNKNOWN_SEMANTIC; + this.semantic = semantic || ''; /** * If this is a standard parameter (SAS) this will be the name of the type @@ -266,6 +266,24 @@ o3d.Effect.prototype.loadFromFXString = /** + * Generates an array of indexed strings. For example, given 'arr' and a size + * of 10, generates 'arr[0]', 'arr[1]', 'arr[2]' up to 'arr[9]'. + * + * @param {string} base The name of the array. + * @param {number} size The number of elements in the array. + * @return {!Array.<string>} + * @private + */ +o3d.Effect.prototype.getParamArrayNames_ = function(base, size) { + var names = []; + for (var i = 0; i < size; i++) { + names[i] = base + '[' + i + ']'; + } + return names; +} + + +/** * Iterates through the active uniforms of the program and gets the * location of each one and stores them by name in the uniforms * object. @@ -278,8 +296,28 @@ o3d.Effect.prototype.getUniforms_ = this.program_, this.gl.ACTIVE_UNIFORMS); for (var i = 0; i < numUniforms; ++i) { var info = this.gl.getActiveUniform(this.program_, i); - this.uniforms_[info.name] = {info: info, - location: this.gl.getUniformLocation(this.program_, info.name)}; + if (info.name.indexOf('[') != -1) { + // This is an array param and we need to individually query each item in + // the array to get its location. + var baseName = info.name.substring(0, info.name.indexOf('[')); + var names = this.getParamArrayNames_(baseName, info.size); + var locations = []; + for (var j = 0; j < names.length; j++) { + locations[j] = this.gl.getUniformLocation(this.program_, names[j]); + } + this.uniforms_[baseName] = { + info: {name: baseName, size: info.size, type: info.type}, + kind: o3d.Effect.ARRAY, + locations: locations /* mind the s */ + }; + } else { + // Not an array param. + this.uniforms_[info.name] = { + info: info, + kind: o3d.Effect.ELEMENT, + location: this.gl.getUniformLocation(this.program_, info.name) + }; + } } }; @@ -334,6 +372,7 @@ o3d.Effect.getParamTypes_ = function(gl) { return o3d.Effect.paramTypes_; } + /** * A map linking names of certain attributes in the shader to the corresponding * semantic and semantic index. @@ -375,11 +414,21 @@ o3d.Effect.prototype.createUniformParameters = function(param_object) { var sasTypes = o3d.Param.sasTypes_; var paramTypes = o3d.Effect.getParamTypes_(this.gl); - for (var name in this.uniforms_) { - var info = this.uniforms_[name].info; + var uniformData = this.uniforms_[name]; if (!sasTypes[name]) { - param_object.createParam(info.name, paramTypes[info.type]); + switch (uniformData.kind) { + case o3d.Effect.ARRAY: + param_object.createParam(name, 'ParamParamArray'); + break; + case o3d.Effect.STRUCT: + o3d.notImplemented(); + break; + case o3d.Effect.ELEMENT: + default: + param_object.createParam(name, paramTypes[uniformData.info.type]); + break; + } } } }; @@ -422,19 +471,18 @@ o3d.Effect.prototype.createSASParameters = o3d.Effect.prototype.getParameterInfo = function() { var infoArray = []; var sasTypes = o3d.Param.sasTypes_; - var paramTypes = o3d.Effect.getParamTypes_(this.gl); var semanticMap = o3d.Effect.semanticMap_; + var paramTypes = o3d.Effect.getParamTypes_(this.gl); for (var name in this.uniforms_) { - var info = this.uniforms_[name].info; - var sasTypeName = sasTypes[name] || ''; - var className = paramTypes[info.type] || ''; - var numElements = 0; // TODO(petersont): Add array support. - var semantic = (semanticMap[name] && semanticMap[name].semantic) ? - semanticMap[name].semantic : o3d.Stream.UNKNOWN_SEMANTIC; - + var uniformData = this.uniforms_[name]; + var sasClassName = sasTypes[name] || ''; + var dataType = paramTypes[uniformData.info.type] || ''; + var numElements = (uniformData.kind == o3d.Effect.ARRAY) ? + uniformData.info.size : 0; // 0 if a non-array type. + var semantic = semanticMap[name] ? name : ''; infoArray.push(new o3d.EffectParameterInfo( - name, className, numElements, semantic, sasTypeName)); + name, dataType, numElements, semantic.toUpperCase(), sasClassName)); } return infoArray; @@ -461,7 +509,7 @@ o3d.Effect.prototype.getStreamInfo = function() { /** * Searches the objects in the given list for parameters to apply to the * uniforms defined on this effects program, and applies them, favoring - * the objects nearer the begining of the list. + * the objects nearer the beginning of the list. * * @param {!Array.<!o3d.ParamObject>} object_list The param objects to search. * @private @@ -482,7 +530,11 @@ o3d.Effect.prototype.searchForParams_ = function(object_list) { } var param = obj.getParam(name); if (param) { - param.applyToLocation(this.gl, uniformInfo.location); + if (uniformInfo.kind == o3d.Effect.ARRAY) { + param.applyToLocations(this.gl, uniformInfo.locations); + } else { + param.applyToLocation(this.gl, uniformInfo.location); + } filled_map[name] = true; } } @@ -491,10 +543,19 @@ o3d.Effect.prototype.searchForParams_ = function(object_list) { this.updateHelperConstants_(this.gl.displayInfo.width, this.gl.displayInfo.height); filled_map[o3d.Effect.HELPER_CONSTANT_NAME] = true; - for (var name in this.uniforms_) { if (!filled_map[name]) { - throw ('Uniform param not filled: "'+ name + '"'); + if (this.uniforms_[name].info.type == this.gl.SAMPLER_2D) { + if (this.gl.client.reportErrors_()) { + this.gl.client.error_callback("Missing ParamSampler"); + } + var defaultParamSampler = o3d.ParamSampler.defaultParamSampler_; + defaultParamSampler.gl = this.gl; + defaultParamSampler.applyToLocation(this.gl, + this.uniforms_[name].location); + } else { + throw ('Uniform param not filled: "'+ name + '"'); + } } } }; @@ -543,6 +604,17 @@ o3d.Effect.COLUMN_MAJOR = 1; /** + * UniformType, + * ELEMENT, the param is a single gl.* element + * ARRAY, the param is an array of same-typed elements + * STRUCT, not implemented + */ +o3d.Effect.ELEMENT = 0; +o3d.Effect.ARRAY = 1; +o3d.Effect.STRUCT = 2; + + +/** * The order in which matrix data is loaded to the GPU. * @type {o3d.Effect.MatrixLoadOrder} */ @@ -554,5 +626,3 @@ o3d.Effect.prototype.matrix_load_order_ = o3d.Effect.ROW_MAJOR; * @type {string} */ o3d.Effect.prototype.source_ = ''; - - diff --git a/o3d/samples/o3d-webgl/param.js b/o3d/samples/o3d-webgl/param.js index 59c0219..1aae87e 100644 --- a/o3d/samples/o3d-webgl/param.js +++ b/o3d/samples/o3d-webgl/param.js @@ -337,7 +337,7 @@ o3d.inherit('ParamMatrix4', 'Param'); */ o3d.ParamParamArray = function() { o3d.Param.call(this); - this.value = []; + this.value = null; }; o3d.inherit('ParamParamArray', 'Param'); @@ -834,6 +834,23 @@ o3d.ParamMatrix4.prototype.applyToLocation = function(gl, location) { }; /** + * Called to specify the values of a uniform array. + * @param {WebGLContext} gl The current context. + * @param {!Array.<!WebGLUniformLocation>} locationArray An array of locations + * to which to apply the values. + */ +o3d.ParamParamArray.prototype.applyToLocations = function(gl, locationArray) { + if (locationArray.length != this.value.length) { + gl.client.error_callback( + 'Invalid uniform param array: incorrect number of elements.'); + } + for (var i = 0; i < this.value.length; i++) { + // Cannot have a ParamArray of ParamArrays, so safe to call applyToLocation + this.value.getParam(i).applyToLocation(gl, locationArray[i]); + } +}; + +/** * A counter to ensure each texture sampler gets a unqiue id. * @private */ @@ -852,14 +869,30 @@ o3d.ParamSampler.prototype.applyToLocation = function(gl, location) { var value = null; var target = 0; + var sampler = null; if (this.value) { - this.value.bindAndSetParameters_(); - gl.uniform1i(location, i); - o3d.Param.texture_index_++; + sampler = this.value; + } else { + o3d.Sampler.defaultSampler_.gl = gl; + sampler = o3d.Sampler.defaultSampler_; + if (gl.client.reportErrors_()) { + gl.client.error_callback("Missing Sampler for ParamSampler " + this.name); + } } + + sampler.bindAndSetParameters_(); + gl.uniform1i(location, i); + o3d.Param.texture_index_++; }; +/** + * A default ParamSampler to be used if client does not assign one. + * + * @type {!o3d.ParamSampler} + * @private + */ +o3d.ParamSampler.defaultParamSampler_ = new o3d.ParamSampler(); /** * Object to compute all combinations of world/view/projection diff --git a/o3d/samples/o3d-webgl/param_array.js b/o3d/samples/o3d-webgl/param_array.js new file mode 100644 index 0000000..236c45a --- /dev/null +++ b/o3d/samples/o3d-webgl/param_array.js @@ -0,0 +1,184 @@ +/* + * Copyright 2010, 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. + */ + + +/** + * A ParamArray is an object that holds an array of Params. + * @constructor + */ +o3d.ParamArray = function() { + o3d.NamedObject.call(this); + this.params_ = []; +}; +o3d.inherit('ParamArray', 'NamedObject'); + + +/** + * Creates a Param of the given type at the index requested. If a Param already + * exists at that index the new param will be replace it. If the index is past + * the end of the current array params of the requested type will be created to + * fill out the array to the requested index. + * + * @param {number} index Index at which to create new param. + * @param {string} param_type_name The type of Param to create. For a list of + * valid types see ParamObject.createParam + * @return {!o3d.ParamArray} The newly created Param or null if failure. + */ +o3d.ParamArray.prototype.createParam = function(index, param_type_name) { + param_type_name = o3d.filterTypeName_(param_type_name); + if (!o3d.global.o3d[param_type_name]) + throw ('Invalid param type name: ' + param_type_name); + if (index >= this.params_.length) { + this.resize(index + 1, param_type_name); + } else { + var param = new o3d.global.o3d[param_type_name]; + param.gl = this.gl; + param.owner_ = this; + this.params_[index] = param; + } + + return this.filterResult_(this.params_[index]); +}; + + +/** + * Gets a Param by index. + * + * @param {number} index Index of Param to get. + * @return {!o3d.Param} The Param at index, or null if out of range. + */ +o3d.ParamArray.prototype.getParam = function(index) { + var result = this.params_[index]; + return this.filterResult_(result); +}; + + +/** + * Removes a range of params. This shrinks the array and affects the indices of + * later occurring items. + * + * @param {number} start_index Index of first param to remove. + * @param {number} num_to_remove The number of params to remove starting at + * start_index. + */ +o3d.ParamArray.prototype.removeParams = function(start_index, num_to_remove) { + var paramsNew = []; + var j = 0; + for (var i = 0; i < this.params_.length; i++) { + if (i >= start_index && i < start_index + num_to_remove) { + // Skip these to remove them. + } else { + paramsNew[j] = this.params_[i]; + j++; + } + } + this.params_ = paramsNew; +}; + + +/** + * Resizes the array. + * + * @param {number} num_params The number of params to make the array. + * @param {string} param_type_name The type of Param to create if resizing + * requires params to be created. For a list of valid types see + * ParamObject.createParam. + */ +o3d.ParamArray.prototype.resize = function(num_params, param_type_name) { + param_type_name = o3d.filterTypeName_(param_type_name); + if (!o3d.global.o3d[param_type_name]) + throw ('Invalid param type name: ' + param_type_name); + + for (var i = this.params_.length; i < num_params; i++) { + var param = new o3d.global.o3d[param_type_name]; + param.gl = this.gl; + param.owner_ = this; + this.params_[i] = param; + } +}; + +/** + * The params stored in this ParamArray. + * + * @type {!Array.<!o3d.Param>} + * @private + */ +o3d.ParamArray.prototype.params_ = []; + +/** + * Gets all the param on this param object. + * + * Each access to this field gets the entire list, so it is best to get it + * just once. For example: + * + * var params = ParamArray.params; + * for (var i = 0; i < params.length; i++) { + * var param = params[i]; + * } + * + * Note that modifications to this array [e.g. push()] will not affect + * the underlying ParamArray, while modifications to the array's members + * <b>will</b> affect them. + * + * @type {!Array.<!o3d.Param>} + */ +o3d.ParamArray.prototype.__defineGetter__('params', + function() { + var params = []; + for (var i = 0; i < this.length; i++) { + params[i] = this.params_[i]; + } + return params; + } +); + + +/** + * Returns the number of parameters in this ParamArray. + * + * @type {number} + */ +o3d.ParamArray.prototype.__defineGetter__('length', + function() { + return this.params_.length; + } +); + + +/** + * Filters results, turning 'undefined' into 'null'. + * + * @param {*} result + * @private + */ +o3d.ParamArray.prototype.filterResult_= function(result) { + return (result ? result : null); +}; diff --git a/o3d/samples/o3d-webgl/param_object.js b/o3d/samples/o3d-webgl/param_object.js index 78ee725..61f3fac 100644 --- a/o3d/samples/o3d-webgl/param_object.js +++ b/o3d/samples/o3d-webgl/param_object.js @@ -109,6 +109,7 @@ o3d.ParamObject.prototype.createParam = var param = new o3d.global.o3d[param_type_name]; param.gl = this.gl; param.owner_ = this; + param.name = param_name; this.params_[param_name] = param; return this.filterResult_(this.params_[param_name]); }; diff --git a/o3d/samples/o3d-webgl/sampler.js b/o3d/samples/o3d-webgl/sampler.js index eb0a0e1..4e651a9 100644 --- a/o3d/samples/o3d-webgl/sampler.js +++ b/o3d/samples/o3d-webgl/sampler.js @@ -250,24 +250,37 @@ o3d.Sampler.prototype.convertMagFilter_ = function(o3d_filter) { /** + * A default Sampler that has no texture, thus uses the client's error texture. + * + * @type {!o3d.Sampler} + * @private + */ +o3d.Sampler.defaultSampler_ = new o3d.Sampler(); +o3d.Sampler.defaultSampler_.magFilter = o3d.Sampler.POINT; + +/** * Binds the texture for this sampler and sets texParameters according to the * states of the sampler. */ o3d.Sampler.prototype.bindAndSetParameters_ = function() { + var currentTexture = null; if (this.texture) { - var mip_filter = this.mipFilter; - if (this.texture.levels == 1) { - mip_filter = o3d.Sampler.NONE; - } - - this.texture.bindAndSetParameters_( - this.convertAddressMode_(this.addressModeU), - this.convertAddressMode_(this.addressModeV), - this.convertMinFilter_(this.minFilter, mip_filter), - this.convertMagFilter_(this.magFilter)); + currentTexture = this.texture; + } else if (!this.gl.client.reportErrors_()) { + currentTexture = this.gl.client.error_texture_; } else { - this.gl.client.error_callback("Sampler used with no texture set."); - return; + currentTexture = this.gl.client.fallback_error_texture_; + this.gl.client.error_callback("Missing texture for sampler " + this.name); + } + + var mip_filter = this.mipFilter; + if (currentTexture.levels == 1) { + mip_filter = o3d.Sampler.NONE; } + currentTexture.bindAndSetParameters_( + this.convertAddressMode_(this.addressModeU), + this.convertAddressMode_(this.addressModeV), + this.convertMinFilter_(this.minFilter, mip_filter), + this.convertMagFilter_(this.magFilter)); } diff --git a/o3d/samples/o3djs/effect.js b/o3d/samples/o3djs/effect.js index 0d23889..c9b5548 100644 --- a/o3d/samples/o3djs/effect.js +++ b/o3d/samples/o3djs/effect.js @@ -617,16 +617,16 @@ o3djs.effect.buildCheckerShaderString = function() { var p = o3djs.effect; var varyingDecls = p.BEGIN_OUT_STRUCT + p.VARYING + p.FLOAT4 + ' ' + - p.VERTEX_VARYING_PREFIX + 'position' + + p.VARYING_DECLARATION_PREFIX + 'position' + p.semanticSuffix('POSITION') + ';\n' + p.VARYING + p.FLOAT2 + ' ' + - p.VERTEX_VARYING_PREFIX + 'texCoord' + + p.VARYING_DECLARATION_PREFIX + 'texCoord' + p.semanticSuffix('TEXCOORD0') + ';\n' + p.VARYING + p.FLOAT3 + ' ' + - p.VERTEX_VARYING_PREFIX + 'normal' + + p.VARYING_DECLARATION_PREFIX + 'normal' + p.semanticSuffix('TEXCOORD1') + ';\n' + p.VARYING + p.FLOAT3 + ' ' + - p.VERTEX_VARYING_PREFIX + 'worldPosition' + + p.VARYING_DECLARATION_PREFIX + 'worldPosition' + p.semanticSuffix('TEXCOORD2') + ';\n' + p.END_STRUCT; |