/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @fileoverview This file contains a basic utility library that simplifies the * creation of simple 2D Canvas surfaces for the purposes of drawing 2D elements * in O3D. * * Example * *
* <html><body> * <script type="text/javascript" src="o3djs/all.js"> * </script> * <script> * window.onload = init; * * function init() { * o3djs.base.makeClients(initStep2); * } * * function initStep2(clientElements) { * var clientElement = clientElements[0]; * var client = clientElement.client; * var pack = client.createPack(); * var viewInfo = o3djs.rendergraph.createBasicView( * pack, * client.root, * client.renderGraphRoot); * * // Create an instance of the canvas utility library. * var canvasLib = o3djs.canvas.create( * pack, client.root, g_viewInfo); * * // Create a 700x500 rectangle at (x,y,z) = (4, 10, 0) * var canvasQuad = canvasLib.createXYQuad(4, 10, 0, 700, 500, false); * * // Draw into the canvas. * canvasQuad.canvas.clear([1, 0, 0, 1]); * canvasQuad.canvas.drawText('Hello', 0, 10, canvasPaint); * ... * ... * * // Update the o3d texture associated with the canvas. * canvasQuad.updateTexture(); * } * </script> * <div id="o3d" style="width: 600px; height: 600px"></div> * </body></html> ** */ o3djs.provide('o3djs.canvas'); o3djs.require('o3djs.effect'); o3djs.require('o3djs.primitives'); /** * A Module for using a 2d canvas. * @namespace */ o3djs.canvas = o3djs.canvas || {}; /** * Creates an o3djs.canvas library object through which CanvasQuad objects * can be created. * @param {!o3d.Pack} pack to manage objects created by this library. * @param {!o3d.Transform} root Default root for visual objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo A ViewInfo object as * created by o3djs.createView which contains draw lists that the created * quads will be placed into. * @return {!o3djs.canvas.CanvasInfo} A CanvasInfo object containing * references to all the common O3D elements used by this instance * of the library. */ o3djs.canvas.create = function(pack, root, viewInfo) { return new o3djs.canvas.CanvasInfo(pack, root, viewInfo); }; /** * The shader code used by the canvas quads. It only does two things: * 1. Transforms the shape to screen space via the worldViewProjection matrix. * 2. Performs a texture lookup to display the contents of the texture * bound to texSampler0. * @type {string} */ o3djs.canvas.FX_STRING_CG = 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + 'sampler texSampler0;\n' + 'struct VertexShaderInput {\n' + ' float4 position : POSITION;\n' + ' float2 texcoord : TEXCOORD0;\n' + '};\n'+ 'struct PixelShaderInput {\n' + ' float4 position : POSITION;\n' + ' float2 texcoord : TEXCOORD0;\n' + '};\n' + 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + ' PixelShaderInput output;\n' + ' output.position = mul(input.position, worldViewProjection);\n' + ' output.texcoord = input.texcoord;\n' + ' return output;\n' + '}\n' + 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' + ' return tex2D(texSampler0, input.texcoord);\n' + '}\n' + '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + '// #o3d MatrixLoadOrder RowMajor\n'; // Auto-generated by convert.py. o3djs.canvas.FX_STRING_GLSL = '// glslv profile log:\n' + '// 22 lines, 0 errors.\n' + '\n' + '// glslf profile log:\n' + '// 22 lines, 0 errors.\n' + '\n' + '// glslv output by Cg compiler\n' + '// cgc version 2.0.0010, build date Dec 12 2007\n' + '// command line args: -profile glslv\n' + '//vendor NVIDIA Corporation\n' + '//version 2.0.0.10\n' + '//profile glslv\n' + '//program vertexShaderFunction\n' + '//semantic worldViewProjection : WORLDVIEWPROJECTION\n' + '//semantic texSampler0\n' + '//var float4x4 worldViewProjection : WORLDVIEWPROJECTION : _ZZ2SworldViewProjection[0], 4 : -1 : 1\n' + '//var sampler texSampler0 : : : -1 : 0\n' + '//var float4 input.position : $vin.POSITION : POSITION : 0 : 1\n' + '//var float2 input.texcoord : $vin.TEXCOORD0 : TEXCOORD0 : 0 : 1\n' + '//var float4 vertexShaderFunction.position : $vout.POSITION : POSITION : -1 : 1\n' + '//var float2 vertexShaderFunction.texcoord : $vout.TEXCOORD0 : TEXCOORD0 : -1 : 1\n' + '\n' + 'attribute vec4 position;\n' + 'attribute vec4 texcoord0;\n' + 'vec4 _glPositionTemp;\n' + 'uniform vec4 dx_clipping;\n' + '\n' + 'struct VertexShaderInput {\n' + ' vec4 position;\n' + ' vec2 texcoord;\n' + '};\n' + '\n' + 'struct PixelShaderInput {\n' + ' vec4 position;\n' + ' vec2 texcoord;\n' + '};\n' + '\n' + 'PixelShaderInput _ZZ3Sret_0;\n' + 'vec4 _ZZ3SrZh0003;\n' + 'uniform mat4 worldviewprojection;\n' + '\n' + ' // main procedure, the original name was vertexShaderFunction\n' + 'void main()\n' + '{\n' + '\n' + '\n' + ' _ZZ3SrZh0003 = position.x*worldviewprojection[0];\n' + ' _ZZ3SrZh0003 = _ZZ3SrZh0003 + position.y*worldviewprojection[1];\n' + ' _ZZ3SrZh0003 = _ZZ3SrZh0003 + position.z*worldviewprojection[2];\n' + ' _ZZ3SrZh0003 = _ZZ3SrZh0003 + position.w*worldviewprojection[3];\n' + ' _ZZ3Sret_0.position = _ZZ3SrZh0003;\n' + ' _ZZ3Sret_0.texcoord = texcoord0.xy;\n' + ' gl_TexCoord[0].xy = texcoord0.xy;\n' + ' _glPositionTemp = _ZZ3SrZh0003; gl_Position = vec4(_glPositionTemp.x + _glPositionTemp.w * dx_clipping.x, dx_clipping.w * (_glPositionTemp.y + _glPositionTemp.w * dx_clipping.y), _glPositionTemp.z * 2 - _glPositionTemp.w, _glPositionTemp.w);\n' + ' return;\n' + '} // main end\n' + '\n' + '\n' + '// #o3d SplitMarker\n' + '// #o3d MatrixLoadOrder RowMajor\n' + '\n' + '// glslf output by Cg compiler\n' + '// cgc version 2.0.0010, build date Dec 12 2007\n' + '// command line args: -profile glslf\n' + '//vendor NVIDIA Corporation\n' + '//version 2.0.0.10\n' + '//profile glslf\n' + '//program pixelShaderFunction\n' + '//semantic worldViewProjection : WORLDVIEWPROJECTION\n' + '//semantic texSampler0\n' + '//var float4x4 worldViewProjection : WORLDVIEWPROJECTION : , 4 : -1 : 0\n' + '//var sampler texSampler0 : : _ZZ2StexSampler0 : -1 : 1\n' + '//var float2 input.texcoord : $vin.TEXCOORD0 : TEXCOORD0 : 0 : 1\n' + '//var float4 pixelShaderFunction : $vout.COLOR : COLOR : -1 : 1\n' + '\n' + '\n' + '\n' + 'struct VertexShaderInput {\n' + ' vec2 texcoord;\n' + '};\n' + '\n' + 'struct PixelShaderInput {\n' + ' vec2 texcoord;\n' + '};\n' + '\n' + 'vec4 _ZZ3Sret_0;\n' + 'sampler2D _ZZ3SsZh0003;\n' + 'uniform sampler texSampler0;\n' + '\n' + ' // main procedure, the original name was pixelShaderFunction\n' + 'void main()\n' + '{\n' + '\n' + '\n' + ' _ZZ3SsZh0003 = sampler2D(texSampler0);\n' + ' _ZZ3Sret_0 = texture2D(_ZZ3SsZh0003, gl_TexCoord[0].xy);\n' + ' gl_FragColor = _ZZ3Sret_0;\n' + ' return;\n' + '} // main end\n' + '\n'; /** * Sets the shader language used. Passing 'glsl' will cause all generated * shader code to be in glsl. Passing anything else will result in the * default o3d hlsl/cg based shader language. * @param {string} language Shader language to use. */ o3djs.canvas.setLanguage = function(language) { o3djs.canvas.FX_STRING = o3djs.canvas.FX_STRING_CG; if (language == 'glsl') o3djs.canvas.FX_STRING = o3djs.canvas.FX_STRING_GLSL; } /** * The CanvasInfo object creates and keeps references to the O3D objects * that are shared between all CanvasQuad objects created through it. * @constructor * @param {!o3d.Pack} pack Pack to manage CanvasInfo objects. * @param {!o3d.Transform} root Default root for visual objects. * @param {!o3djs.rendergraph.ViewInfo} viewInfo A ViewInfo object as * created by o3djs.createView which contains draw lists that the * created quads will be placed into. */ o3djs.canvas.CanvasInfo = function(pack, root, viewInfo) { /** * The pack being used to manage objects created by this CanvasInfo. * @type {!o3d.Pack} */ this.pack = pack; /** * The ViewInfo this CanvasInfo uses for rendering. * @type {!o3djs.rendergraph.ViewInfo} */ this.viewInfo = viewInfo; /** * The default root for objects created by this CanvasInfo. * @type {!o3d.Transform} */ this.root = root; /** * The Effect object shared by all CanvasQuad instances. * @type {!o3d.Effect} */ this.effect_ = this.pack.createObject('Effect'); this.effect_.loadFromFXString(o3djs.canvas.FX_STRING); /** * Material for canvases with transparent content * @type {!o3d.Material} */ this.transparentMaterial_ = this.pack.createObject('Material'); /** * Material for canvases with opaque content. * @type {!o3d.Material} */ this.opaqueMaterial_ = this.pack.createObject('Material'); this.transparentMaterial_.effect = this.effect_; this.opaqueMaterial_.effect = this.effect_; this.transparentMaterial_.drawList = viewInfo.zOrderedDrawList; this.opaqueMaterial_.drawList = viewInfo.performanceDrawList; /** * State object to handle the transparency blending mode * for transparent canvas quads. * The canvas bitmap already multiplies the color values by alpha. In order * to avoid a black halo around text drawn on a transparent background we * need to set the blending mode as follows. * @type {!o3d.State} */ this.transparentState_ = this.pack.createObject('State'); this.transparentState_.getStateParam('AlphaBlendEnable').value = true; this.transparentState_.getStateParam('SourceBlendFunction').value = o3djs.base.o3d.State.BLENDFUNC_ONE; this.transparentState_.getStateParam('DestinationBlendFunction').value = o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA; this.transparentMaterial_.state = this.transparentState_; // Create 2d plane shapes. createPlane makes an XZ plane by default // so we pass in matrix to rotate it to an XY plane. We could do // all our manipulations in XZ but most people seem to like XY for 2D. /** * A shape for transparent quads. * @type {!o3d.Shape} */ this.transparentQuadShape = o3djs.primitives.createPlane( this.pack, this.transparentMaterial_, 1, 1, 1, 1, [[1, 0, 0, 0], [0, 0, 1, 0], [0, -1, 0, 0], [0, 0, 0, 1]]); /** * A shape for opaque quads. * @type {!o3d.Shape} */ this.opaqueQuadShape = o3djs.primitives.createPlane( this.pack, this.opaqueMaterial_, 1, 1, 1, 1, [[1, 0, 0, 0], [0, 0, 1, 0], [0, -1, 0, 0], [0, 0, 0, 1]]); }; /** * The CanvasQuad object encapsulates a Transform, a rectangle Shape, * an effect that applies a texture to render the quad, and a matching Canvas * object that can render into the texture. The dimensions of the texture and * the canvas object match those of the quad in order to get pixel-accurate * results with the appropriate orthographic projection. * The resulting rectangle Shape is positioned at the origin. It can be moved * around by setting the localMatrix on the Transform object referenced to by * the canvasQuad.transform property. * The Canvas associated with the returned CanvasQuad object can be retrieved * from the object's 'canvas' property. After issuing any draw commands on the * Canvas, you need to call the updateTexture() method on the CanvasQuad to * update the contents of the quad surface. * @constructor * @param {!o3djs.canvas.CanvasInfo} canvasInfo The CanvasInfo object * instance creating this CanvasQuad. * @param {number} width The width of the quad. * @param {number} height The height of the quad. * @param {boolean} transparent Set to true if the canvas will * be transparent so that the appropriate blending modes are set. * @param {!o3d.Transform} opt_parent parent transform to parent * the newly created quad under. If no parent transform is provided then * the quad gets parented under the CanvasInfo's root. */ o3djs.canvas.CanvasQuad = function(canvasInfo, width, height, transparent, opt_parent) { /** * The CanvasInfo managing this CanvasQuad * @type {!o3djs.canvas.CanvasInfo} */ this.canvasInfo = canvasInfo; var parentTransform = opt_parent || canvasInfo.root; // create a transform for positioning /** * A transform for this quad. * @type {!o3d.Transform} */ this.transform = canvasInfo.pack.createObject('Transform'); this.transform.parent = parentTransform; // create a transform for scaling to the size of the image just so // we don't have to manage that manually in the transform above. /** * A scale transform for this quad. * You can change the scale the quad without effecting its positon using * this transform. * @type {!o3d.Transform} */ this.scaleTransform = canvasInfo.pack.createObject('Transform'); this.scaleTransform.parent = this.transform; /** * The texture the canvas will draw on. * @type {!o3d.Texture2D} */ this.texture = /** @type {!o3d.Texture2D} */ (canvasInfo.pack.createTexture2D( width, height, o3djs.base.o3d.Texture.ARGB8, 1, // mipmap levels false)); // Create a Canvas object to go with the quad. /** * The Canvas object used to draw on this quad. * @type {!o3d.Canvas} */ this.canvas = canvasInfo.pack.createObject('Canvas'); this.canvas.setSize(width, height); /** * The sampler for the texture. * @type {!o3d.Sampler} */ this.sampler = canvasInfo.pack.createObject('Sampler'); this.sampler.addressModeU = o3djs.base.o3d.Sampler.CLAMP; this.sampler.addressModeV = o3djs.base.o3d.Sampler.CLAMP; /** * The param sampler for this transform. * @private * @type {!o3d.ParamSampler} */ this.paramSampler_ = this.scaleTransform.createParam('texSampler0', 'ParamSampler'); this.paramSampler_.value = this.sampler; this.sampler.texture = this.texture; if (transparent) { this.scaleTransform.addShape(canvasInfo.transparentQuadShape); } else { this.scaleTransform.addShape(canvasInfo.opaqueQuadShape); } this.scaleTransform.translate(width / 2, height / 2, 0); this.scaleTransform.scale(width, -height, 1); }; /** * Copies the current contents of the Canvas object to the texture associated * with the quad. This method should be called after any new draw calls have * been issued to the CanvasQuad's Canvas object. */ o3djs.canvas.CanvasQuad.prototype.updateTexture = function() { var width = this.texture.width; var height = this.texture.height; this.texture.drawImage(this.canvas, 0, height - 1, width, -height, 0, 0, 0, width, height); }; /** * Creates a CanvasQuad object on the XY plane at the specified position. * @param {number} topX The x coordinate of the top left corner of the quad. * @param {number} topY The y coordinate of the top left corner of the quad. * @param {number} z The z coordinate of the quad. z values are negative * numbers, the smaller the number the further back the quad will be. * @param {number} width The width of the quad. * @param {number} height The height of the quad. * @param {boolean} transparent Set to true if the canvas bitmap uses * transparency so that the appropriate blending modes are set. * @param {!o3d.Transform} opt_parent parent transform to parent the newly * created quad under. If no parent transform is provided then the quad * gets parented under the CanvasInfo's root. * @return {!o3djs.canvas.CanvasQuad} The newly created CanvasQuad object. */ o3djs.canvas.CanvasInfo.prototype.createXYQuad = function(topX, topY, z, width, height, transparent, opt_parent) { var canvasQuad = new o3djs.canvas.CanvasQuad(this, width, height, transparent, opt_parent); canvasQuad.transform.translate(topX, topY, z); return canvasQuad; }; /** * Creates a CanvasQuad object of the given size. The resulting rectangle Shape * is centered at the origin. It can be moved around by setting the * localMatrix on the Transform object referenced to by the canvasQuad.transform * property. * @param {number} width The width of the quad. * @param {number} height The height of the quad. * @param {boolean} transparent Set to true if the canvas bitmap uses * transparency so that the appropriate blending modes are set. * @param {!o3d.Transform} opt_parent parent transform to parent the newly * created quad under. If no parent transform is provided then the quad * gets parented under the CanvasInfo's root. * @return {!o3djs.canvas.CanvasQuad} The newly created CanvasQuad object. */ o3djs.canvas.CanvasInfo.prototype.createQuad = function(width, height, transparent, opt_parent) { return new o3djs.canvas.CanvasQuad(this, width, height, transparent, opt_parent); }; // For compatability with o3d code, the default language is o3d shading // language. o3djs.canvas.setLanguage('o3d');