/* * 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 a 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 =
'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';
/**
* 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;
// Create the Effect object shared by all CanvasQuad instances.
this.effect = this.pack.createObject('Effect');
this.effect.loadFromFXString(o3djs.canvas.FX_STRING);
// Create two materials: One used for canvases with transparent content
// and one for opaque canvases.
this.transparentMaterial = this.pack.createObject('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;
// Create a 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.
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) {
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;
// create the texture the canvas will draw on.
this.texture = 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);
// setup the sampler for the texture
this.sampler = canvasInfo.pack.createObject('Sampler');
this.sampler.addressModeU = o3djs.base.o3d.Sampler.CLAMP;
this.sampler.addressModeV = o3djs.base.o3d.Sampler.CLAMP;
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() {
this.canvas.copyToTexture(this.texture);
};
/**
* 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 {CanvasQuad} The newly created CanvasQuad object.
*/
o3djs.canvas.CanvasInfo.prototype.createQuad = function(width,
height,
transparent,
opt_parent) {
return new o3djs.canvas.CanvasQuad(width,
height,
transparent,
opt_parent);
};