diff options
author | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 21:00:00 +0000 |
---|---|---|
committer | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 21:00:00 +0000 |
commit | e512583b79e366db399a6566964412bb6e5af823 (patch) | |
tree | e15e3c05e273a0366caf2fcbc5689ae377b3e877 | |
parent | a37a999f87eed77b08e7e1b6bdb86d406f31ea9b (diff) | |
download | chromium_src-e512583b79e366db399a6566964412bb6e5af823.zip chromium_src-e512583b79e366db399a6566964412bb6e5af823.tar.gz chromium_src-e512583b79e366db399a6566964412bb6e5af823.tar.bz2 |
Added textures, texture samplers and render targets to o3d-webgl. Also fixed bugs, added calls to parent class constructor to classes that didn't have them before, added a few demos to exhibit/test textures and render surfaces.
Review URL: http://codereview.chromium.org/856004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41482 0039d316-1c4b-4281-b951-d872f2087c98
36 files changed, 2246 insertions, 201 deletions
diff --git a/o3d/samples/o3d-webgl-samples/hellocube-colors.html b/o3d/samples/o3d-webgl-samples/hellocube-colors.html new file mode 100644 index 0000000..2af5699 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/hellocube-colors.html @@ -0,0 +1,343 @@ +<!-- +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 creates an O3D area with a spinning cube. The user +can change the color of the cube by clicking on one of the buttons that appear +at the bottom of the page. This sample demonstrates how to use O3D Params +to change the values of uniform parameters used by shaders. +--> +<!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> +Hello Square Colors: Getting started with O3D, take 2. +</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'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page has is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing + +var g_cubeTransform; +var g_cubeColorParam; + +/** + * Changes the color of the cube. + * @param red red component of the color + * @param green green component of the color + * @param blue blue component of the color + */ +function changeColor(red, green, blue) { + // Set the rgb color values and alpha = 1 + g_cubeColorParam.value = [red, green, blue, 1]; +} + +/** + * Creates an O3D shape representing a cube. The shape consists of + * a single primitive with eight vertices and 12 triangles (two for each face + * of the cube). + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles. There's eight vertices in total which + // are shared between the face triangles. + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + cubePrimitive.numberVertices = 8; // 8 vertices in total + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 8 corners of the cube. + var positionArray = [ + -0.5, -0.5, 0.5, // vertex 0 + 0.5, -0.5, 0.5, // vertex 1 + -0.5, 0.5, 0.5, // vertex 2 + 0.5, 0.5, 0.5, // vertex 3 + -0.5, 0.5, -0.5, // vertex 4 + 0.5, 0.5, -0.5, // vertex 5 + -0.5, -0.5, -0.5, // vertex 6 + 0.5, -0.5, -0.5 // vertex 7 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 2, 1 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, // face 1 + 2, 1, 3, + 2, 3, 4, // face 2 + 4, 3, 5, + 4, 5, 6, // face 3 + 6, 5, 7, + 6, 7, 0, // face 4 + 0, 7, 1, + 1, 7, 3, // face 5 + 3, 7, 5, + 6, 0, 4, // face 6 + 4, 0, 2 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions Buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the cube and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var vertexShaderString = document.getElementById('vshader').value; + var pixelShaderString = document.getElementById('pshader').value; + cubeEffect.loadVertexShaderFromString(vertexShaderString); + cubeEffect.loadPixelShaderFromString(pixelShaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the color parameter from the material and set its value to red. + g_cubeColorParam = cubeMaterial.getParam('color'); + g_cubeColorParam.value = [1, 0, 0, 1]; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Parent the cube's transform to the client root. + g_cubeTransform.parent = g_client.root; + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + g_finished = true; // for selenium testing. +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Square: Colors</h1> +This example shows how to use parameters to the color output of a shader. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" width="600px" height="600px"></div> +<!-- End of O3D plugin --> + +<form name="default_form" action="#" method="get"> + <p> + Change color: + <input type="button" value="Red" onclick="changeColor(1,0,0);" /> + <input type="button" value="Green" onclick="changeColor(0,1,0);" /> + <input type="button" value="Blue" onclick="changeColor(0,0,1);" /> + <input type="button" value="Yellow" onclick="changeColor(1,1,0);" /> + <input type="button" value="Cyan" onclick="changeColor(0,1,1);" /> + <input type="button" value="Purple" onclick="changeColor(1,0,1);" /> + <input type="button" value="White" onclick="changeColor(1,1,1);" /> + </p> +</form> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="vshader"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + } + +</textarea> +<textarea id="pshader"> + // Color to draw with. + uniform vec4 color; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = color; + } + + // Here we tell our effect file *which* functions are + // our vertex and pixel shaders. +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/o3d-webgl-samples/hellocube-textures.html b/o3d/samples/o3d-webgl-samples/hellocube-textures.html new file mode 100644 index 0000000..0c20cf4 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/hellocube-textures.html @@ -0,0 +1,433 @@ +<!-- +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 creates an O3D area with a textured cube in the middle. The +user can specify the URL where the texture image will be picked from. +This sample is a simple demonstration of texture usage in O3D. +--> +<!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> +Hello Square Textures: Getting started with O3D, take 3. +</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.io'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_cubeTransform; +var g_sampler; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing +var g_textureLoadDenied = false; // also for selenium testing + +/** + * Creates an O3D shape representing a cube. + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles (6 faces x 2 triangles per face) + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + + // Vertices used by each triangle must specify both a position and texture + // coordinates. We cannot share vertices between adjacent cube faces since + // while their positions are the same, their texture coordinates are + // different. We therefore create 24 vertices, 4 for each of the cube's + // six faces. + cubePrimitive.numberVertices = 24; + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 24 vertices used by the cube. + var positionArray = [ + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, 0.5, 0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5 + ]; + + // The following array stores the texture coordinates (u, v) for each vertex. + // These coordinates are used by the shader when displaying the texture image + // on the mesh triangles. + var texCoordsArray = [ + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 0, 2 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, + 0, 2, 3, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 13, 14, + 12, 14, 15, + 16, 17, 18, + 16, 18, 19, + 20, 21, 22, + 20, 22, 23 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var texCoordsBuffer = g_pack.createObject('VertexBuffer'); + var texCoordsField = texCoordsBuffer.createField('FloatField', 2); + texCoordsBuffer.set(texCoordsArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the texture coordinates buffer with the primitive. + streamBank.setVertexStream( + g_o3d.Stream.TEXCOORD, // semantic + 0, // semantic index + texCoordsField, // field + 0); // start_index + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the quad and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Set the texture URL. + path = '../assets/texture_b3.jpg'; + var url = document.getElementById("url").value = path; + + // Initialize global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var vertexShaderString = document.getElementById('vshader').value; + var pixelShaderString = document.getElementById('pshader').value; + cubeEffect.loadVertexShaderFromString(vertexShaderString); + cubeEffect.loadPixelShaderFromString(pixelShaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the material's sampler parameter so that we can set the texture value + // to it. + var samplerParam = cubeMaterial.getParam('texSampler0'); + + // Create a Sampler object and set the min filtering to ANISOTROPIC. This + // will improve the quality of the rendered texture when viewed at an angle. + g_sampler = g_pack.createObject('Sampler'); + g_sampler.minFilter = g_o3d.Sampler.ANISOTROPIC; + g_sampler.maxAnisotropy = 4; + samplerParam.value = g_sampler; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Note that we don't parent the transform until the texture is + // succesfully loaded because we don't want the system + // to try the draw the shape without its required texture. + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + // Set the initial texture. + changeTexture(); +} + +/** + * Fetches the bitmap pointed to by the URL supplied by the user, creates + * an O3D Texture object with it updates the Sampler used by the material + * to point to the newly created texture. + */ +function changeTexture() { + var textureUrl = document.getElementById('url').value; + o3djs.io.loadTexture(g_pack, textureUrl, function(texture, exception) { + // Remove the currently used texture from the pack so that when it's not + // referenced anymore, it can get destroyed. + if (g_sampler.texture) + g_pack.removeObject(g_sampler.texture); + + // Because the loading is asynchronous an exception is not thrown but is + // instead passed on failure. + if (exception) { + g_sampler.texture = null; + + g_textureLoadDenied = true; // for selenium testing. + } else { + // Set the texture on the sampler object to the newly created texture + // object returned by the request. + g_sampler.texture = texture; + + // We can now safely add the cube transform to the root of the + // scenegraph since it now has a valid texture. If the transform + // is already parented under the root, the call will have no effect. + g_cubeTransform.parent = g_client.root; + + g_finished = true; // for selenium testing. + } + }); +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Cube: Textures</h1> +This example shows how texture map a cube using an image fetched from a URL. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" width="600px" height="600px"></div> +<!-- End of O3D plugin --> +<br /> +Image URL: <input type="text" id="url" size="100"> +<input type="button" id="updateButton" onclick="changeTexture();" value="Update Texture"><BR> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="vshader"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + attribute vec2 texCoord0; + + varying vec2 uvs; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + uvs = texCoord0; + } + +</textarea> +<textarea id="pshader"> + + // Color to draw with. + uniform sampler2D texSampler0; + + varying vec2 uvs; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = texture2D(texSampler0, uvs); + } +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/o3d-webgl-samples/shadow-map.html b/o3d/samples/o3d-webgl-samples/shadow-map.html new file mode 100644 index 0000000..2bf81cc --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/shadow-map.html @@ -0,0 +1,636 @@ +<!-- +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 uses a custom render graph to implement a basic shadow map +algorithm. + +The technique works by rendering the scene in two passes. The first pass +renders the geometry in the scene with a shader that colors each pixel a shade +of gray representing how far the rendered point is from the light source. That +image, the shadow map, is rendered to a texture, and then the second (visible) +render pass samples it to determine which points in the scene are in shaodow. +--> +<!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> +Shadow Mapping +</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.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.effect'); +o3djs.require('o3djs.debug'); +o3djs.require('o3djs.material'); + +// The initClient() function runs when the page has finished loading. +window.onload = initClient; + +// global variables +var g_o3dElement; +var g_client; +var g_o3d; +var g_math; +var g_pack; +var g_colorViewInfo; +var g_shadowViewInfo; +var g_shadowTexture; +var g_shadowMaterial; +var g_colorEffect; +var g_shadowSampler; +var g_lightViewProjection; +var g_lightFrustumTransform; +var g_globalParams = { }; +var g_viewFromLight = false; + +var g_renderSurfaceSet; +var g_colorPassRenderRoot; + +var g_lightWorldPos = [5, 10, 0]; +var g_lightColor = [1, 1, 1, 1]; +var g_eyePosition = [1, 6, 20]; +var g_targetPosition = [0, 2, 0]; + +// constants. +var SHADOW_MAP_WIDTH = 512; +var SHADOW_MAP_HEIGHT = 512; + +var g_finished = false; // for selenium testing. + + +/** + * Creates the client area. + */ +function initClient() { + o3djs.webgl.makeClients(main, 'FloatingPointTextures', true); +} + + +/** + * Initializes global variables, positions camera, draws shapes. + * @param {Array} clientElements Array of o3d object elements. + */ +function main(clientElements) { + // Init global variables. + initGlobals(clientElements); + + // Set up the rendergraph. + initRenderGraph(); + + // Load effects, bind material parameters. + initMaterials(); + + // Add the shapes to the transform graph. + createShapes(); + + // Set up the view and projection transformations for the camera. + updateCamera(); + + // Init global parameters. initGlobalParams() searches all materials in order + // to bind parameters, so it must be called after initMaterials() + initGlobalParams(); + + // Set the view and projection transformations for the light. + updateLightMatrix(); + + // Create the light that gets drawn. + createLightShape(); + + // Execute keyPressed() when we detect a keypress on the window or + // on the o3d object. + window.document.onkeypress = keyPressed; + g_o3dElement.onkeypress = keyPressed; + + //toggleView(); + + g_finished = true; // for selenium testing. +} + + +/** + * Initializes global variables and libraries. + */ +function initGlobals(clientElements) { + g_o3dElement = clientElements[0]; + g_client = g_o3dElement.client; + g_o3d = g_o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); +} + + +/** + * Sets up the render graph. Builds a basic view for the camera and the light + * point of view, arranges for the view from the light to be rendered to a + * texture for the shadow map. Unlike the basic render graph created by the + * the utility function o3djs.rendergraph.createBasicView, to render the shadow + * map and then render the scene, we need two subtrees of the render graph, one + * for shadow map render pass and one to draw the scene. + */ +function initRenderGraph() { + // The children of any one node in the render graph get traversed in order by + // priority. Here, we're forcing the shadow map to get rendered first by + // by giving its render root lower priority. + var shadowPassRenderRoot = g_pack.createObject('RenderNode'); + shadowPassRenderRoot.priority = 0; + + g_colorPassRenderRoot = g_pack.createObject('RenderNode'); + g_colorPassRenderRoot.priority = 1; + + g_colorPassRenderRoot.parent = g_client.renderGraphRoot; + + // Create the texture that will store the depth information. + g_shadowTexture = g_pack.createTexture2D(SHADOW_MAP_WIDTH, + SHADOW_MAP_HEIGHT, + g_o3d.Texture.ABGR32F, + 1, + true); + var renderSurface = g_shadowTexture.getRenderSurface(0); + + // Create the depth-stencil buffer required when rendering the teapot. + var depthSurface = g_pack.createDepthStencilSurface(SHADOW_MAP_WIDTH, + SHADOW_MAP_HEIGHT); + + shadowPassRenderRoot.parent = g_client.renderGraphRoot; + + g_renderSurfaceSet = g_pack.createObject('RenderSurfaceSet'); + g_renderSurfaceSet.renderSurface = renderSurface; + g_renderSurfaceSet.renderDepthStencilSurface = depthSurface; + + g_renderSurfaceSet.parent = shadowPassRenderRoot; + + // Create a render sub-graph for the shadow map generation. + g_shadowViewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_renderSurfaceSet, + [1, 1, 1, 1]); + + // Create a render sub-graph for the regular pass. + g_colorViewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_colorPassRenderRoot, + [0, 0, 0, 1]); +} + + +/** + * Switches between the camera and light point of view. + */ +function toggleView() { + if (g_viewFromLight) { + g_shadowViewInfo.root.parent = g_renderSurfaceSet; + g_colorPassRenderRoot.parent = g_client.renderGraphRoot; + g_viewFromLight = false; + } else { + g_shadowViewInfo.root.parent = g_client.renderGraphRoot; + g_colorPassRenderRoot.parent = null; + g_viewFromLight = true; + } +} + +/** + * Creates a material to be put on all shapes in the scene for the shadow pass, + * and loads effects for materials in the scene. Other materials are created + * on the fly as the shapes are created. + */ +function initMaterials() { + var colorVertexShader = document.getElementById('colorVertexShader').text; + var colorPixelShader = document.getElementById('colorPixelShader').text; + + g_colorEffect = g_pack.createObject('Effect'); + g_colorEffect.loadVertexShaderFromString(colorVertexShader); + g_colorEffect.loadPixelShaderFromString(colorPixelShader); + + var shadowVertexShader = document.getElementById('shadowVertexShader').text; + var shadowPixelShader = document.getElementById('shadowPixelShader').text; + + g_shadowMaterial = g_pack.createObject('Material'); + g_shadowMaterial.drawList = g_shadowViewInfo.performanceDrawList; + + var shadowEffect = g_pack.createObject('Effect'); + shadowEffect.loadVertexShaderFromString(shadowVertexShader); + shadowEffect.loadPixelShaderFromString(shadowPixelShader); + + g_shadowMaterial.effect = shadowEffect; + shadowEffect.createUniformParameters(g_shadowMaterial); + + g_shadowSampler = g_pack.createObject('Sampler'); + g_shadowSampler.texture = g_shadowTexture; + g_shadowSampler.minFilter = g_o3d.Sampler.POINT; + g_shadowSampler.magFilter = g_o3d.Sampler.POINT; + g_shadowSampler.mipFilter = g_o3d.Sampler.POINT; + g_shadowSampler.addressModeU = g_o3d.Sampler.BORDER; + g_shadowSampler.addressModeV = g_o3d.Sampler.BORDER; + g_shadowSampler.borderColor = [1, 1, 1, 1]; + +} + + +/** + * Sets up reasonable view and projection matrices. + */ +function updateCamera() { + // Set up a perspective transformation for the projection. + g_colorViewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree frustum. + g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + g_colorViewInfo.drawContext.view = g_math.matrix4.lookAt( + g_eyePosition, // eye + g_targetPosition, // target + [0, 1, 0]); // up +} + + +/** + * Computes the view and projection matrices from the point of view of the + * light. Sets the lightViewProjection parameter so the color shader can access + * it. + */ +function updateLightMatrix() { + // The perspective projection matrix for the light. + var lightProjection = g_math.matrix4.perspective( + g_math.degToRad(45), // 45 degree fov. + SHADOW_MAP_WIDTH / SHADOW_MAP_HEIGHT, // Aspect ratio. + 4, // Near plane. + 20); // Far plane. + + var adjustedProjection = + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, -1, 1]]; + o3d.Transform.compose( + adjustedProjection, lightProjection, adjustedProjection); + + // Make the light point toward the origin + var lightView = g_math.matrix4.lookAt( + g_lightWorldPos, // light + [0, 0, 0], // target + [1, 0, 0]); // up + + g_lightViewProjection = g_math.matrix4.composition( + lightProjection, lightView); + + g_shadowViewInfo.drawContext.projection = lightProjection; + g_shadowViewInfo.drawContext.view = lightView; + g_globalParams.lightViewProjection.value = g_lightViewProjection; +} + + +/** + * Creates shapes using the primitives utility library, and adds them to the + * transform graph at the root node. + */ +function createShapes() { + // A green phong-shaded material for the cube. + var cubeMaterial = createColorMaterial([0.2, 0.5, 0, 1]); + + // The cube shape. + var cube = o3djs.primitives.createCube( + g_pack, + cubeMaterial, + 2); // The length of each side of the cube. + + // A red phong-shaded material for the sphere. + var sphereMaterial = createColorMaterial([0.7, 0.2, 0.1, 1]); + + // The sphere shape. + var sphere = o3djs.primitives.createSphere( + g_pack, sphereMaterial, 0.5, 50, 50); + + // A blue phong-shaded material for the plane. + var planeMaterial = createColorMaterial([0, 0.3, 0.5, 1]); + + // The plane shape. + var plane = o3djs.primitives.createPlane( + g_pack, + planeMaterial, + 20, // Width. + 20, // Depth. + 1, // Horizontal subdivisions. + 1); // Vertical subdivisions. + + // Associate to each shape, a translation vector. + var transformTable = [ + {shape: cube, translation: [0, 1, 0]}, + {shape: sphere, translation: [0.5, 2.5, 0]}, + {shape: plane, translation: [0, 0, 0]} + ]; + + // Add the shapes to the transform graph with the translation. + var modelRoot = g_pack.createObject('Transform'); + modelRoot.parent = g_client.root; + for (var tt = 0; tt < transformTable.length; ++tt) { + var transform = g_pack.createObject('Transform'); + transform.addShape(transformTable[tt].shape); + // The shadow material is bound to a DrawList in the subtree of the + // rendergraph that handles the shadow map generation, so it gets drawn in + // that render pass only. + transformTable[tt].shape.createDrawElements(g_pack, g_shadowMaterial); + + transform.translate(transformTable[tt].translation); + transform.parent = modelRoot; + } +} + + +/** + * Creates the wireframe frustum showing the shadow map's render volume. + */ +function createLightShape() { + var inverseMatrix = g_math.matrix4.inverse(g_lightViewProjection); + +/* + // Scale and translate a cube of side length 2 to get a box + // that extends from [-1, -1, 0] to [1, 1, 1]. + var shape = o3djs.debug.createLineCube( + g_pack, + o3djs.material.createConstantMaterial(g_pack, + g_colorViewInfo, + [1, 0, 0, 1]), + 2, + g_math.matrix4.compose( + g_math.matrix4.translation([0, 0, 0.5]), + g_math.matrix4.scaling([1, 1, 0.5])));*/ + + g_lightFrustumTransform = g_pack.createObject('Transform'); + g_lightFrustumTransform.localMatrix = inverseMatrix; + g_lightFrustumTransform.parent = g_client.root; + //g_lightFrustumTransform.addShape(shape); +} + + +/** + * Creates a Phong-shaded, shadowed material based on the given color. + */ +function createColorMaterial(baseColor) { + var material = g_pack.createObject('Material'); + material.drawList = g_colorViewInfo.performanceDrawList; + + material.effect = g_colorEffect; + g_colorEffect.createUniformParameters(material); + + material.getParam('shadowMapSampler').value = g_shadowSampler; + + material.getParam('ambient').value = g_math.mulScalarVector(0.1, baseColor); + material.getParam('diffuse').value = g_math.mulScalarVector(0.8, baseColor); + material.getParam('specular').value = [1, 1, 1, 1]; + material.getParam('shininess').value = 20; + + return material; +} + +/** + * Binds params for light position, light color and the light view-projection + * matrix to all materials in the scene where they apply. + */ +function initGlobalParams() { + var paramSpec = { + 'lightColor': 'ParamFloat4', + 'lightWorldPos': 'ParamFloat3', + 'lightViewProjection': 'ParamMatrix4'}; + + g_globalParams = o3djs.material.createParams(g_pack, paramSpec); + o3djs.material.bindParams(g_pack, g_globalParams); + + g_globalParams.lightWorldPos.value = g_lightWorldPos; + g_globalParams.lightColor.value = g_lightColor; +} + + +/** + * The keyboard event handler. + */ +function keyPressed(event) { + var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event)); + keyChar = keyChar.toLowerCase(); + + var delta = 0.2; + switch(keyChar) { + case 'a': + moveLight([-delta, 0, 0]); + break; + case 'd': + moveLight([delta, 0, 0]); + break; + case 's': + moveLight([0, -delta, 0]); + break; + case 'w': + moveLight([0, delta, 0]); + break; + case 'i': + moveLight([0, 0, delta]); + break; + case 'o': + moveLight([0, 0, -delta]); + break; + + case ' ': + toggleView(); + break; + } +} + +/** + * Moves the light by the given vector delta, and updates params so the light + * draws in the right spot and the shadows move. + */ +function moveLight(delta) { + g_lightWorldPos = g_math.addVector(g_lightWorldPos, delta); + g_globalParams.lightWorldPos.value = g_lightWorldPos; + updateLightMatrix(); + g_lightFrustumTransform.localMatrix = + g_math.matrix4.inverse(g_lightViewProjection); +} + + +</script> + + + +<script id="shadowVertexShader" type="text/glsl"> + /** + * This shader is for the effect applied in the first render pass, when the + * shadow map is created. The scene is rendered from the perspective of the + * light, the grayscale value of each pixel in the rendered image represents + * how far away the rendered point is from the light (the lighter, the + * farther) This image gets rendered to a texture, and that texture gets + * sampled in the second render pass, when the geometry is drawn to the + * screen. + */ + attribute vec4 position; + uniform mat4 worldViewProjection; + + varying vec4 vposition; + + void main() { + vposition = worldViewProjection * position; + gl_Position = vposition; + } +</script> +<script id="shadowPixelShader" type="text/glsl"> + varying vec4 vposition; + + void main() { + vec4 color; + vec3 q = vposition.xyz / vposition.w; + + float depth = 0.5*(q.z + 1.0); + color.r = fract(16777216.0 * depth); + color.g = fract(65536.0 * depth); + color.b = fract(256.0 * depth); + color.a = depth; + + gl_FragColor = color; + } +</script> + + +<script id="colorVertexShader" type="text/glsl"> + attribute vec4 position; + attribute vec3 normal; + + uniform mat4 worldViewProjection; + uniform mat4 world; + uniform mat4 worldInverseTranspose; + uniform mat4 lightViewProjection; + + varying vec4 vposition; + varying vec3 vnormal; + varying vec4 vprojTextureCoords; + varying vec4 vworldPosition; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + vworldPosition = world * position; + vnormal = vec3(worldInverseTranspose * vec4(normal, 0)); + vposition = worldViewProjection * position; + vprojTextureCoords = lightViewProjection * world * position; + gl_Position = vposition; + } +</script> +<script id="colorPixelShader" type="text/glsl"> + uniform vec4 ambient; + uniform vec4 diffuse; + uniform vec4 specular; + uniform float shininess; + + varying vec4 vposition; + varying vec4 vworldPosition; + varying vec3 vnormal; + varying vec4 vprojTextureCoords; + + uniform sampler2D shadowMapSampler; + + uniform vec3 lightWorldPos; + uniform mat4 viewInverse; + + vec3 lighting(vec3 position, vec3 normal, vec4 pigment, vec4 specular, float shininess) { + vec3 l = normalize(vec3(lightWorldPos) - position); // Toward light. + vec3 n = normalize(normal); // Normal. + vec3 v = normalize(vec3(viewInverse * vec4(0,0,0,1)) - position); // Toward eye. + vec3 r = normalize(-reflect(v, n)); + + return clamp(dot(n,l), 0.0, 1.0) * diffuse.rgb + + 0.2 * specular.rgb * pow(max(dot(l, r), 0.0), shininess); + } + + void main() { + vec3 outColor = ambient.rgb; + vec4 projCoords = vprojTextureCoords; + + // Convert texture coords to [0, 1] range. + projCoords /= projCoords.w; + projCoords.x = 0.5 * projCoords.x + 0.5; + projCoords.y = 0.5 * projCoords.y + 0.5; + projCoords.z = 0.5 * projCoords.z + 0.5; + + float depth = projCoords.z; + + float light; + + // If the rednered point is farther from the light than the distance encoded + // in the shadow map, we give it a light coefficient of 0. + vec4 color = texture2D(shadowMapSampler, projCoords.xy); + + light = (color.a + + color.b / 256.0 + + color.g / 65536.0 + + color.r / 16777216.0) + 0.008 > depth ? 1.0 : 0.0; + + // Make the illuninated area a round spotlight shape just for fun. + // Comment this line out to see just the shadows. + light *= 1.0 - smoothstep(0.45, 0.5, + length(projCoords.xy - vec2(0.5, 0.5))); + + outColor += light * lighting( + vec3(vworldPosition), + vnormal, + diffuse, + specular, + shininess); + + gl_FragColor = vec4(outColor, 1.0); + } +</script> + + + +</head> +<body> +<h1>Shadow Maps</h1> +This sample implements a basic shadow map. +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" width="800px" height="600px"></div> +<!-- End of O3D plugin --> +Use A, S, D, W, I and O to move the light. +Press spacebar to see the shadow map. +</body> +</html> diff --git a/o3d/samples/o3d-webgl/base.js b/o3d/samples/o3d-webgl/base.js index 9863ee6..9962de5 100644 --- a/o3d/samples/o3d-webgl/base.js +++ b/o3d/samples/o3d-webgl/base.js @@ -149,19 +149,31 @@ o3d.inherit = function(subClassName, superClassName) { subClass.prototype = new superClass; subClass.prototype.superClassName = superClassName; subClass.prototype.superClass = superClass; + subClass.prototype.className = subClassName; }; /** + * Utility function to remove an object from an array. + * @param {!Array} array The array. + * @param {Object} object The thing to be removed. + */ +o3d.removeFromArray = function(array, object) { + var i = array.indexOf(object); + if (i >= 0) { + array.splice(i, 1); + } +} + + +/** * If an o3d function has not been implemented in javascript yet, it should * call this function to throw an error because it's better than doing * nothing. */ o3d.notImplemented = function() { - var callerName = arguments.caller.toString(); - callerName = callerName.substr('function '.length); - callerName = ownName.substr(0, ownName.indexOf('(')); - throw 'Not implemented: ' + callerName; + debugger; + throw 'Not implemented.'; }; @@ -179,6 +191,8 @@ o3d.include('param_object'); o3d.include('param'); o3d.include('event'); o3d.include('raw_data'); +o3d.include('texture'); +o3d.include('bitmap'); o3d.include('file_request'); o3d.include('client'); o3d.include('render_node'); @@ -192,7 +206,6 @@ o3d.include('render_surface_set'); o3d.include('render_surface'); o3d.include('state'); o3d.include('draw_context'); -o3d.include('texture'); o3d.include('sampler'); o3d.include('transform'); o3d.include('pack'); diff --git a/o3d/samples/o3d-webgl/bitmap.js b/o3d/samples/o3d-webgl/bitmap.js index 6ce1df7..5e2fcfc 100644 --- a/o3d/samples/o3d-webgl/bitmap.js +++ b/o3d/samples/o3d-webgl/bitmap.js @@ -73,13 +73,20 @@ o3d.Bitmap.IMAGE = 6; o3d.Bitmap.SLICE = 7; +/** + * In webgl the bitmap object is represented by an offscreen canvas. + * @type {Canvas} + * @private + */ +o3d.Bitmap.prototype.canvas_ = null; + /** * Flips a bitmap vertically in place. - * @type {boolean} */ -o3d.Bitmap.prototype.flipVertically = false; - +o3d.Bitmap.prototype.flipVertically = function() { + this.defer_flip_vertically_to_texture_ = true; +}; /** @@ -94,7 +101,7 @@ o3d.Bitmap.prototype.flipVertically = false; */ o3d.Bitmap.prototype.generateMips = function(source_level, num_levels) { - o3d.notImplemented(); + this.defer_mipmaps_to_texture_ = true; }; @@ -113,12 +120,29 @@ o3d.Bitmap.prototype.width = 0; o3d.Bitmap.prototype.height = 0; +/** + * Instead of generating mipmaps in the bitmap object, just set this boolean + * to true, then the texture will generate mipmaps when it loads the bitmap. + * @type {boolean} + * @private + */ +o3d.Bitmap.prototype.defer_mipmaps_to_texture_ = false; + + +/** + * Instead of flipping vertically in the bitmap object, just set this boolean + * to true, then the texture will generate mipmaps when it loads the bitmap. + * @type {boolean} + * @private + */ +o3d.Bitmap.prototype.defer_flip_vertically_to_texture_ = false; + /** * The format of the bitmap (read only). - * @type {number} + * @type {!o3d.Texture.Format} */ -o3d.Bitmap.prototype.format = 0; +o3d.Bitmap.prototype.format = o3d.Texture.UNKNOWN_FORMAT; @@ -132,9 +156,9 @@ o3d.Bitmap.prototype.numMipmaps = 1; /** * The Semantic of the bitmap. - * @type {!o3d.Stream.Semantic} + * @type {!o3d.Bitmap.Semantic} */ -o3d.Bitmap.prototype.semantic = o3d.Stream.UNKNOWN_SEMANTIC; +o3d.Bitmap.prototype.semantic = o3d.Bitmap.UNKNOWN_SEMANTIC; diff --git a/o3d/samples/o3d-webgl/client.js b/o3d/samples/o3d-webgl/client.js index 5bbb7b2..95912a9 100644 --- a/o3d/samples/o3d-webgl/client.js +++ b/o3d/samples/o3d-webgl/client.js @@ -125,7 +125,10 @@ o3d.Renderer.installRenderInterval = function() { * The ClientInfo is used to get information about the client. * @constructor */ -o3d.ClientInfo = function() { }; +o3d.ClientInfo = function() { + o3d.NamedObject.call(this); +}; +o3d.inherit('ClientInfo', 'NamedObject'); /** @@ -208,6 +211,7 @@ o3d.ClientInfo.prototype.non_power_of_two_textures = true; * @constructor */ o3d.Client = function() { + o3d.NamedObject.call(this); this.root = new o3d.Transform; this.renderGraphRoot = new o3d.RenderNode; this.root = new o3d.Transform; @@ -231,7 +235,7 @@ o3d.Client.RenderCallback = goog.typedef; o3d.Client.TickCallback = goog.typedef; /** - * @type {function(!o3d.Event): void} + * @type {function(string): void} */ o3d.Client.ErrorCallback = goog.typedef; @@ -262,10 +266,13 @@ o3d.Client.prototype.then_ = 0; */ o3d.Client.prototype.root = null; + /** * Function that gets called when the client encounters an error. */ -o3d.Client.prototype.error_callback = function(error_message) {}; +o3d.Client.prototype.error_callback = function(error_message) { + alert(error_message); +}; /** @@ -300,7 +307,7 @@ o3d.Client.prototype.cleanup = function () { /** * Creates a pack object. * A pack object. - * @returns {!o3d.Pack} A new pack object. + * @return {!o3d.Pack} A new pack object. */ o3d.Client.prototype.createPack = function() { @@ -314,7 +321,7 @@ o3d.Client.prototype.createPack = * Searches the Client for an object matching the given id. * * @param {number} id The id of the object to look for. - * @returns {o3d.ObjectBase} The object or null if a object + * @return {o3d.ObjectBase} The object or null if a object * with the given id is not found. */ o3d.Client.prototype.getObjectById = @@ -327,7 +334,7 @@ o3d.Client.prototype.getObjectById = * Searches the Client for objects of a particular name and type. * @param {string} name name of object to look for. * @param {string} class_name name of class to look for. - * @returns {!Array.<!o3d.ObjectBase>} Array of objects found. + * @return {!Array.<!o3d.ObjectBase>} Array of objects found. */ o3d.Client.prototype.getObjects = function(name, class_name) { @@ -339,7 +346,7 @@ o3d.Client.prototype.getObjects = /** * Searches the Client for objects of a particular type. * @param {string} class_name name of class to look for. - * @returns {!Array.<!Object>} Array of objects found. + * @return {!Array.<!Object>} Array of objects found. */ o3d.Client.prototype.getObjectsByClassName = function(class_name) { @@ -506,6 +513,27 @@ o3d.Client.prototype.__defineSetter__('height', /** + * Initializes this client using the canvas. + * @param {Canvas} + */ +o3d.Client.prototype.initWithCanvas = function(canvas) { + var gl; + try {gl = canvas.getContext("experimental-webgl") } catch(e) { } + if (!gl) + try {gl = canvas.getContext("moz-webgl") } catch(e) { } + if (!gl) { + alert("No WebGL context found"); + return null; + } + + canvas.client.gl = gl; + gl.client = this; + gl.displayInfo = {width: canvas.width, + height: canvas.height}; +}; + + +/** * Sets the per frame render callback. * * Note: The callback will not be called recursively. When your callback is @@ -736,7 +764,7 @@ o3d.Client.prototype.invalidateAllParameters = function() { * @param {string} mime_type The type of data url you want. * Currently O3D only supports image/png. See HTML5 canvas tag * for info about toDataURL. - * @returns {string} A Data URL for the backbuffer. + * @return {string} A Data URL for the backbuffer. */ o3d.Client.prototype.toDataURL = function(opt_mime_type) { diff --git a/o3d/samples/o3d-webgl/draw_context.js b/o3d/samples/o3d-webgl/draw_context.js index ee30d9b..bf360cb 100644 --- a/o3d/samples/o3d-webgl/draw_context.js +++ b/o3d/samples/o3d-webgl/draw_context.js @@ -42,6 +42,7 @@ * @constructor */ o3d.DrawContext = function(opt_view, opt_projection) { + o3d.NamedObject.call(this); this.view = opt_view || [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; this.projection = opt_projection || diff --git a/o3d/samples/o3d-webgl/draw_list.js b/o3d/samples/o3d-webgl/draw_list.js index 537751b..a0cffca4 100644 --- a/o3d/samples/o3d-webgl/draw_list.js +++ b/o3d/samples/o3d-webgl/draw_list.js @@ -37,6 +37,7 @@ * @constructor */ o3d.DrawList = function() { + o3d.NamedObject.call(this); this.list_ = []; }; o3d.inherit('DrawList', 'NamedObject'); @@ -72,7 +73,6 @@ o3d.DrawList.BY_PRIORITY = 2; */ o3d.DrawList.prototype.render = function() { // TODO(petersont): Add sort. - for (var i = 0; i < this.list_.length; ++i) { var drawElementInfo = this.list_[i]; var world = drawElementInfo.world; diff --git a/o3d/samples/o3d-webgl/draw_pass.js b/o3d/samples/o3d-webgl/draw_pass.js index f3deea8..495e357 100644 --- a/o3d/samples/o3d-webgl/draw_pass.js +++ b/o3d/samples/o3d-webgl/draw_pass.js @@ -39,6 +39,7 @@ * @constructor */ o3d.DrawPass = function(opt_drawList, opt_sortMethod) { + o3d.RenderNode.call(this); this.drawList = opt_drawList; this.sortMethod = opt_sortMethod || o3d.DrawList.BY_PERFORMANCE; }; diff --git a/o3d/samples/o3d-webgl/effect.js b/o3d/samples/o3d-webgl/effect.js index 53d8882..f8d7dc5 100644 --- a/o3d/samples/o3d-webgl/effect.js +++ b/o3d/samples/o3d-webgl/effect.js @@ -89,6 +89,7 @@ o3d.EffectParameterInfo.prototype.sas_class_name = ''; * @constructor */ o3d.EffectStreamInfo = function(opt_semantic, opt_semantic_index) { + o3d.NamedObject.call(this); if (!opt_semantic) { opt_semantic = o3d.Stream.UNKNOWN_SEMANTIC; } @@ -176,6 +177,13 @@ o3d.Effect.prototype.loadShaderFromString = function(shaderString, type) { var shader = this.gl.createShader(type); this.gl.shaderSource(shader, shaderString); this.gl.compileShader(shader); + + var log = this.gl.getShaderInfoLog(shader); + if (log != '') { + this.gl.client.error_callback( + 'Shader compile failed with error log:\n' + log); + } + this.gl.attachShader(this.program_, shader); }; @@ -245,24 +253,24 @@ o3d.Effect.prototype.createUniformParameters = 'worldView': true, 'worldProjection': true, 'worldViewProjection': true, - 'worldI': true, - 'viewI': true, - 'projectionI': true, - 'worldViewI': true, - 'worldProjectionI': true, - 'worldViewProjectionI': true, - 'worldT': true, - 'viewT': true, - 'projectionT': true, - 'worldViewT': true, - 'worldProjectionT': true, - 'worldViewProjectionT': true, - 'worldIT': true, - 'viewIT': true, - 'projectionIT': true, - 'worldViewIT': true, - 'worldProjectionIT': true, - 'worldViewProjectionIT': true}; + 'worldInverse': true, + 'viewInverse': true, + 'projectionInverse': true, + 'worldViewInverse': true, + 'worldProjectionInverse': true, + 'worldViewProjectionInverse': true, + 'worldTranspose': true, + 'viewTranspose': true, + 'projectionTranspose': true, + 'worldViewTranspose': true, + 'worldProjectionTranspose': true, + 'worldViewProjectionTranspose': true, + 'worldInverseTranspose': true, + 'viewInverseTranspose': true, + 'projectionInverseTranspose': true, + 'worldViewInverseTranspose': true, + 'worldProjectionInverseTranspose': true, + 'worldViewProjectionInverseTranspose': true}; for (name in this.uniforms) { var info = this.uniforms[name].info; @@ -330,7 +338,7 @@ o3d.Effect.prototype.createSASParameters = /** * Gets info about the parameters this effect needs. - * @returns {!Array.<!o3d.EffectParameterInfo>} an array of + * @return {!Array.<!o3d.EffectParameterInfo>} an array of * EffectParameterInfo objects. */ o3d.Effect.prototype.getParameterInfo = function() { @@ -341,7 +349,7 @@ o3d.Effect.prototype.getParameterInfo = function() { /** * Gets info about the streams this effect needs. - * @returns {!Array.<!o3d.EffectStreamInfo>} an array of + * @return {!Array.<!o3d.EffectStreamInfo>} an array of * EffectStreamInfo objects. */ o3d.Effect.prototype.getStreamInfo = function() { @@ -390,6 +398,12 @@ o3d.Effect.prototype.searchForParams = function(object_list) { } } } + + for (name in this.uniforms) { + if (!filled_map[name]) { + throw ('Uniform param not filled: '+name); + } + } }; diff --git a/o3d/samples/o3d-webgl/element.js b/o3d/samples/o3d-webgl/element.js index 8a953a8..e42a788 100644 --- a/o3d/samples/o3d-webgl/element.js +++ b/o3d/samples/o3d-webgl/element.js @@ -164,7 +164,7 @@ o3d.Element.prototype.drawElements = []; * pass null it will use the material on this Element. This allows you * to easily setup the default (just draw as is) by passing null or * setup a shadow pass by passing in a shadow material. - * @returns {!o3d.DrawElement} The created draw element. + * @return {!o3d.DrawElement} The created draw element. */ o3d.Element.prototype.createDrawElement = function(pack, material) { @@ -183,7 +183,7 @@ o3d.Element.prototype.createDrawElement = * @param {o3d.Cull} cull which side of the triangles to ignore. * @param {!o3d.math.Point3} start position of start of ray in local space. * @param {!o3d.math.Point3} end position of end of ray. in local space. - * @returns {!o3d.RayIntersectionInfo} RayIntersectionInfo class. If valid() + * @return {!o3d.RayIntersectionInfo} RayIntersectionInfo class. If valid() * is false then something was wrong, Check GetLastError(). If * intersected() is true then the ray intersected a something. position() * is the exact point of intersection. @@ -198,7 +198,7 @@ o3d.Element.prototype.intersectRay = * Computes the bounding box in same coordinate system as the specified * POSITION stream. * @param {number} position_stream_index Index of POSITION stream. - * @returns {!o3d.BoundingBox} The boundingbox for this element in local space. + * @return {!o3d.BoundingBox} The boundingbox for this element in local space. */ o3d.Element.prototype.getBoundingBox = function(position_stream_index) { diff --git a/o3d/samples/o3d-webgl/event.js b/o3d/samples/o3d-webgl/event.js index 91bfcbd..c928e23 100644 --- a/o3d/samples/o3d-webgl/event.js +++ b/o3d/samples/o3d-webgl/event.js @@ -35,7 +35,9 @@ * as an argument to event handlers triggered by the plugin. * @constructor */ -o3d.Event = function() { }; +o3d.Event = function() { + o3d.ObjectBase.call(this); +}; o3d.inherit('Event', 'ObjectBase'); @@ -233,7 +235,9 @@ o3d.Event.prototype.fullscreen = false; * An Event that gets sent to the render callback. * @constructor */ -o3d.RenderEvent = function() {}; +o3d.RenderEvent = function() { + o3d.Event.call(this); +}; o3d.inherit('RenderEvent', 'Event'); @@ -248,7 +252,9 @@ o3d.RenderEvent.prototype.elapsedTime = 0; * An Event that gets sent to the render callback. * @constructor */ -o3d.TickEvent = function() {}; +o3d.TickEvent = function() { + o3d.Event.call(this); +}; o3d.inherit('RenderEvent', 'Event'); diff --git a/o3d/samples/o3d-webgl/field.js b/o3d/samples/o3d-webgl/field.js index 5f82237..23058bf 100644 --- a/o3d/samples/o3d-webgl/field.js +++ b/o3d/samples/o3d-webgl/field.js @@ -37,7 +37,9 @@ * Buffer the Field's buffer property will be set to null. * @constructor */ -o3d.Field = function() { }; +o3d.Field = function() { + o3d.NamedObject.call(this); +}; o3d.inherit('Field', 'NamedObject'); /** @@ -101,7 +103,7 @@ o3d.Field.prototype.setAt = * * @param {number} start_index index of the first value to get. * @param {number} num_elements number of elements to read from field. - * @returns {number} The values of the field. + * @return {number} The values of the field. */ o3d.Field.prototype.getAt = function(start_index, num_elements) { @@ -114,21 +116,27 @@ o3d.Field.prototype.getAt = * A field that contains floating point numbers. * @constructor */ -o3d.FloatField = function() { }; +o3d.FloatField = function() { + o3d.Field.call(this); +}; o3d.inherit('FloatField', 'Field'); /** * A field that contains unsigned integers. * @constructor */ -o3d.UInt32Field = function() { }; +o3d.UInt32Field = function() { + o3d.Field.call(this); +}; o3d.inherit('UInt32Field', 'Field'); /** * A field that contains unsigned bytes. * @constructor */ -o3d.UByteNField = function() { }; +o3d.UByteNField = function() { + o3d.Field.call(this); +}; o3d.inherit('UByteNField', 'Field'); diff --git a/o3d/samples/o3d-webgl/file_request.js b/o3d/samples/o3d-webgl/file_request.js index 122364a..8c0d44c 100644 --- a/o3d/samples/o3d-webgl/file_request.js +++ b/o3d/samples/o3d-webgl/file_request.js @@ -133,6 +133,13 @@ o3d.FileRequest.prototype.done = false; o3d.FileRequest.prototype.success = false; +/** + * The image object if we are opening an image. + * @type {Image} + * @private + */ +o3d.FileRequest.prototype.image_ = null; + /** * An error message. @@ -143,17 +150,53 @@ o3d.FileRequest.prototype.success = false; o3d.FileRequest.prototype.error = ''; +/** + * Guesses from a url whether the url is an image file. + * @param {string} url The URL. + * @private + */ +o3d.FileRequest.prototype.isImageUrl_ = function(url) { + var extension = url.substring(url.length - 4); + return (extension == '.png' || extension == '.jpg'); +}; + + +/** + * Called by the image class when the image file is loaded... if we're + * loading an image. + * @private + */ +o3d.FileRequest.prototype.imageLoaded_ = function() { + if (this.image_.complete) { + this.success = true; + this.done = true; + this.readyState = 4; + this.data = new o3d.RawData(); + this.data.image_ = this.image_; + } + this.onreadystatechange.apply(this, arguments); +}; + /** * Set up several of the request fields. * @param {string} method "GET" is the only supported method at this time * @param {string} uri the location of the file to fetch - * @param {boolean} async true is the only legal value at this time + * @param {boolean} async true is the only legal value at this time. */ o3d.FileRequest.prototype.open = function(method, uri, async) { this.uri = uri; - this.request_.open(method, uri, async); + if (this.isImageUrl_(uri)) { + this.image_ = new Image(); + var that = this; + this.image_.onload = function() { + that.imageLoaded_.call(that); + } + this.image_.src = uri; + } else { + this.request_.open(method, uri, async); + } }; @@ -163,7 +206,7 @@ o3d.FileRequest.prototype.open = * matter what, with success or failure. */ o3d.FileRequest.prototype.send = function() { - this.request_.send(); + // This function left blank for compatability with o3djs.io. }; diff --git a/o3d/samples/o3d-webgl/named_object.js b/o3d/samples/o3d-webgl/named_object.js index ce6c871..0fd1ec2 100644 --- a/o3d/samples/o3d-webgl/named_object.js +++ b/o3d/samples/o3d-webgl/named_object.js @@ -31,29 +31,12 @@ /** - * Takes the name of a class as an argument, and returns true if this object is - * either an instance of that class or derives from that class. - * - * var t = pack.createObject('o3d.Transform'); - * t.isAClassName('o3d.Transform'); - * t.isAClassName('o3d.ParamObject'); - * t.isAClassName('o3d.Shape'); - * - * @param {o3d.String} class_name Name of class to check for. - * @returns {o3d.bool} true if this object is a or is derived from the given - * class name. - */ -o3d.ObjectBase.prototype.isAClassName = - function(class_name) { - o3d.notImplemented(); -}; - - -/** * Base class for all objects that can have their name set. * @constructor */ -o3d.NamedObject = function() { }; +o3d.NamedObject = function() { + o3d.NamedObjectBase.call(this); +}; o3d.inherit('NamedObject', 'NamedObjectBase'); diff --git a/o3d/samples/o3d-webgl/named_object_base.js b/o3d/samples/o3d-webgl/named_object_base.js index 5176426..b3b6858 100644 --- a/o3d/samples/o3d-webgl/named_object_base.js +++ b/o3d/samples/o3d-webgl/named_object_base.js @@ -34,7 +34,9 @@ * Base class for all objects that are identifiable by a name. * @constructor */ -o3d.NamedObjectBase = function() { }; +o3d.NamedObjectBase = function() { + o3d.ObjectBase.call(this); +}; o3d.inherit('NamedObjectBase', 'ObjectBase'); diff --git a/o3d/samples/o3d-webgl/object_base.js b/o3d/samples/o3d-webgl/object_base.js index 42931eb..a0f81b9 100644 --- a/o3d/samples/o3d-webgl/object_base.js +++ b/o3d/samples/o3d-webgl/object_base.js @@ -54,14 +54,16 @@ o3d.ObjectBase.prototype.superClass = null; /** * Traverses the current object's class and all its superclasses and * determines if any of them are of the given name. - * returns {bool} + * @param {string} className The name of a class. + * @return {boolean} Whether this is counts as a className. */ -o3d.ObjectBase.isAClassName = function(o, className) { - while (o != undefined) { - if (o.className == 'className') { +o3d.ObjectBase.prototype.isAClassName = function(className) { + var object = this; + while (object != undefined) { + if (object.className == className) { return true; } - o = o.superClass.prototype; + object = object.superClass && object.superClass.prototype; } return false; }; diff --git a/o3d/samples/o3d-webgl/pack.js b/o3d/samples/o3d-webgl/pack.js index fc10e49..925e5f7 100644 --- a/o3d/samples/o3d-webgl/pack.js +++ b/o3d/samples/o3d-webgl/pack.js @@ -39,6 +39,7 @@ * @constructor */ o3d.Pack = function() { + o3d.NamedObject.call(this); this.objects_ = []; }; o3d.inherit('Pack', 'NamedObject'); @@ -105,22 +106,12 @@ o3d.Pack.prototype.destroy = function() { * Now the last reference has been removed and myTransform will be freed. * * @param {o3d.ObjectBase} object Object to remove. - * @returns {boolean} True if the object was successfully removed. + * @return {boolean} True if the object was successfully removed. * False if the object is not part of this pack. */ o3d.Pack.prototype.removeObject = function(object) { - var i = 0; - for (var j = 0; j < this.objects_.length; ++j) { - if (this.objects_[i] == object) { - j++; - } - this.objects_[j] = this.objects_[i]; - i++; - } - if (this.objects_.length > i) { - this.objects_.pop(); - } + o3d.removeFromArray(this.objects_, object); }; @@ -176,7 +167,7 @@ o3d.Pack.prototype.removeObject = * ParamOp4FloatsToFloat4 * ParamOp16FloatsToMatrix4 * TRSToMatrix4 - * @returns {o3d.ObjectBase} The created object. + * @return {o3d.ObjectBase} The created object. */ o3d.Pack.prototype.createObject = function(type_name) { @@ -186,6 +177,7 @@ o3d.Pack.prototype.createObject = } var object = new foo(); object.gl = this.gl; + this.objects_.push(object); return object; }; @@ -204,11 +196,32 @@ o3d.Pack.prototype.createObject = * compelete mipmap chain. * @param {boolean} enable_render_surfaces If true, the texture object will * expose RenderSurface objects through GetRenderSurface(...). - * @returns {!o3d.Texture2D} The Texture2D object. + * @return {!o3d.Texture2D} The Texture2D object. */ o3d.Pack.prototype.createTexture2D = function(width, height, format, levels, enable_render_surfaces) { - o3d.notImplemented(); + var texture = this.createObject('Texture2D'); + texture.width = width; + texture.height = height; + texture.levels = levels; + texture.texture_ = this.gl.createTexture(); + + if (enable_render_surfaces) { + this.gl.bindTexture(this.gl.TEXTURE_2D, texture.texture_); + this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, + 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); + + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); + } + + return texture; }; @@ -225,7 +238,7 @@ o3d.Pack.prototype.createTexture2D = * the compelete mipmap chain. * @param {boolean} enable_render_surfaces If true, the texture object * will expose RenderSurface objects through GetRenderSurface(...). - * @returns {!o3d.TextureCUBE} The TextureCUBE object. + * @return {!o3d.TextureCUBE} The TextureCUBE object. */ o3d.Pack.prototype.createTextureCUBE = function(edge_length, format, levels, enable_render_surfaces) { @@ -241,11 +254,13 @@ o3d.Pack.prototype.createTextureCUBE = * * @param {number} width The width of the RenderSurface in pixels * @param {number} height The height of the RenderSurface in pixels - * @returns {!o3d.RenderDepthStencilSurface} The RenderSurface object. + * @return {!o3d.RenderDepthStencilSurface} The RenderSurface object. */ o3d.Pack.prototype.createDepthStencilSurface = function(width, height) { - o3d.notImplemented(); + var surface = this.createObject("RenderDepthStencilSurface"); + surface.initWithSize_(width, height); + return surface; }; @@ -260,7 +275,7 @@ o3d.Pack.prototype.createDepthStencilSurface = * @param {string} class_type_name the Class of the object. It is okay * to pass base types for example "o3d.RenderNode" will return * ClearBuffers, DrawPasses, etc... - * @returns {!Array.<!o3d.ObjectBase>} Array of Objects. + * @return {!Array.<!o3d.ObjectBase>} Array of Objects. */ o3d.Pack.prototype.getObjects = function(name, class_type_name) { @@ -278,11 +293,24 @@ o3d.Pack.prototype.getObjects = * @param {string} class_type_name the Class of the object. It is * okay to pass base types for example "o3d.RenderNode" will return * ClearBuffers, DrawPasses, etc... - * @returns {!Array.<!o3d.ObjectBase>} Array of Objects. + * @return {!Array.<!o3d.ObjectBase>} Array of Objects. */ o3d.Pack.prototype.getObjectsByClassName = function(class_type_name) { - o3d.notImplemented(); + if (class_type_name.substr(0, 4) == 'o3d.') { + class_type_name = class_type_name.substr(4); + } + + var found = []; + + for (var i = 0; i < this.objects_.length; ++i) { + var object = this.objects_[i]; + if (object.isAClassName(class_type_name)) { + found.push(object); + } + } + + return found; }; @@ -311,7 +339,7 @@ o3d.Pack.prototype.objects_ = []; * load a texture is to load a RawData, use that to create Bitmap, Massage * the Bitmap to your liking the use that to create a Texture. * @param {string} type Must be "TEXTURE" or "RAWDATA" - * @returns {!o3d.FileRequest} a FileRequest + * @return {!o3d.FileRequest} a FileRequest */ o3d.Pack.prototype.createFileRequest = function(type) { @@ -328,18 +356,43 @@ o3d.Pack.prototype.createFileRequest = * * @param {!o3d.RawData} raw_data contains the bitmap data in a supported * format. - * @returns {!o3d.Bitmap} An Array of Bitmaps object. + * @return {!Array.<!o3d.Bitmap>} An Array of Bitmaps object. */ o3d.Pack.prototype.createBitmapsFromRawData = function(raw_data) { - o3d.notImplemented(); + var bitmap = this.createObject('Bitmap') + if (!raw_data.image_) { + throw ('Cannot create bitmap from non-image data.'); + return []; + } + bitmap.height = raw_data.image_.height; + bitmap.width = raw_data.image_.width; + + var canvas = document.createElement('CANVAS'); + + canvas.width = bitmap.width; + canvas.height = bitmap.height; + + bitmap.canvas_ = canvas; + var context = canvas.getContext('2d'); + // Flip it. + context.translate(0, bitmap.height); + context.scale(1, -1); + context.drawImage(raw_data.image_, + 0, 0, bitmap.width, bitmap.height); + + // TODO(petersont): I'm not sure how to get the format. + bitmap.format = o3d.Texture.ARGB8; + bitmap.numMipmaps = 1; + + return [bitmap]; }; /** * Create RawData given a data URL. * @param {string} data_url The data URL from which to create the RawData. - * @returns {!o3d.RawData} The RawData. + * @return {!o3d.RawData} The RawData. */ o3d.Pack.prototype.createRawDataFromDataURL = function(data_url) { diff --git a/o3d/samples/o3d-webgl/param.js b/o3d/samples/o3d-webgl/param.js index 0bb28fe..3b23f58 100644 --- a/o3d/samples/o3d-webgl/param.js +++ b/o3d/samples/o3d-webgl/param.js @@ -79,13 +79,39 @@ o3d.Param.prototype.outputConnections = []; o3d.Param.prototype.owner_ = null; /** + * @type {Object} The value of the parameter. + */ +o3d.Param.prototype.value_ = null; + +o3d.Param.prototype.__defineSetter__('value', + function(v) { + if (this.inputConnection) { + throw ('Tried to set bound parameter.'); + } else { + this.value_ = v; + } + } +); + +o3d.Param.prototype.__defineGetter__('value', + function() { + if (this.inputConnection) { + return this.inputConnection.value; + } else { + return this.value_; + } + } +); + + +/** * Directly binds two Param elements such that this parameter gets its value * from the source parameter. The source must be compatible with this * parameter. * * @param {o3d.Param} source_param The parameter that the value originates * from. Passing in null will unbind any parameter currently bound. - * @returns {boolean} True if the bind succeeded. + * @return {boolean} True if the bind succeeded. */ o3d.Param.prototype.bind = function(source_param) { @@ -130,20 +156,6 @@ o3d.Param.prototype.unbindOutputs = function() { o3d.Param.prototype.read_only_ = false; - -/** - * The input connection for this param. - */ -o3d.Param.prototype.input_connection = null; - - - -/** - * The output connections for this param. - */ -o3d.Param.prototype.output_connections = []; - - /** * @constructor */ @@ -703,7 +715,7 @@ o3d.inherit('WorldViewProjectionInverseTransposeParamMatrix4', * Called to specify the value of a uniform variable. */ o3d.ParamFloat.prototype.applyToLocation = function(gl, location) { - gl.uniform4fv(location, this.value); + gl.uniform1f(location, this.value); }; /** @@ -736,6 +748,18 @@ o3d.ParamMatrix4.prototype.applyToLocation = function(gl, location) { o3d.Transform.flattenMatrix4(this.value)); }; +/** + * Called to specify the value of a uniform variable. + */ +o3d.ParamSampler.prototype.applyToLocation = function(gl, location) { + var i = 0; + gl.activeTexture(gl.TEXTURE0 + i); + if (!this.value || !this.value.texture || !this.value.texture.texture_) { + throw ('Attempt to use texture parameter before texture value set.'); + } + gl.bindTexture(gl.TEXTURE_2D, this.value.texture.texture_); + gl.uniform1i(location, i); +}; /** @@ -759,43 +783,43 @@ o3d.Param.SAS.createParam('viewProjection', o3d.Param.SAS.createParam('worldViewProjection', 'WorldViewProjectionParamMatrix4'); -o3d.Param.SAS.createParam('worldI', +o3d.Param.SAS.createParam('worldInverse', 'WorldInverseParamMatrix4'); -o3d.Param.SAS.createParam('viewI', +o3d.Param.SAS.createParam('viewInverse', 'ViewInverseParamMatrix4'); -o3d.Param.SAS.createParam('projectionI', +o3d.Param.SAS.createParam('projectionInverse', 'ProjectionInverseParamMatrix4'); -o3d.Param.SAS.createParam('worldViewI', +o3d.Param.SAS.createParam('worldViewInverse', 'WorldViewInverseParamMatrix4'); -o3d.Param.SAS.createParam('viewProjectionI', +o3d.Param.SAS.createParam('viewProjectionInverse', 'ViewProjectionInverseParamMatrix4'); -o3d.Param.SAS.createParam('worldViewProjectionI', +o3d.Param.SAS.createParam('worldViewProjectionInverse', 'WorldViewProjectionInverseParamMatrix4'); -o3d.Param.SAS.createParam('worldT', +o3d.Param.SAS.createParam('worldTranspose', 'WorldInverseParamMatrix4'); -o3d.Param.SAS.createParam('viewT', +o3d.Param.SAS.createParam('viewTranspose', 'ViewTransposeParamMatrix4'); -o3d.Param.SAS.createParam('projectionT', +o3d.Param.SAS.createParam('projectionTranspose', 'ProjectionTransposeParamMatrix4'); -o3d.Param.SAS.createParam('worldViewT', +o3d.Param.SAS.createParam('worldViewTranspose', 'WorldViewTransposeParamMatrix4'); -o3d.Param.SAS.createParam('viewProjectionT', +o3d.Param.SAS.createParam('viewProjectionTranspose', 'ViewProjectionTransposeParamMatrix4'); -o3d.Param.SAS.createParam('worldViewProjectionT', +o3d.Param.SAS.createParam('worldViewProjectionTranspose', 'WorldViewProjectionTransposeParamMatrix4'); -o3d.Param.SAS.createParam('worldIT', +o3d.Param.SAS.createParam('worldInverseTranspose', 'WorldInverseTransposeParamMatrix4'); -o3d.Param.SAS.createParam('viewIT', +o3d.Param.SAS.createParam('viewInverseTranspose', 'ViewInverseTransposeParamMatrix4'); -o3d.Param.SAS.createParam('projectionIT', +o3d.Param.SAS.createParam('projectionInverseTranspose', 'ProjectionInverseTransposeParamMatrix4'); -o3d.Param.SAS.createParam('worldViewIT', +o3d.Param.SAS.createParam('worldViewInverseTranspose', 'WorldViewInverseTransposeParamMatrix4'); -o3d.Param.SAS.createParam('viewProjectionIT', +o3d.Param.SAS.createParam('viewProjectionInverseTranspose', 'ViewProjectionInverseTransposeParamMatrix4'); -o3d.Param.SAS.createParam('worldViewProjectionIT', +o3d.Param.SAS.createParam('worldViewProjectionInverseTranspose', 'WorldViewProjectionInverseTransposeParamMatrix4'); /** diff --git a/o3d/samples/o3d-webgl/param_object.js b/o3d/samples/o3d-webgl/param_object.js index 625f876..5c5ea89 100644 --- a/o3d/samples/o3d-webgl/param_object.js +++ b/o3d/samples/o3d-webgl/param_object.js @@ -36,6 +36,7 @@ * @constructor */ o3d.ParamObject = function() { + o3d.NamedObject.call(this); this.params_ = {}; }; o3d.inherit('ParamObject', 'NamedObject'); @@ -95,7 +96,7 @@ o3d.inherit('ParamObject', 'NamedObject'); * 'WorldViewProjectionInverseParamMatrix4' * 'WorldViewProjectionTransposeParamMatrix4' * 'WorldViewProjectionInverseTransposeParamMatrix4' - * @returns {!o3d.Param} The newly created Param or null on failure. + * @return {!o3d.Param} The newly created Param or null on failure. */ o3d.ParamObject.prototype.createParam = function(param_name, param_type_name) { @@ -113,7 +114,7 @@ o3d.ParamObject.prototype.createParam = * Searches by name for a Param defined in the object. * * @param {string} param_name Name to search for. - * @returns {!o3d.Param} The Param with the given name, or null otherwise. + * @return {!o3d.Param} The Param with the given name, or null otherwise. */ o3d.ParamObject.prototype.getParam = function(param_name) { @@ -128,7 +129,7 @@ o3d.ParamObject.prototype.getParam = * or if the param is unremovable. * * @param {!o3d.Param} param param to remove. - * @returns {boolean} True if the param was removed. + * @return {boolean} True if the param was removed. */ o3d.ParamObject.prototype.removeParam = function(param) { diff --git a/o3d/samples/o3d-webgl/raw_data.js b/o3d/samples/o3d-webgl/raw_data.js index ab3c0f4..59a441c 100644 --- a/o3d/samples/o3d-webgl/raw_data.js +++ b/o3d/samples/o3d-webgl/raw_data.js @@ -44,7 +44,9 @@ * request.send(); * @constructor */ -o3d.RawData = function() { }; +o3d.RawData = function() { + o3d.NamedObject.call(this); +}; o3d.inherit('RawData', 'NamedObject'); @@ -56,6 +58,13 @@ o3d.inherit('RawData', 'NamedObject'); o3d.RawData.prototype.string_value = ''; +/** + * The data as an image if it is an image. + * @type {Image} + * @private + */ +o3d.RawData.prototype.image_ = null; + /** * The uri of the RawData. diff --git a/o3d/samples/o3d-webgl/ray_intersection_info.js b/o3d/samples/o3d-webgl/ray_intersection_info.js index ab317b4..db37c4d 100644 --- a/o3d/samples/o3d-webgl/ray_intersection_info.js +++ b/o3d/samples/o3d-webgl/ray_intersection_info.js @@ -36,6 +36,7 @@ * @constructor */ o3d.RayIntersectionInfo = function() { + o3d.NamedObject.call(this); o3d.RayIntersectionInfo.prototype.position = [0, 0, 0]; }; o3d.inherit('RayIntersectionInfo', 'NamedObject'); diff --git a/o3d/samples/o3d-webgl/render_node.js b/o3d/samples/o3d-webgl/render_node.js index d97b2e9..fd82868 100644 --- a/o3d/samples/o3d-webgl/render_node.js +++ b/o3d/samples/o3d-webgl/render_node.js @@ -41,6 +41,7 @@ * @constructor */ o3d.RenderNode = function(opt_priority, opt_active) { + o3d.ParamObject.call(this); this.priority = opt_priority; this.children = []; }; @@ -77,8 +78,16 @@ o3d.RenderNode.prototype.parent = null; o3d.RenderNode.prototype.__defineSetter__('parent', function(p) { + if (this.parent_) { + this.parent_.removeChild(this); + } this.parent_ = p; - p.addChild(this); + if (this.parent_) { + if (!this.parent_.addChild) { + throw ('Parent of render node must be render node or null.'); + } + this.parent_.addChild(this); + } } ); @@ -97,6 +106,14 @@ o3d.RenderNode.prototype.addChild = function(child) { }; +/** + * Removes a child node. + * @param {o3d.RenderNode} child The child to add. + */ +o3d.RenderNode.prototype.removeChild = function(child) { + o3d.removeFromArray(this.children, child); +}; + /** * The immediate children of this RenderNode. @@ -146,7 +163,7 @@ o3d.RenderNode.prototype.getRenderNodesInTree = * will affect them. * * @param {string} name Rendernode name to look for. - * @returns {Array.<!o3d.RenderNode>} An array containing all nodes among + * @return {Array.<!o3d.RenderNode>} An array containing all nodes among * this node and its decendants that have the given name. */ o3d.RenderNode.prototype.getRenderNodesByNameInTree = @@ -164,7 +181,7 @@ o3d.RenderNode.prototype.getRenderNodesByNameInTree = * will affect them. * * @param {string} class_name class name to look for. - * @returns {Array.<!o3d.RenderNode>} An array containing all nodes among + * @return {Array.<!o3d.RenderNode>} An array containing all nodes among * this node and its decendants whose type is class_name. */ o3d.RenderNode.prototype.getRenderNodesByClassNameInTree = diff --git a/o3d/samples/o3d-webgl/render_surface.js b/o3d/samples/o3d-webgl/render_surface.js index d3b3877..8012bea 100644 --- a/o3d/samples/o3d-webgl/render_surface.js +++ b/o3d/samples/o3d-webgl/render_surface.js @@ -41,6 +41,9 @@ */ o3d.RenderSurfaceBase = function(width, height, texture) { o3d.ParamObject.call(this); + this.width = width; + this.height = height; + this.texture = texture; }; o3d.inherit('RenderSurfaceBase', 'ParamObject'); @@ -60,7 +63,6 @@ o3d.RenderSurfaceBase.prototype.width = 0; o3d.RenderSurfaceBase.prototype.height = 0; - /** * A RenderSurface encapsulates the notion of a renderable surface. * When used in conjunction with a RenderSurfaceSet node in the render graph, @@ -69,7 +71,9 @@ o3d.RenderSurfaceBase.prototype.height = 0; * only be accessed through the texture getRenderSurface(...) interfaces. * @constructor */ -o3d.RenderSurface = function() { }; +o3d.RenderSurface = function() { + o3d.RenderSurfaceBase.call(this); +}; o3d.inherit('RenderSurface', 'RenderSurfaceBase'); @@ -80,10 +84,61 @@ o3d.RenderSurface.prototype.texture = null; /** + * The mip level targeted by this render surface. + * @type {number} + */ +o3d.RenderSurface.level = 0; + + +/** + * The underlying GL framebuffer object. + * @type {WebGLFramebuffer} + * @private + */ +o3d.RenderSurfaceBase.prototype.framebuffer_ = null; + +/** + * Initializes a render surface to render to the given texture. + * @param {o3d.Texture2D} texture The texture. + */ +o3d.RenderSurface.prototype.initWithTexture = function(texture, level) { + this.framebuffer_ = this.gl.createFramebuffer(); + this.texture = texture; + this.level = level; + this.width = texture.width; + this.height = texture.height; +}; + +/** * A RenderDepthStencilSurface represents a depth stencil render surface. * @constructor */ -o3d.RenderDepthStencilSurface = function() { }; +o3d.RenderDepthStencilSurface = function() { + o3d.RenderSurfaceBase.call(this); +}; o3d.inherit('RenderDepthStencilSurface', 'RenderSurfaceBase'); +/** + * The GL renderbuffer object for the depth buffer. + * @type {WebGLRenderbuffer} + * @private + */ +o3d.RenderDepthStencilSurface.prototype.depth_stencil_buffer_ = null; + + +/** + * Allocates depth and stnecil buffers of the given size. + * @param {number} width + * @param {number} height + * @private + */ +o3d.RenderDepthStencilSurface.prototype.initWithSize_ = + function(width, height) { + this.depth_stencil_buffer_ = this.gl.createRenderbuffer(); + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.depth_stencil_buffer_); + this.gl.renderbufferStorage( + this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT16, width, height); + this.width = width; + this.height = height; +}; diff --git a/o3d/samples/o3d-webgl/render_surface_set.js b/o3d/samples/o3d-webgl/render_surface_set.js index dfcd901..6b4bbec 100644 --- a/o3d/samples/o3d-webgl/render_surface_set.js +++ b/o3d/samples/o3d-webgl/render_surface_set.js @@ -36,8 +36,8 @@ * from the given RenderSurfaceSet node will operate on the contents of * the bound depth and color buffers. * Note the following usage constraints of this node: - * 1) If both a color and depth surface is bound, then they must be of matching - * dimensions. + * 1) If both a color and depth surface is bound, then they must be of + * matching dimensions. * 2) At least one of render_surface and render_depth_surface must non-null. * * @param {o3d.RenderSurface} opt_renderSurface The render surface to set. @@ -45,7 +45,9 @@ * depth stencil render surface to set. * @constructor */ -o3d.RenderSurfaceSet = function() { +o3d.RenderSurfaceSet = + function(opt_renderSurface, opt_renderDepthStencilSurface) { + o3d.RenderNode.call(this); o3d.RenderSurfaceSet.prototype.renderSurface = opt_renderSurface; o3d.RenderSurfaceSet.prototype.renderDepthStencilSurface = @@ -62,7 +64,6 @@ o3d.inherit('RenderSurfaceSet', 'RenderNode'); o3d.RenderSurfaceSet.prototype.renderSurface = null; - /** * The render depth stencil surface to which the depth contents of all * RenderNode children should be drawn. @@ -70,5 +71,95 @@ o3d.RenderSurfaceSet.prototype.renderSurface = null; */ o3d.RenderSurfaceSet.prototype.renderDepthStencilSurface = null; +/** + * Helper function to set the framebuffer back to the default one. + * @private + */ +o3d.RenderSurfaceSet.prototype.clearFramebufferObjects_ = + function() { + this.gl.bindFramebuffer( + this.gl.FRAMEBUFFER, this.renderSurface.framebuffer_); + + this.gl.framebufferRenderbuffer( + this.gl.FRAMEBUFFER, + this.gl.COLOR_ATTACHMENT0, + this.gl.RENDERBUFFER, + 0); + + this.gl.framebufferRenderbuffer( + this.gl.FRAMEBUFFER, + this.gl.DEPTH_STENCIL_ATTACHMENT, + this.gl.RENDERBUFFER, + 0); + + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, 0); +}; + + +/** + * Helper function to set up both the color and depth-stencil targets. + * @private + */ +o3d.RenderSurfaceSet.prototype.installFramebufferObjects_ = + function() { + // Reset the bound attachments to the current framebuffer object. + this.clearFramebufferObjects_(); + + this.gl.bindFramebuffer( + this.gl.FRAMEBUFFER, this.renderSurface.framebuffer_); + + if (this.renderSurface) { + var texture = this.renderSurface.texture.texture_; + var level = this.renderSurface.level; + + // TODO(petersont): If it's a cube, this call should be different. + this.gl.bindTexture(this.gl.TEXTURE_2D, texture); + this.gl.framebufferTexture2D( + this.gl.FRAMEBUFFER, + this.gl.COLOR_ATTACHMENT0, + this.gl.TEXTURE_2D, + texture, + level); + } + + if (this.renderDepthStencilSurface) { + var depth_stencil_buffer = + this.renderDepthStencilSurface.depth_stencil_buffer_; + + // TODO(petersont): In the WebGL spec, there is a depth-stencil + // attachment, but it hasn't been implemented yet, once it is, + // this should use one of those. + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, depth_stencil_buffer); + this.gl.framebufferRenderbuffer( + this.gl.FRAMEBUFFER, + this.gl.DEPTH_ATTACHMENT, + this.gl.RENDERBUFFER, + depth_stencil_buffer); + } +}; + + +/** + * Called during the rendergraph traversal before the children are rendered. + * @private + */ +o3d.RenderSurfaceSet.prototype.before = function() { + this.installFramebufferObjects_(); + this.previousHeight = this.gl.displayInfo.height; + this.previousWidth = this.gl.displayInfo.width; + this.gl.displayInfo.height = this.renderSurface.height; + this.gl.displayInfo.width = this.renderSurface.width; +}; + + +/** + * Called during the rendergraph traversal after the children are rendered. + * @private + */ +o3d.RenderSurfaceSet.prototype.after = function() { + this.clearFramebufferObjects_(); + this.gl.displayInfo.height = this.previousHeight; + this.gl.displayInfo.width = this.previousWidth; +}; diff --git a/o3d/samples/o3d-webgl/sampler.js b/o3d/samples/o3d-webgl/sampler.js new file mode 100644 index 0000000..1748d83 --- /dev/null +++ b/o3d/samples/o3d-webgl/sampler.js @@ -0,0 +1,170 @@ +/* + * 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. + */ + + +/** + * Sampler is the base of all texture samplers. Texture samplers encapsulate + * a texture reference with a set of states that define how the texture + * gets applied to a surface. Sampler states are set either via Params defined + * on the Sampler object or directly via one the convenience methods defined + * on the Sampler. The following states are supported (default values are in + * parenthesis): + * \li 'addressModeU' (WRAP) + * \li 'addressModeV' (WRAP) + * \li 'addressModeW' (WRAP) + * \li 'magFilter' (LINEAR) + * \li 'minFilter' (LINEAR) + * \li 'mipFilter' (POINT) + * \li 'borderColor' ([0,0,0,0]) + * \li 'maxAnisotropy' (1) + */ +o3d.Sampler = function() { + o3d.ParamObject.call(this); + this.borderColor = [0, 0, 0, 0]; +}; +o3d.inherit('Sampler', 'ParamObject'); + + + +/** + * @type {number} + */ +o3d.Sampler.AddressMode = goog.typedef; + + +/** + * AddressMode, + * Controls what happens with texture coordinates outside the [0..1] range. + * WRAP + * MIRROR + * CLAMP + * BORDER + */ +o3d.Sampler.WRAP = 0; +o3d.Sampler.MIRROR = 1; +o3d.Sampler.CLAMP = 2; +o3d.Sampler.BORDER = 3; + + +/** + * @type {number} + */ +o3d.Sampler.FilterType = goog.typedef; + +/** + * FilterType, + * Texture filtering types. + * NONE + * POINT + * LINEAR + * ANISOTROPIC + */ +o3d.Sampler.NONE = 0; +o3d.Sampler.POINT = 1; +o3d.Sampler.LINEAR = 2; +o3d.Sampler.ANISOTROPIC = 3; + + +/** + * The texture address mode for the u coordinate. + * @type {!o3d.Sampler.AddressMode} + */ +o3d.Sampler.prototype.addressModeU = o3d.Sampler.WRAP; + + + +/** + * The texture address mode for the v coordinate. + * @type {!o3d.Sampler.AddressMode} + */ +o3d.Sampler.prototype.addressModeV = o3d.Sampler.WRAP; + + + +/** + * The texture address mode for the w coordinate. + * @type {!o3d.Sampler.AddressMode} + */ +o3d.Sampler.prototype.addressModeW = o3d.Sampler.WRAP; + + + +/** + * The magnification filter. Valid values for the mag filter are: POINT and + * @type {!o3d.Sampler.FilterType} + */ +o3d.Sampler.prototype.magFilter = o3d.Sampler.LINEAR; + + + +/** + * The minification filter. Valid values for the min filter are: POINT, LINEAR + * and ANISOTROPIC. + * @type {!o3d.Sampler.FilterType} + */ +o3d.Sampler.prototype.minFilter = o3d.Sampler.LINEAR; + + + +/** + * The mipmap filter used during minification. Valid values for the mip filter + * are: NONE, POINT and LINEAR. + * @type {!o3d.Sampler.FilterType} + */ +o3d.Sampler.prototype.mipFilter = o3d.Sampler.LINEAR; + + + +/** + * Color returned for texture coordinates outside the [0,1] range when the + * address mode is set to BORDER. + * @type {!Array.<number>} + */ +o3d.Sampler.prototype.borderColor = [0, 0, 0, 0]; + + + +/** + * Degree of anisotropy used when the ANISOTROPIC filter type is used. + * @type {number} + */ +o3d.Sampler.prototype.maxAnisotropy = 1; + + + +/** + * The Texture object used by this Sampler. + * @type {o3d.Texture} + */ +o3d.Sampler.prototype.texture = null; + + + diff --git a/o3d/samples/o3d-webgl/shape.js b/o3d/samples/o3d-webgl/shape.js index 4754869..ab47b2f 100644 --- a/o3d/samples/o3d-webgl/shape.js +++ b/o3d/samples/o3d-webgl/shape.js @@ -37,6 +37,7 @@ * @constructor */ o3d.Shape = function() { + o3d.ParamObject.call(this); this.elements = []; }; o3d.inherit('Shape', 'ParamObject'); @@ -72,7 +73,10 @@ o3d.Shape.prototype.elements = []; */ o3d.Shape.prototype.createDrawElements = function(pack, material) { - o3d.notImplemented(); + var elements = this.elements; + for (var i = 0; i < elements.length; ++i) { + elements[i].createDrawElement(pack, material); + } }; diff --git a/o3d/samples/o3d-webgl/state_set.js b/o3d/samples/o3d-webgl/state_set.js index 8fe1881..054b5a8 100644 --- a/o3d/samples/o3d-webgl/state_set.js +++ b/o3d/samples/o3d-webgl/state_set.js @@ -39,7 +39,7 @@ * @constructor */ o3d.StateSet = function(opt_state) { - o3d.ParamObject.call(this); + o3d.RenderNode.call(this); this.state = opt_state || null; }; o3d.inherit('StateSet', 'RenderNode'); diff --git a/o3d/samples/o3d-webgl/stream.js b/o3d/samples/o3d-webgl/stream.js index 0a3cf19..6a96aa1 100644 --- a/o3d/samples/o3d-webgl/stream.js +++ b/o3d/samples/o3d-webgl/stream.js @@ -35,6 +35,7 @@ * @constructor */ o3d.Stream = function(semantic, semantic_index, field, start_index) { + o3d.NamedObject.call(this); this.semantic = semantic; this.semanticIndex = semantic_index; this.field = field; diff --git a/o3d/samples/o3d-webgl/stream_bank.js b/o3d/samples/o3d-webgl/stream_bank.js index b6c032a..8329709 100644 --- a/o3d/samples/o3d-webgl/stream_bank.js +++ b/o3d/samples/o3d-webgl/stream_bank.js @@ -35,6 +35,7 @@ * @constructor */ o3d.StreamBank = function() { + o3d.NamedObject.call(this); this.vertexStreams = []; }; o3d.inherit('StreamBank', 'NamedObject'); @@ -53,7 +54,7 @@ o3d.StreamBank.prototype.vertexStreams = []; * @param {number} semantic_index Which index of a particular semantic to use. * @param {o3d.Field} field The field containing information for this stream. * @param {number} start_index The first element to use. - * @returns {boolean} True if successful. + * @return {boolean} True if successful. */ o3d.StreamBank.prototype.setVertexStream = function(semantic, semantic_index, field, start_index) { @@ -71,7 +72,7 @@ o3d.StreamBank.prototype.setVertexStream = * @param {o3d.Stream.Semantic} semantic The particular use of this stream. * @param {o3d.Stream.Semantic} semantic_index Which index of a particular * semantic to use. - * @returns {o3d.Stream} The found stream or null if it does not exist. + * @return {o3d.Stream} The found stream or null if it does not exist. */ o3d.StreamBank.prototype.getVertexStream = function(semantic, semantic_index) { @@ -87,7 +88,7 @@ o3d.StreamBank.prototype.getVertexStream = * @param {o3d.Stream.Semantic} semantic The particular use of this stream. * @param {o3d.Stream.Semantic} semantic_index Which index of a particular * semantic to use. - * @returns {boolean} true if the specified stream existed. + * @return {boolean} true if the specified stream existed. */ o3d.StreamBank.prototype.removeVertexStream = function(semantic, semantic_index) { diff --git a/o3d/samples/o3d-webgl/texture.js b/o3d/samples/o3d-webgl/texture.js index 767e608..7c5367b 100644 --- a/o3d/samples/o3d-webgl/texture.js +++ b/o3d/samples/o3d-webgl/texture.js @@ -105,7 +105,7 @@ o3d.Texture.prototype.format = o3d.Texture.UNKNOWN_FORMAT; /** * The number of mipmap levels used by the texture. - * type {number} + * @type {number} */ o3d.Texture.prototype.levels = 1; @@ -113,11 +113,18 @@ o3d.Texture.prototype.levels = 1; /** * True if all the alpha values in the texture are 1.0 - * type {boolean} + * @type {boolean} */ o3d.Texture.prototype.alphaIsOne = true; +/** + * The the associated gl texture. + * @type {WebGLTexture} + * @private + */ +o3d.Texture.prototype.texture_ = null; + /** * Generates Mips. @@ -126,7 +133,8 @@ o3d.Texture.prototype.alphaIsOne = true; */ o3d.Texture.prototype.generateMips = function(source_level, num_levels) { - o3d.notImplemented(); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture_); + this.gl.generateMipmap(this.gl.TEXTURE_2D); }; @@ -138,9 +146,16 @@ o3d.Texture.prototype.generateMips = * @param {number} opt_height The height of this texture in pixels. * @constructor */ -o3d.Texture2D = function() { +o3d.Texture2D = function(opt_width, opt_height) { + o3d.Texture.call(this); this.width = opt_width || 0; this.height = opt_height || 0; + + /** + * The cache of rendersurface objects. + * @private + */ + this.renderSurfaces_ = []; }; o3d.inherit('Texture2D', 'Texture'); @@ -160,18 +175,22 @@ o3d.Texture2D.prototype.width = 0; o3d.Texture2D.prototype.height = 0; - /** * Returns a RenderSurface object associated with a mip_level of a texture. * * @param {number} mip_level The mip-level of the surface to be returned. - * @param {number} opt_pack pack This parameter is no longer used. - * The surface exists as long as the texture it came from exists. - * @returns {o3d.RenderSurface} The RenderSurface object. + * @return {o3d.RenderSurface} The RenderSurface object. */ o3d.Texture2D.prototype.getRenderSurface = - function(mip_level, opt_pack) { - o3d.notImplemented(); + function(mip_level) { + if (!this.renderSurfaces_[mip_level]) { + var renderSurface = new o3d.RenderSurface(); + renderSurface.gl = this.gl; + renderSurface.initWithTexture(this, mip_level); + this.renderSurfaces_[mip_level] = renderSurface; + } + + return this.renderSurfaces_[mip_level]; }; @@ -244,7 +263,7 @@ o3d.Texture2D.prototype.setRect = * @param {number} y The y coordinate of the area in the texture to retrieve. * @param {number} width The width of the area to retrieve. * @param {number} height The height of the area to retrieve. - * @returns {number} Array of pixel values. + * @return {number} Array of pixel values. */ o3d.Texture2D.prototype.getRect = function(level, x, y, width, height) { @@ -260,7 +279,24 @@ o3d.Texture2D.prototype.getRect = */ o3d.Texture2D.prototype.setFromBitmap = function(bitmap) { - o3d.notImplemented(); + this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture_); + this.gl.texImage2D(this.gl.TEXTURE_2D, + 0, // Level. + bitmap.canvas_, + bitmap.defer_flip_verically_to_texture_); + + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.gl.TEXTURE_2D, + this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); + + if (bitmap.defer_mipmaps_to_texture_) { + this.generateMips(); + } }; @@ -301,7 +337,9 @@ o3d.Texture2D.prototype.drawImage = * @param {number} edgeLength The length of any edge of this texture * @constructor */ -o3d.TextureCUBE = function() { }; +o3d.TextureCUBE = function() { + o3d.Texture.call(this); +}; o3d.inherit('TextureCUBE', 'Texture'); @@ -346,7 +384,7 @@ o3d.TextureCUBE.prototype.edge_length = 0; * @param {o3d.Pack} pack This parameter is no longer used. The surface exists * as long as the texture it came from exists. * @param {number} mip_level The mip-level of the surface to be returned. - * @returns {o3d.RenderSurface} The RenderSurface object. + * @return {o3d.RenderSurface} The RenderSurface object. */ o3d.TextureCUBE.prototype.getRenderSurface = function(face, mip_level, opt_pack) { @@ -410,7 +448,7 @@ o3d.TextureCUBE.prototype.setRect = * @param {number} y The y coordinate of the area in the texture to retrieve. * @param {number} width The width of the area to retrieve. * @param {number} height The height of the area to retrieve. - * @returns {number} Array of pixel values. + * @return {number} Array of pixel values. */ o3d.TextureCUBE.prototype.getRect = function(face, level, x, y, width, height) { diff --git a/o3d/samples/o3d-webgl/transform.js b/o3d/samples/o3d-webgl/transform.js index 2ef5d21..ff72076 100644 --- a/o3d/samples/o3d-webgl/transform.js +++ b/o3d/samples/o3d-webgl/transform.js @@ -51,6 +51,7 @@ o3d.Transform = function(opt_localMatrix, opt_worldMatrix, opt_visible, opt_boundingBox, opt_cull) { + o3d.ParamObject.call(this); this.localMatrix = opt_localMatrix || [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; this.worldMatrix = opt_worldMatrix || @@ -149,7 +150,7 @@ o3d.Transform.prototype.getTransformsInTree = * will affect them. * * @param {string} name Transform name to look for. - * @returns {Array.<o3d.Transform>} An array containing the transforms of the + * @return {Array.<o3d.Transform>} An array containing the transforms of the * under and including this transform matching the given name. */ o3d.Transform.prototype.getTransformsByNameInTree = @@ -182,12 +183,12 @@ o3d.Transform.prototype.addShape = /** * Removes a shape from this transform. * @param {o3d.Shape} shape Shape to remove. - * @returns {boolean} true if successful, false if shape was not in + * @return {boolean} true if successful, false if shape was not in * this transform. */ o3d.Transform.prototype.removeShape = function(shape) { - + o3d.notImplemented(); }; @@ -211,6 +212,7 @@ o3d.Transform.prototype.shapes = []; + /** * Walks the tree of transforms starting with this transform and creates * draw elements. If an Element already has a DrawElement that uses material a @@ -353,7 +355,7 @@ o3d.Transform.compose = function(a, b, opt_target) { * * @param {!Array.<!Array.<number>>} a A matrix. * @param {!Array.<!Array.<number>>} b Another matrix. - * @returns {boolean} Whether they are equal. + * @return {boolean} Whether they are equal. */ o3d.Transform.matricesEqual = function(a, b) { if (a==b) { @@ -416,7 +418,7 @@ o3d.Transform.transpose = function(m, opt_target) { * inverse of a. */ o3d.Transform.inverse = function(m, opt_target) { - var t = opt_target || a; + var t = opt_target || m; var m0 = m[0]; var m1 = m[1]; var m2 = m[2]; @@ -897,7 +899,7 @@ o3d.Transform.prototype.scale = * Utility function to flatten an o3djs-style matrix * (which is an array of arrays) into one array of entries. * @param {Array.<Array.<number> >} m The o3djs-style matrix. - * @returns {Array.<number>} The flattened matrix. + * @return {Array.<number>} The flattened matrix. */ o3d.Transform.flattenMatrix4 = function(m) { var m0 = m[0]; diff --git a/o3d/samples/o3d-webgl/tree_traversal.js b/o3d/samples/o3d-webgl/tree_traversal.js index 5a5dbb6..c92f601 100644 --- a/o3d/samples/o3d-webgl/tree_traversal.js +++ b/o3d/samples/o3d-webgl/tree_traversal.js @@ -42,8 +42,9 @@ * @constructor */ o3d.TreeTraversal = function(opt_transform) { + o3d.RenderNode.call(this); this.transform = opt_transform; - this.drawLists = []; + this.drawLists_ = []; this.drawListsToReset_ = []; }; o3d.inherit('TreeTraversal', 'RenderNode'); @@ -90,7 +91,7 @@ o3d.TreeTraversal.prototype.registerDrawList = /** * Unregisters a DrawList with this TreeTraversal. * @param {o3d.DrawList} draw_list DrawList to unregister. - * @returns {boolean} true if unregistered. false if this draw_list was + * @return {boolean} true if unregistered. false if this draw_list was * not registered. */ o3d.TreeTraversal.prototype.unregisterDrawList = diff --git a/o3d/samples/o3d-webgl/viewport.js b/o3d/samples/o3d-webgl/viewport.js index 6c082c0..70e1386 100644 --- a/o3d/samples/o3d-webgl/viewport.js +++ b/o3d/samples/o3d-webgl/viewport.js @@ -44,6 +44,7 @@ * @constructor */ o3d.Viewport = function(opt_viewport, opt_depthRange) { + o3d.RenderNode.call(this); this.viewport = opt_viewport || [0.0, 0.0, 1.0, 1.0]; this.depthRange = opt_depthRange || [0.0, 1.0]; }; @@ -66,7 +67,6 @@ o3d.inherit('Viewport', 'RenderNode'); o3d.Viewport.prototype.viewport = [0.0, 0.0, 1.0, 1.0]; - /** * The min Z and max Z depth range in [min Z, max Z] format. * Default = [0.0, 1.0]. @@ -81,16 +81,17 @@ o3d.Viewport.prototype.depthRange = [0.0, 1.0]; * scissor region in gl. */ o3d.Viewport.prototype.before = function() { - var x = this.viewport[0] * this.gl.canvas.width; - var y = this.viewport[1] * this.gl.canvas.height; - var width = this.viewport[2] * this.gl.canvas.width; - var height = this.viewport[3] * this.gl.canvas.height; + var x = this.viewport[0] * this.gl.displayInfo.width; + var y = this.viewport[1] * this.gl.displayInfo.height; + var width = this.viewport[2] * this.gl.displayInfo.width; + var height = this.viewport[3] * this.gl.displayInfo.height; + this.gl.viewport(x, y, width, height); if (x != 0 || y != 0 || this.viewport[2] != 1 || this.viewport[3] != 1) { this.gl.enable(this.gl.SCISSOR_TEST); this.gl.scissor(x, y, width, height); } else { - + this.gl.disable(this.gl.SCISSOR_TEST); } }; diff --git a/o3d/samples/o3djs/webgl.js b/o3d/samples/o3djs/webgl.js index 6940660..1bbe168 100644 --- a/o3d/samples/o3djs/webgl.js +++ b/o3d/samples/o3djs/webgl.js @@ -82,31 +82,70 @@ o3djs.webgl.makeClients = function(callback, /** + * Adds a wrapper object to single gl function context that checks for errors + * before the call. + * @param {WebGLContext} context + * @param {string} fname The name of the function. + * @return {} + */ +o3djs.webgl.createGLErrorWrapper = function(context, fname) { + return function() { + var rv = context[fname].apply(context, arguments); + var err = context.getError(); + if (err != 0) + throw "GL error " + err + " in " + fname; + return rv; + }; +}; + +/** + * Adds a wrapper object to a webgl context that checks for errors + * before each function call. + */ +o3djs.webgl.addDebuggingWrapper = function(context) { + // Thanks to Ilmari Heikkinen for the idea on how to implement this + // so elegantly. + var wrap = {}; + for (var i in context) { + if (typeof context[i] == 'function') { + wrap[i] = createGLErrorWrapper(context, i); + } else { + wrap[i] = context[i]; + } + } + wrap.getError = function() { + return context.getError(); + }; + return wrap; +}; + + +/** * Creates a canvas under the given parent element and an o3d.Client * under that. + * + * @ param {!Element} element The element under which to insert the client. + * @ param {string} opt_features Features to turn on. + * @ param {boolean} opt_debug Whether gl debugging features should be + * enabled. */ -o3djs.webgl.createClient = function(element, opt_features) { +o3djs.webgl.createClient = function(element, opt_features, opt_debug) { opt_features = opt_features || ''; + opt_debug = opt_debug || false; - // TODO(petersont): Not sure what to do with the features object. var canvas; canvas = document.createElement('canvas'); canvas.setAttribute('width', element.getAttribute('width')); canvas.setAttribute('height', element.getAttribute('height')); + canvas.client = new o3d.Client; + canvas.client.initWithCanvas(canvas); canvas.o3d = o3d; - var gl; - try {gl = canvas.getContext("experimental-webgl") } catch(e) { } - if (!gl) - try {gl = canvas.getContext("moz-webgl") } catch(e) { } - if (!gl) { - alert("No WebGL context found"); - return null; + if (opt_debug) { + client.gl = o3djs.webgl.addDebuggingWrapper(client.gl); } - canvas.client.gl = gl; - element.appendChild(canvas); return canvas; }; diff --git a/o3d/samples/primitives.html b/o3d/samples/primitives.html index e61cdae..27763d1 100644 --- a/o3d/samples/primitives.html +++ b/o3d/samples/primitives.html @@ -155,7 +155,7 @@ function createShapes() { createMaterial([1,0,0,1]), 1.0, // Radius of the sphere. 30, // Number of meridians. - 20); // Number of parallels. + 20); // Number of parallels. var cylinder = o3djs.primitives.createCylinder( g_pack, |