summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3djs/gpu2d.js
diff options
context:
space:
mode:
authorkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-26 00:26:00 +0000
committerkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-26 00:26:00 +0000
commit00d721c144d35882dfaf24b3869e95a8d6404ee5 (patch)
tree07243b1cb15a7fb0eddf54fce686c96cda594640 /o3d/samples/o3djs/gpu2d.js
parent0a46d19ce396112b8a42005d1369de30df3bc872 (diff)
downloadchromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.zip
chromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.tar.gz
chromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.tar.bz2
Added the bulk of the algorithm for GPU accelerated 2D vector curve
rendering from "Rendering Vector Art on the GPU" by Loop and Blinn, GPU Gems 3, Chapter 25. The main entry point to the algorithm is the PathProcessor, which takes in a Skia path and converts it to two triangle meshes: one for the exterior region of the shape containing the curve segments, and one for the interior region of the shape which is filled with constant (1.0) alpha. The o3d.ProcessedPath class is the internal object which exposes the needed entry points to JavaScript. However, o3djs.gpu2d is the user-level entry point to the algorithm. This exposes a Path primitive to which line, quadratic curve and cubic curve segments can be added, and simple fills (currently only a solid color). An SVG loader in samples/gpu2d/svgloader.js illustrates how content might be imported at run time. Several samples and regression tests demonstrate the current state of the implementation. More work is planned. Some small generalizations to the O3D code were necessary to support two-dimensional vertices. Note that I plan to submit gpu2d.js and/or svgloader.js for JavaScript readability. I have run both through the JS compiler and have fixed as many of the doc generation errors as possible in svgloader.js without pulling this file into the o3djs namespace. Tested in O3D on Windows and Mac OS X. BUG=none TEST=various SVG based tests Review URL: http://codereview.chromium.org/652016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40079 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples/o3djs/gpu2d.js')
-rw-r--r--o3d/samples/o3djs/gpu2d.js680
1 files changed, 680 insertions, 0 deletions
diff --git a/o3d/samples/o3djs/gpu2d.js b/o3d/samples/o3djs/gpu2d.js
new file mode 100644
index 0000000..4e19322
--- /dev/null
+++ b/o3d/samples/o3djs/gpu2d.js
@@ -0,0 +1,680 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file provides GPU-accelerated rendering of 2D
+ * vector graphics in 3D.
+ */
+o3djs.provide('o3djs.gpu2d');
+
+/**
+ * A module providing GPU-accelerated rendering of 2D vector graphics
+ * in 3D.
+ * @namespace
+ */
+o3djs.gpu2d = o3djs.gpu2d || {};
+
+/**
+ * Creates a new Path, which holds one or more closed contours
+ * composed of 2D primitives like lines, quadratic curves, and cubic
+ * curves.
+ * @param {!o3d.Pack} pack Pack in which geometry and materials
+ * associated with the curves will be created.
+ * @param {!o3d.DrawList} drawList The DrawList on which the triangle
+ * mesh will be drawn. Typically this will be the
+ * zOrderedDrawList from an o3djs.rendergraph.ViewInfo.
+ * @return {!o3djs.gpu2d.Path} The created Path.
+ */
+o3djs.gpu2d.createPath = function(pack,
+ drawList) {
+ return new o3djs.gpu2d.Path(pack, drawList);
+};
+
+/**
+ * Constructs a new Path. Do not call this directly; use
+ * o3djs.gpu2d.createPath instead.
+ * @param {!o3d.Pack} pack Pack in which geometry and materials
+ * associated with the curves will be created.
+ * @param {!o3d.DrawList} drawList The DrawList on which the triangle
+ * mesh will be drawn. Typically this will be the
+ * zOrderedDrawList.
+ * @constructor
+ */
+o3djs.gpu2d.Path = function(pack, drawList) {
+ /**
+ * Pack in which curves' geometry and materials are created.
+ * @type {!o3d.Pack}
+ * @private
+ */
+ this.pack_ = pack;
+
+ /**
+ * DrawList in which curves' geometry and materials will be
+ * rendered.
+ * @type {!o3d.DrawList}
+ * @private
+ */
+ this.drawList_ = drawList;
+
+ /**
+ * Internal object which manages the triangle mesh associated with
+ * the curves.
+ * @type {!o3d.ProcessedPath}
+ * @private
+ */
+ this.path_ = pack.createObject('ProcessedPath');
+
+ // Set up the Primitives in the ProcessedPath.
+ //
+ // The mesh is separated into two different regions. The exterior
+ // region of the mesh is the portion containing the cubic curve
+ // segments. It is this region whose alpha value is computed using
+ // Loop and Blinn's shader. The interior region of the mesh is
+ // simply filled with a constant alpha. The reason for the split is
+ // that it is difficult to assign texture coordinates to cause Loop
+ // and Blinn's shader to fill a region with constant alpha. While
+ // there is some cost associated with switching shaders and
+ // performing two draw calls, doing so simplifies the logic.
+
+ // Create state objects so we can turn on alpha blending for the
+ // exterior triangles. We also disable backface culling so that we
+ // can view the vector shapes from both sides.
+ var exteriorState = pack.createObject('State');
+ exteriorState.getStateParam('o3d.AlphaBlendEnable').value = true;
+ exteriorState.getStateParam('o3d.SourceBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA;
+ exteriorState.getStateParam('o3d.DestinationBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA;
+ exteriorState.getStateParam('o3d.CullMode').value =
+ o3djs.base.o3d.State.CULL_NONE;
+
+ var interiorState = pack.createObject('State');
+ interiorState.getStateParam('o3d.CullMode').value =
+ o3djs.base.o3d.State.CULL_NONE;
+
+ // Create the materials for the exterior and interior regions.
+
+ /**
+ * The material for the exterior triangles, filled with Loop and
+ * Blinn's shader.
+ * @type {!o3d.Material}
+ * @private
+ */
+ this.exteriorMaterial_ = pack.createObject('Material');
+ this.exteriorMaterial_.name = 'ExteriorMaterial';
+ this.exteriorMaterial_.state = exteriorState;
+ this.exteriorMaterial_.drawList = drawList;
+
+ /**
+ * The material for the interior triangles, filled with a solid
+ * shader.
+ * @type {!o3d.Material}
+ * @private
+ */
+ this.interiorMaterial_ = pack.createObject('Material');
+ this.interiorMaterial_.name = 'InteriorMaterial';
+ this.interiorMaterial_.state = interiorState;
+ this.interiorMaterial_.drawList = drawList;
+
+ /**
+ * The Shape which is the transform graph's view of the Path.
+ * @type {!o3d.Shape}
+ */
+ this.shape = pack.createObject('Shape');
+
+ // Create the exterior region.
+ var primitive = pack.createObject('Primitive');
+ var streamBank = pack.createObject('StreamBank');
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ // The coordinates of the triangles are 2D
+ var vertices = vertexBuffer.createField('FloatField', 2);
+ /**
+ * The Field for the exterior vertices.
+ * @type {!o3d.FloatField}
+ * @private
+ */
+ this.exteriorVertices_ = vertices;
+ // The (Loop/Blinn) texture coordinates are 3D
+ var texcoords = vertexBuffer.createField('FloatField', 3);
+ /**
+ * The Field for the exterior texture coordinates.
+ * @type {!o3d.FloatField}
+ * @private
+ */
+ this.exteriorTexCoords_ = texcoords;
+ streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0);
+ streamBank.setVertexStream(o3djs.base.o3d.Stream.TEXCOORD, 0, texcoords, 0);
+ primitive.streamBank = streamBank;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST;
+ primitive.material = this.exteriorMaterial_;
+ primitive.owner = this.shape;
+ /**
+ * The Primitive for the exterior triangles.
+ * @type {!o3d.Primitive}
+ * @private
+ */
+ this.exteriorTriangles_ = primitive;
+
+ // Create the interior region.
+ primitive = pack.createObject('Primitive');
+ streamBank = pack.createObject('StreamBank');
+ vertexBuffer = pack.createObject('VertexBuffer');
+ // The coordinates of the triangles are 2D
+ vertices = vertexBuffer.createField('FloatField', 2);
+ /**
+ * The Field for the interior vertices.
+ * @type {!o3d.FloatField}
+ * @private
+ */
+ this.interiorVertices_ = vertices;
+ streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0);
+ primitive.streamBank = streamBank;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST;
+ primitive.material = this.interiorMaterial_;
+ primitive.owner = this.shape;
+ /**
+ * The Primitive for the interior triangles.
+ * @type {!o3d.Primitive}
+ * @private
+ */
+ this.interiorTriangles_ = primitive;
+
+ // Initialize the fill to a solid color.
+ this.setFill(o3djs.gpu2d.createColor(pack, 0.0, 0.0, 0.0, 1.0));
+
+ // Create draw elements for the shape.
+ this.shape.createDrawElements(pack, null);
+};
+
+/**
+ * Clears out any previously added segments or generated triangles
+ * from this Path.
+ */
+o3djs.gpu2d.Path.prototype.clear = function() {
+ this.path_.clear();
+};
+
+/**
+ * Moves the pen to the given absolute X,Y coordinates. If a contour
+ * isn't currently open on this path, one is opened.
+ * @param {number} x the x coordinate to move to.
+ * @param {number} y the y coordinate to move to.
+ */
+o3djs.gpu2d.Path.prototype.moveTo = function(x, y) {
+ this.path_.moveTo(x, y);
+};
+
+/**
+ * Draws a line from the current coordinates to the given absolute
+ * X,Y coordinates.
+ * @param {number} x the x coordinate to draw a line to.
+ * @param {number} y the y coordinate to draw a line to.
+ */
+o3djs.gpu2d.Path.prototype.lineTo = function(x, y) {
+ this.path_.lineTo(x, y);
+};
+
+/**
+ * Draws a quadratic curve from the current coordinates through the
+ * given control point and end point, specified in absolute
+ * coordinates.
+ * @param {number} cx the x coordinate of the quadratic's control point
+ * @param {number} cy the y coordinate of the quadratic's control point
+ * @param {number} x the x coordinate of the quadratic's end point
+ * @param {number} y the y coordinate of the quadratic's end point
+ */
+o3djs.gpu2d.Path.prototype.quadraticTo = function(cx, cy, x, y) {
+ this.path_.quadraticTo(cx, cy, x, y);
+};
+
+/**
+ * Draws a cubic curve from the current coordinates through the
+ * given control points and end point, specified in absolute
+ * coordinates.
+ * @param {number} c0x the x coordinate of the cubic's first control point
+ * @param {number} c0y the y coordinate of the cubic's first control point
+ * @param {number} c1x the x coordinate of the cubic's second control point
+ * @param {number} c1y the y coordinate of the cubic's second control point
+ * @param {number} x the x coordinate of the cubic's end point
+ * @param {number} y the y coordinate of the cubic's end point
+ */
+o3djs.gpu2d.Path.prototype.cubicTo = function(c0x, c0y, c1x, c1y, x, y) {
+ this.path_.cubicTo(c0x, c0y, c1x, c1y, x, y);
+};
+
+/**
+ * Closes the current contour on this Path.
+ */
+o3djs.gpu2d.Path.prototype.close = function() {
+ this.path_.close();
+};
+
+/**
+ * Updates the triangle mesh associated with this Path. Call this
+ * after adding any new segments to the Path.
+ */
+o3djs.gpu2d.Path.prototype.update = function() {
+ this.path_.createMesh(this.exteriorVertices_,
+ this.exteriorTexCoords_,
+ this.interiorVertices_);
+ var numVertices = this.exteriorVertices_.buffer.numElements;
+ if (numVertices == 1) {
+ this.exteriorTriangles_.numberVertices = 0;
+ this.exteriorTriangles_.numberPrimitives = 0;
+ } else {
+ this.exteriorTriangles_.numberVertices = numVertices;
+ this.exteriorTriangles_.numberPrimitives = numVertices / 3;
+ }
+ numVertices = this.interiorVertices_.buffer.numElements;
+ if (numVertices == 1) {
+ this.interiorTriangles_.numberVertices = 0;
+ this.interiorTriangles_.numberPrimitives = 0;
+ } else {
+ this.interiorTriangles_.numberVertices = numVertices;
+ this.interiorTriangles_.numberPrimitives = numVertices / 3;
+ }
+};
+
+//----------------------------------------------------------------------
+// Fills
+
+/**
+ * Sets the fill for this Path.
+ * @param {!o3djs.gpu2d.Fill} fill the fill for this Path.
+ */
+o3djs.gpu2d.Path.prototype.setFill = function(fill) {
+ if (this.fill_) {
+ this.fill_.detach_(this);
+ }
+ this.interiorMaterial_.effect = fill.interiorEffect;
+ this.exteriorMaterial_.effect = fill.exteriorEffect;
+ this.fill_ = fill;
+ fill.attach_(this);
+};
+
+/**
+ * Base class for all Fills. Do not call this directly; use, for
+ * example, o3djs.gpu2d.createColor instead.
+ * @param {!o3d.Pack} pack the Pack in which to create materials.
+ * @constructor
+ */
+o3djs.gpu2d.Fill = function(pack) {
+ this.pack_ = pack;
+ this.attachedPaths_ = [];
+};
+
+/**
+ * Attaches this Fill to the given path.
+ * @param {!o3djs.gpu2d.Path} path Path to attach the fill to.
+ * @private
+ */
+o3djs.gpu2d.Fill.prototype.attach_ = function(path) {
+ if (this.attachedPaths_.indexOf(path) < 0)
+ this.attachedPaths_.push(path);
+ this.apply_(path);
+};
+
+/**
+ * Detaches this Fill from the given path.
+ * @param {!o3djs.gpu2d.Path} path Path to detach the fill from.
+ * @private
+ */
+o3djs.gpu2d.Fill.prototype.detach_ = function(path) {
+ var idx = this.attachedPaths_.indexOf(path);
+ if (idx >= 0)
+ this.attachedPaths_.splice(idx, idx);
+};
+
+/**
+ * Applies this Fill to all attached paths.
+ * @private
+ */
+o3djs.gpu2d.Fill.prototype.applyToPaths_ = function() {
+ for (var i = 0; i < this.attachedPaths_.length; i++) {
+ this.apply_(this.attachedPaths_[i]);
+ }
+};
+
+/**
+ * Base "apply" operation for fills -- a no-op.
+ * @private
+ */
+o3djs.gpu2d.Fill.prototype.apply_ = function(path) {
+};
+
+/**
+ * A class for a solid color fill. Do not call this directly; use
+ * o3djs.gpu2d.createColor instead.
+ * @param {!o3d.Pack} pack the Pack in which to create materials.
+ * @constructor
+ * @extends {o3djs.gpu2d.Fill}
+ */
+o3djs.gpu2d.Color = function(pack) {
+ o3djs.gpu2d.Fill.call(this, pack);
+ this.interiorEffect =
+ o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, true);
+ this.exteriorEffect =
+ o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, false);
+ this.r_ = 0.0;
+ this.g_ = 0.0;
+ this.b_ = 0.0;
+ this.a_ = 1.0;
+};
+
+o3djs.base.inherit(o3djs.gpu2d.Color,
+ o3djs.gpu2d.Fill);
+
+/**
+ * Sets the color of this fill.
+ * @param {number} r Red component (0.0 - 1.0).
+ * @param {number} g Green component (0.0 - 1.0).
+ * @param {number} b Blue component (0.0 - 1.0).
+ * @param {number} a Alpha component (0.0 - 1.0).
+ */
+o3djs.gpu2d.Color.prototype.set = function(r, g, b, a) {
+ this.r_ = r;
+ this.g_ = g;
+ this.b_ = b;
+ this.a_ = a;
+ this.applyToPaths_();
+};
+
+/**
+ * Gets the value of the Color fill as an array.
+ * @return {!o3d.Float4}
+ */
+o3djs.gpu2d.Color.prototype.get = function() {
+ return [this.r_, this.g_, this.b_, this.a_];
+};
+
+/**
+ * Applies this color to the given path.
+ * @param {!o3djs.gpu2d.Path} path to apply the fill to.
+ * @private
+ */
+o3djs.gpu2d.Color.prototype.apply_ = function(path) {
+ this.applyToMaterial_(path.interiorMaterial_);
+ this.applyToMaterial_(path.exteriorMaterial_);
+};
+
+/**
+ * Applies this color to the given material
+ * @param {!o3d.Material} material to apply the fill to.
+ * @private
+ */
+o3djs.gpu2d.Color.prototype.applyToMaterial_ = function(material) {
+ var paramName = 'color';
+ var paramType = 'ParamFloat4';
+ var param = material.getParam(paramName);
+ if (!param) {
+ param = material.createParam(paramName, paramType);
+ }
+ param.set(this.r_, this.g_, this.b_, this.a_);
+};
+
+/**
+ * Creates a solid color fill.
+ * @param {!o3d.Pack} pack the Pack in which to create materials.
+ * @param {number} red Red component (0.0 - 1.0).
+ * @param {number} green Green component (0.0 - 1.0).
+ * @param {number} blue Blue component (0.0 - 1.0).
+ * @param {number} alpha Alpha component (0.0 - 1.0).
+ * @return {!o3djs.gpu2d.Color} The created Color.
+ */
+o3djs.gpu2d.createColor = function(pack, red, green, blue, alpha) {
+ var result = new o3djs.gpu2d.Color(pack);
+ result.set(red, green, blue, alpha);
+ return result;
+};
+
+//----------------------------------------------------------------------
+// Shaders and effects
+
+// TODO(kbr): antialiasing is not supported yet because the ddx
+// and ddy instructions are not part of the shader model 2.0. On
+// Windows we could easily upgrade to ps2.0a, but on Mac and Linux
+// there isn't an easy upgrade path from ARBVP1.0 and ARBFP1.0 which
+// incorporates these instructions.
+//
+// The solution within O3D is to compute the gradients using the
+// closed-form solution in Loop and Blinn's SIGGRAPH '05 paper. This
+// requires computation of the Psi matrix per vertex. In GLSL this is
+// not necessary; derivative instructions are always available there.
+
+/**
+ * Generates the source for the shader used on the exterior triangles
+ * of the shape -- the ones that evaluate the curve function.
+ * @param {boolean} antialias whether to enable antialiasing.
+ * @param {string} fillUniforms the uniforms for the fill.
+ * @param {string} fillSource the source code snippet for the fill.
+ * @return {string}
+ * @private
+ */
+o3djs.gpu2d.generateLoopBlinnShaderSource_ = function(antialias,
+ fillUniforms,
+ fillSource) {
+ var result = '' +
+ 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
+ fillUniforms +
+ '\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float2 position : POSITION;\n' +
+ ' float3 klm : TEXCOORD0;\n' +
+ '};\n' +
+ '\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float3 klm : TEXCOORD0;\n' +
+ '};\n' +
+ '\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ '\n' +
+ ' output.position = mul(float4(input.position, 0, 1),\n' +
+ ' worldViewProjection);\n' +
+ ' output.klm = input.klm;\n' +
+ ' return output;\n' +
+ '}\n' +
+ '\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' +
+ ' float3 klm = input.klm;\n';
+ var alphaComputation;
+ if (antialias) {
+ alphaComputation = '' +
+ ' // Gradients\n' +
+ ' float3 px = ddx(input.klm);\n' +
+ ' float3 py = ddy(input.klm);\n' +
+ '\n' +
+ ' // Chain rule\n' +
+ ' float k2 = klm.x * klm.x;\n' +
+ ' float c = k2 * klm.x - klm.y * klm.z;\n' +
+ ' float k23 = 3.0 * k2;\n' +
+ ' float cx = k23 * px.x - klm.z * px.y - klm.y * px.z;\n' +
+ ' float cy = k23 * py.x - klm.z * py.y - klm.y * py.z;\n' +
+ '\n' +
+ ' // Signed distance\n' +
+ ' float sd = c / sqrt(cx * cx + cy * cy);\n' +
+ '\n' +
+ ' // Linear alpha\n' +
+ ' float alpha = clamp(0.5 - sd, 0.0, 1.0);\n';
+ } else {
+ alphaComputation = '' +
+ ' float t = klm.x * klm.x * klm.x - klm.y * klm.z;\n' +
+ ' float alpha = clamp(sign(t), 0.0, 1.0);\n';
+ }
+
+ return result + alphaComputation +
+ '\n' +
+ fillSource +
+ '}\n' +
+ '\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+};
+
+/**
+ * Generates the source for the shader used on the interior triangles
+ * of the shape.
+ * @param {string} fillUniforms the uniforms for the fill.
+ * @param {string} fillSource the source code snippet for the fill.
+ * @return {string}
+ * @private
+ */
+o3djs.gpu2d.generateSolidShaderSource_ = function(fillUniforms, fillSource) {
+ var result = '' +
+ 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
+ fillUniforms +
+ '\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float2 position : POSITION;\n' +
+ '};\n' +
+ '\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ '};\n' +
+ '\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ '\n' +
+ ' output.position = mul(float4(input.position, 0, 1),\n' +
+ ' worldViewProjection);\n' +
+ ' return output;\n' +
+ '}\n' +
+ '\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' +
+ ' float alpha = 1.0;\n' +
+ fillSource +
+ '}\n' +
+ '\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+ return result;
+};
+
+/**
+ * Enum for the types of fills.
+ * @enum
+ * @private
+ */
+o3djs.gpu2d.FillTypes_ = {
+ COLOR: 0
+};
+
+/**
+ * Shader code for the various fills, indexed by FillTypes_.
+ * @type {!Array.<{uniforms: string, source: string}>}
+ * @private
+ */
+o3djs.gpu2d.FILL_CODE_ = [
+ { uniforms:
+ 'uniform float4 color;\n',
+ source:
+ 'return float4(color.r, color.g, color.b, color.a * alpha);\n'
+ }
+];
+
+/**
+ * Cache of effects indexed by pack's client ID. Each entry is an
+ * array indexed by fill type.
+ * @type {!Array.<!Array.<!o3d.Effect>>}
+ * @private
+ */
+o3djs.gpu2d.interiorEffectCache_ = [];
+
+/**
+ * Cache of effects indexed by pack's client ID. Each entry is an
+ * array indexed by fill type.
+ * @type {!Array.<!Array.<!o3d.Effect>>}
+ * @private
+ */
+o3djs.gpu2d.exteriorEffectCache_ = [];
+
+/**
+ * Loads a fill effect for a Path.
+ * @param {!o3d.Pack} pack the Pack in which to create materials.
+ * @param {o3djs.gpu2d.FillTypes_} fillType the fill type to create.
+ * @param {boolean} interior whether this effect is filling the solid
+ * interior portion of the shape or the exterior region containing
+ * the curves.
+ * @return {!o3d.Effect}
+ * @private
+ */
+o3djs.gpu2d.loadEffect_ = function(pack, fillType, interior) {
+ var effectCache;
+ if (interior) {
+ effectCache = o3djs.gpu2d.interiorEffectCache_;
+ } else {
+ effectCache = o3djs.gpu2d.exteriorEffectCache_;
+ }
+ var effectList = o3djs.gpu2d.getEffectList_(pack, effectCache);
+ var effect = effectList[fillType];
+ if (!effect) {
+ effect = pack.createObject('Effect');
+ var result = false;
+ var sourceSnippets = o3djs.gpu2d.FILL_CODE_[fillType];
+ if (interior) {
+ result = effect.loadFromFXString(
+ o3djs.gpu2d.generateSolidShaderSource_(sourceSnippets.uniforms,
+ sourceSnippets.source));
+ } else {
+ result = effect.loadFromFXString(
+ o3djs.gpu2d.generateLoopBlinnShaderSource_(false,
+ sourceSnippets.uniforms,
+ sourceSnippets.source));
+ }
+ if (!result) {
+ alert('Error loading shader: interior = ' + interior);
+ }
+ effectList[fillType] = effect;
+ }
+ return effect;
+};
+
+/**
+ * Fetches and/or creates the effect list for a given pack from the
+ * passed effect cache.
+ * @param {!o3d.Pack} pack the Pack in which to create materials.
+ * @param {!Array.<!Array.<!o3d.Effect>>} effectCache the effect cache.
+ * @return {!Array.<o3d.Effect>}
+ * @private
+ */
+o3djs.gpu2d.getEffectList_ = function(pack, effectCache) {
+ var list = effectCache[pack.clientId];
+ if (!list) {
+ list = [];
+ effectCache[pack.clientId] = list;
+ }
+ return list;
+};
+