summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3djs/gpu2d.js
diff options
context:
space:
mode:
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;
+};
+