OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2010, Google Inc. |
| 3 * All rights reserved. |
| 4 * |
| 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are |
| 7 * met: |
| 8 * |
| 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. |
| 11 * * Redistributions in binary form must reproduce the above |
| 12 * copyright notice, this list of conditions and the following disclaimer |
| 13 * in the documentation and/or other materials provided with the |
| 14 * distribution. |
| 15 * * Neither the name of Google Inc. nor the names of its |
| 16 * contributors may be used to endorse or promote products derived from |
| 17 * this software without specific prior written permission. |
| 18 * |
| 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ |
| 31 |
| 32 |
| 33 /** |
| 34 * @fileoverview This file provides GPU-accelerated rendering of 2D |
| 35 * vector graphics in 3D. |
| 36 */ |
| 37 o3djs.provide('o3djs.gpu2d'); |
| 38 |
| 39 /** |
| 40 * A module providing GPU-accelerated rendering of 2D vector graphics |
| 41 * in 3D. |
| 42 * @namespace |
| 43 */ |
| 44 o3djs.gpu2d = o3djs.gpu2d || {}; |
| 45 |
| 46 /** |
| 47 * Creates a new Path, which holds one or more closed contours |
| 48 * composed of 2D primitives like lines, quadratic curves, and cubic |
| 49 * curves. |
| 50 * @param {!o3d.Pack} pack Pack in which geometry and materials |
| 51 * associated with the curves will be created. |
| 52 * @param {!o3d.DrawList} drawList The DrawList on which the triangle |
| 53 * mesh will be drawn. Typically this will be the |
| 54 * zOrderedDrawList from an o3djs.rendergraph.ViewInfo. |
| 55 * @return {!o3djs.gpu2d.Path} The created Path. |
| 56 */ |
| 57 o3djs.gpu2d.createPath = function(pack, |
| 58 drawList) { |
| 59 return new o3djs.gpu2d.Path(pack, drawList); |
| 60 }; |
| 61 |
| 62 /** |
| 63 * Constructs a new Path. Do not call this directly; use |
| 64 * o3djs.gpu2d.createPath instead. |
| 65 * @param {!o3d.Pack} pack Pack in which geometry and materials |
| 66 * associated with the curves will be created. |
| 67 * @param {!o3d.DrawList} drawList The DrawList on which the triangle |
| 68 * mesh will be drawn. Typically this will be the |
| 69 * zOrderedDrawList. |
| 70 * @constructor |
| 71 */ |
| 72 o3djs.gpu2d.Path = function(pack, drawList) { |
| 73 /** |
| 74 * Pack in which curves' geometry and materials are created. |
| 75 * @type {!o3d.Pack} |
| 76 * @private |
| 77 */ |
| 78 this.pack_ = pack; |
| 79 |
| 80 /** |
| 81 * DrawList in which curves' geometry and materials will be |
| 82 * rendered. |
| 83 * @type {!o3d.DrawList} |
| 84 * @private |
| 85 */ |
| 86 this.drawList_ = drawList; |
| 87 |
| 88 /** |
| 89 * Internal object which manages the triangle mesh associated with |
| 90 * the curves. |
| 91 * @type {!o3d.ProcessedPath} |
| 92 * @private |
| 93 */ |
| 94 this.path_ = pack.createObject('ProcessedPath'); |
| 95 |
| 96 // Set up the Primitives in the ProcessedPath. |
| 97 // |
| 98 // The mesh is separated into two different regions. The exterior |
| 99 // region of the mesh is the portion containing the cubic curve |
| 100 // segments. It is this region whose alpha value is computed using |
| 101 // Loop and Blinn's shader. The interior region of the mesh is |
| 102 // simply filled with a constant alpha. The reason for the split is |
| 103 // that it is difficult to assign texture coordinates to cause Loop |
| 104 // and Blinn's shader to fill a region with constant alpha. While |
| 105 // there is some cost associated with switching shaders and |
| 106 // performing two draw calls, doing so simplifies the logic. |
| 107 |
| 108 // Create state objects so we can turn on alpha blending for the |
| 109 // exterior triangles. We also disable backface culling so that we |
| 110 // can view the vector shapes from both sides. |
| 111 var exteriorState = pack.createObject('State'); |
| 112 exteriorState.getStateParam('o3d.AlphaBlendEnable').value = true; |
| 113 exteriorState.getStateParam('o3d.SourceBlendFunction').value = |
| 114 o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA; |
| 115 exteriorState.getStateParam('o3d.DestinationBlendFunction').value = |
| 116 o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA; |
| 117 exteriorState.getStateParam('o3d.CullMode').value = |
| 118 o3djs.base.o3d.State.CULL_NONE; |
| 119 |
| 120 var interiorState = pack.createObject('State'); |
| 121 interiorState.getStateParam('o3d.CullMode').value = |
| 122 o3djs.base.o3d.State.CULL_NONE; |
| 123 |
| 124 // Create the materials for the exterior and interior regions. |
| 125 |
| 126 /** |
| 127 * The material for the exterior triangles, filled with Loop and |
| 128 * Blinn's shader. |
| 129 * @type {!o3d.Material} |
| 130 * @private |
| 131 */ |
| 132 this.exteriorMaterial_ = pack.createObject('Material'); |
| 133 this.exteriorMaterial_.name = 'ExteriorMaterial'; |
| 134 this.exteriorMaterial_.state = exteriorState; |
| 135 this.exteriorMaterial_.drawList = drawList; |
| 136 |
| 137 /** |
| 138 * The material for the interior triangles, filled with a solid |
| 139 * shader. |
| 140 * @type {!o3d.Material} |
| 141 * @private |
| 142 */ |
| 143 this.interiorMaterial_ = pack.createObject('Material'); |
| 144 this.interiorMaterial_.name = 'InteriorMaterial'; |
| 145 this.interiorMaterial_.state = interiorState; |
| 146 this.interiorMaterial_.drawList = drawList; |
| 147 |
| 148 /** |
| 149 * The Shape which is the transform graph's view of the Path. |
| 150 * @type {!o3d.Shape} |
| 151 */ |
| 152 this.shape = pack.createObject('Shape'); |
| 153 |
| 154 // Create the exterior region. |
| 155 var primitive = pack.createObject('Primitive'); |
| 156 var streamBank = pack.createObject('StreamBank'); |
| 157 var vertexBuffer = pack.createObject('VertexBuffer'); |
| 158 // The coordinates of the triangles are 2D |
| 159 var vertices = vertexBuffer.createField('FloatField', 2); |
| 160 /** |
| 161 * The Field for the exterior vertices. |
| 162 * @type {!o3d.FloatField} |
| 163 * @private |
| 164 */ |
| 165 this.exteriorVertices_ = vertices; |
| 166 // The (Loop/Blinn) texture coordinates are 3D |
| 167 var texcoords = vertexBuffer.createField('FloatField', 3); |
| 168 /** |
| 169 * The Field for the exterior texture coordinates. |
| 170 * @type {!o3d.FloatField} |
| 171 * @private |
| 172 */ |
| 173 this.exteriorTexCoords_ = texcoords; |
| 174 streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0); |
| 175 streamBank.setVertexStream(o3djs.base.o3d.Stream.TEXCOORD, 0, texcoords, 0); |
| 176 primitive.streamBank = streamBank; |
| 177 primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST; |
| 178 primitive.material = this.exteriorMaterial_; |
| 179 primitive.owner = this.shape; |
| 180 /** |
| 181 * The Primitive for the exterior triangles. |
| 182 * @type {!o3d.Primitive} |
| 183 * @private |
| 184 */ |
| 185 this.exteriorTriangles_ = primitive; |
| 186 |
| 187 // Create the interior region. |
| 188 primitive = pack.createObject('Primitive'); |
| 189 streamBank = pack.createObject('StreamBank'); |
| 190 vertexBuffer = pack.createObject('VertexBuffer'); |
| 191 // The coordinates of the triangles are 2D |
| 192 vertices = vertexBuffer.createField('FloatField', 2); |
| 193 /** |
| 194 * The Field for the interior vertices. |
| 195 * @type {!o3d.FloatField} |
| 196 * @private |
| 197 */ |
| 198 this.interiorVertices_ = vertices; |
| 199 streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0); |
| 200 primitive.streamBank = streamBank; |
| 201 primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST; |
| 202 primitive.material = this.interiorMaterial_; |
| 203 primitive.owner = this.shape; |
| 204 /** |
| 205 * The Primitive for the interior triangles. |
| 206 * @type {!o3d.Primitive} |
| 207 * @private |
| 208 */ |
| 209 this.interiorTriangles_ = primitive; |
| 210 |
| 211 // Initialize the fill to a solid color. |
| 212 this.setFill(o3djs.gpu2d.createColor(pack, 0.0, 0.0, 0.0, 1.0)); |
| 213 |
| 214 // Create draw elements for the shape. |
| 215 this.shape.createDrawElements(pack, null); |
| 216 }; |
| 217 |
| 218 /** |
| 219 * Clears out any previously added segments or generated triangles |
| 220 * from this Path. |
| 221 */ |
| 222 o3djs.gpu2d.Path.prototype.clear = function() { |
| 223 this.path_.clear(); |
| 224 }; |
| 225 |
| 226 /** |
| 227 * Moves the pen to the given absolute X,Y coordinates. If a contour |
| 228 * isn't currently open on this path, one is opened. |
| 229 * @param {number} x the x coordinate to move to. |
| 230 * @param {number} y the y coordinate to move to. |
| 231 */ |
| 232 o3djs.gpu2d.Path.prototype.moveTo = function(x, y) { |
| 233 this.path_.moveTo(x, y); |
| 234 }; |
| 235 |
| 236 /** |
| 237 * Draws a line from the current coordinates to the given absolute |
| 238 * X,Y coordinates. |
| 239 * @param {number} x the x coordinate to draw a line to. |
| 240 * @param {number} y the y coordinate to draw a line to. |
| 241 */ |
| 242 o3djs.gpu2d.Path.prototype.lineTo = function(x, y) { |
| 243 this.path_.lineTo(x, y); |
| 244 }; |
| 245 |
| 246 /** |
| 247 * Draws a quadratic curve from the current coordinates through the |
| 248 * given control point and end point, specified in absolute |
| 249 * coordinates. |
| 250 * @param {number} cx the x coordinate of the quadratic's control point |
| 251 * @param {number} cy the y coordinate of the quadratic's control point |
| 252 * @param {number} x the x coordinate of the quadratic's end point |
| 253 * @param {number} y the y coordinate of the quadratic's end point |
| 254 */ |
| 255 o3djs.gpu2d.Path.prototype.quadraticTo = function(cx, cy, x, y) { |
| 256 this.path_.quadraticTo(cx, cy, x, y); |
| 257 }; |
| 258 |
| 259 /** |
| 260 * Draws a cubic curve from the current coordinates through the |
| 261 * given control points and end point, specified in absolute |
| 262 * coordinates. |
| 263 * @param {number} c0x the x coordinate of the cubic's first control point |
| 264 * @param {number} c0y the y coordinate of the cubic's first control point |
| 265 * @param {number} c1x the x coordinate of the cubic's second control point |
| 266 * @param {number} c1y the y coordinate of the cubic's second control point |
| 267 * @param {number} x the x coordinate of the cubic's end point |
| 268 * @param {number} y the y coordinate of the cubic's end point |
| 269 */ |
| 270 o3djs.gpu2d.Path.prototype.cubicTo = function(c0x, c0y, c1x, c1y, x, y) { |
| 271 this.path_.cubicTo(c0x, c0y, c1x, c1y, x, y); |
| 272 }; |
| 273 |
| 274 /** |
| 275 * Closes the current contour on this Path. |
| 276 */ |
| 277 o3djs.gpu2d.Path.prototype.close = function() { |
| 278 this.path_.close(); |
| 279 }; |
| 280 |
| 281 /** |
| 282 * Updates the triangle mesh associated with this Path. Call this |
| 283 * after adding any new segments to the Path. |
| 284 */ |
| 285 o3djs.gpu2d.Path.prototype.update = function() { |
| 286 this.path_.createMesh(this.exteriorVertices_, |
| 287 this.exteriorTexCoords_, |
| 288 this.interiorVertices_); |
| 289 var numVertices = this.exteriorVertices_.buffer.numElements; |
| 290 if (numVertices == 1) { |
| 291 this.exteriorTriangles_.numberVertices = 0; |
| 292 this.exteriorTriangles_.numberPrimitives = 0; |
| 293 } else { |
| 294 this.exteriorTriangles_.numberVertices = numVertices; |
| 295 this.exteriorTriangles_.numberPrimitives = numVertices / 3; |
| 296 } |
| 297 numVertices = this.interiorVertices_.buffer.numElements; |
| 298 if (numVertices == 1) { |
| 299 this.interiorTriangles_.numberVertices = 0; |
| 300 this.interiorTriangles_.numberPrimitives = 0; |
| 301 } else { |
| 302 this.interiorTriangles_.numberVertices = numVertices; |
| 303 this.interiorTriangles_.numberPrimitives = numVertices / 3; |
| 304 } |
| 305 }; |
| 306 |
| 307 //---------------------------------------------------------------------- |
| 308 // Fills |
| 309 |
| 310 /** |
| 311 * Sets the fill for this Path. |
| 312 * @param {!o3djs.gpu2d.Fill} fill the fill for this Path. |
| 313 */ |
| 314 o3djs.gpu2d.Path.prototype.setFill = function(fill) { |
| 315 if (this.fill_) { |
| 316 this.fill_.detach_(this); |
| 317 } |
| 318 this.interiorMaterial_.effect = fill.interiorEffect; |
| 319 this.exteriorMaterial_.effect = fill.exteriorEffect; |
| 320 this.fill_ = fill; |
| 321 fill.attach_(this); |
| 322 }; |
| 323 |
| 324 /** |
| 325 * Base class for all Fills. Do not call this directly; use, for |
| 326 * example, o3djs.gpu2d.createColor instead. |
| 327 * @param {!o3d.Pack} pack the Pack in which to create materials. |
| 328 * @constructor |
| 329 */ |
| 330 o3djs.gpu2d.Fill = function(pack) { |
| 331 this.pack_ = pack; |
| 332 this.attachedPaths_ = []; |
| 333 }; |
| 334 |
| 335 /** |
| 336 * Attaches this Fill to the given path. |
| 337 * @param {!o3djs.gpu2d.Path} path Path to attach the fill to. |
| 338 * @private |
| 339 */ |
| 340 o3djs.gpu2d.Fill.prototype.attach_ = function(path) { |
| 341 if (this.attachedPaths_.indexOf(path) < 0) |
| 342 this.attachedPaths_.push(path); |
| 343 this.apply_(path); |
| 344 }; |
| 345 |
| 346 /** |
| 347 * Detaches this Fill from the given path. |
| 348 * @param {!o3djs.gpu2d.Path} path Path to detach the fill from. |
| 349 * @private |
| 350 */ |
| 351 o3djs.gpu2d.Fill.prototype.detach_ = function(path) { |
| 352 var idx = this.attachedPaths_.indexOf(path); |
| 353 if (idx >= 0) |
| 354 this.attachedPaths_.splice(idx, idx); |
| 355 }; |
| 356 |
| 357 /** |
| 358 * Applies this Fill to all attached paths. |
| 359 * @private |
| 360 */ |
| 361 o3djs.gpu2d.Fill.prototype.applyToPaths_ = function() { |
| 362 for (var i = 0; i < this.attachedPaths_.length; i++) { |
| 363 this.apply_(this.attachedPaths_[i]); |
| 364 } |
| 365 }; |
| 366 |
| 367 /** |
| 368 * Base "apply" operation for fills -- a no-op. |
| 369 * @private |
| 370 */ |
| 371 o3djs.gpu2d.Fill.prototype.apply_ = function(path) { |
| 372 }; |
| 373 |
| 374 /** |
| 375 * A class for a solid color fill. Do not call this directly; use |
| 376 * o3djs.gpu2d.createColor instead. |
| 377 * @param {!o3d.Pack} pack the Pack in which to create materials. |
| 378 * @constructor |
| 379 * @extends {o3djs.gpu2d.Fill} |
| 380 */ |
| 381 o3djs.gpu2d.Color = function(pack) { |
| 382 o3djs.gpu2d.Fill.call(this, pack); |
| 383 this.interiorEffect = |
| 384 o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, true); |
| 385 this.exteriorEffect = |
| 386 o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, false); |
| 387 this.r_ = 0.0; |
| 388 this.g_ = 0.0; |
| 389 this.b_ = 0.0; |
| 390 this.a_ = 1.0; |
| 391 }; |
| 392 |
| 393 o3djs.base.inherit(o3djs.gpu2d.Color, |
| 394 o3djs.gpu2d.Fill); |
| 395 |
| 396 /** |
| 397 * Sets the color of this fill. |
| 398 * @param {number} r Red component (0.0 - 1.0). |
| 399 * @param {number} g Green component (0.0 - 1.0). |
| 400 * @param {number} b Blue component (0.0 - 1.0). |
| 401 * @param {number} a Alpha component (0.0 - 1.0). |
| 402 */ |
| 403 o3djs.gpu2d.Color.prototype.set = function(r, g, b, a) { |
| 404 this.r_ = r; |
| 405 this.g_ = g; |
| 406 this.b_ = b; |
| 407 this.a_ = a; |
| 408 this.applyToPaths_(); |
| 409 }; |
| 410 |
| 411 /** |
| 412 * Gets the value of the Color fill as an array. |
| 413 * @return {!o3d.Float4} |
| 414 */ |
| 415 o3djs.gpu2d.Color.prototype.get = function() { |
| 416 return [this.r_, this.g_, this.b_, this.a_]; |
| 417 }; |
| 418 |
| 419 /** |
| 420 * Applies this color to the given path. |
| 421 * @param {!o3djs.gpu2d.Path} path to apply the fill to. |
| 422 * @private |
| 423 */ |
| 424 o3djs.gpu2d.Color.prototype.apply_ = function(path) { |
| 425 this.applyToMaterial_(path.interiorMaterial_); |
| 426 this.applyToMaterial_(path.exteriorMaterial_); |
| 427 }; |
| 428 |
| 429 /** |
| 430 * Applies this color to the given material |
| 431 * @param {!o3d.Material} material to apply the fill to. |
| 432 * @private |
| 433 */ |
| 434 o3djs.gpu2d.Color.prototype.applyToMaterial_ = function(material) { |
| 435 var paramName = 'color'; |
| 436 var paramType = 'ParamFloat4'; |
| 437 var param = material.getParam(paramName); |
| 438 if (!param) { |
| 439 param = material.createParam(paramName, paramType); |
| 440 } |
| 441 param.set(this.r_, this.g_, this.b_, this.a_); |
| 442 }; |
| 443 |
| 444 /** |
| 445 * Creates a solid color fill. |
| 446 * @param {!o3d.Pack} pack the Pack in which to create materials. |
| 447 * @param {number} red Red component (0.0 - 1.0). |
| 448 * @param {number} green Green component (0.0 - 1.0). |
| 449 * @param {number} blue Blue component (0.0 - 1.0). |
| 450 * @param {number} alpha Alpha component (0.0 - 1.0). |
| 451 * @return {!o3djs.gpu2d.Color} The created Color. |
| 452 */ |
| 453 o3djs.gpu2d.createColor = function(pack, red, green, blue, alpha) { |
| 454 var result = new o3djs.gpu2d.Color(pack); |
| 455 result.set(red, green, blue, alpha); |
| 456 return result; |
| 457 }; |
| 458 |
| 459 //---------------------------------------------------------------------- |
| 460 // Shaders and effects |
| 461 |
| 462 // TODO(kbr): antialiasing is not supported yet because the ddx |
| 463 // and ddy instructions are not part of the shader model 2.0. On |
| 464 // Windows we could easily upgrade to ps2.0a, but on Mac and Linux |
| 465 // there isn't an easy upgrade path from ARBVP1.0 and ARBFP1.0 which |
| 466 // incorporates these instructions. |
| 467 // |
| 468 // The solution within O3D is to compute the gradients using the |
| 469 // closed-form solution in Loop and Blinn's SIGGRAPH '05 paper. This |
| 470 // requires computation of the Psi matrix per vertex. In GLSL this is |
| 471 // not necessary; derivative instructions are always available there. |
| 472 |
| 473 /** |
| 474 * Generates the source for the shader used on the exterior triangles |
| 475 * of the shape -- the ones that evaluate the curve function. |
| 476 * @param {boolean} antialias whether to enable antialiasing. |
| 477 * @param {string} fillUniforms the uniforms for the fill. |
| 478 * @param {string} fillSource the source code snippet for the fill. |
| 479 * @return {string} |
| 480 * @private |
| 481 */ |
| 482 o3djs.gpu2d.generateLoopBlinnShaderSource_ = function(antialias, |
| 483 fillUniforms, |
| 484 fillSource) { |
| 485 var result = '' + |
| 486 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + |
| 487 fillUniforms + |
| 488 '\n' + |
| 489 'struct VertexShaderInput {\n' + |
| 490 ' float2 position : POSITION;\n' + |
| 491 ' float3 klm : TEXCOORD0;\n' + |
| 492 '};\n' + |
| 493 '\n' + |
| 494 'struct PixelShaderInput {\n' + |
| 495 ' float4 position : POSITION;\n' + |
| 496 ' float3 klm : TEXCOORD0;\n' + |
| 497 '};\n' + |
| 498 '\n' + |
| 499 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + |
| 500 ' PixelShaderInput output;\n' + |
| 501 '\n' + |
| 502 ' output.position = mul(float4(input.position, 0, 1),\n' + |
| 503 ' worldViewProjection);\n' + |
| 504 ' output.klm = input.klm;\n' + |
| 505 ' return output;\n' + |
| 506 '}\n' + |
| 507 '\n' + |
| 508 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' + |
| 509 ' float3 klm = input.klm;\n'; |
| 510 var alphaComputation; |
| 511 if (antialias) { |
| 512 alphaComputation = '' + |
| 513 ' // Gradients\n' + |
| 514 ' float3 px = ddx(input.klm);\n' + |
| 515 ' float3 py = ddy(input.klm);\n' + |
| 516 '\n' + |
| 517 ' // Chain rule\n' + |
| 518 ' float k2 = klm.x * klm.x;\n' + |
| 519 ' float c = k2 * klm.x - klm.y * klm.z;\n' + |
| 520 ' float k23 = 3.0 * k2;\n' + |
| 521 ' float cx = k23 * px.x - klm.z * px.y - klm.y * px.z;\n' + |
| 522 ' float cy = k23 * py.x - klm.z * py.y - klm.y * py.z;\n' + |
| 523 '\n' + |
| 524 ' // Signed distance\n' + |
| 525 ' float sd = c / sqrt(cx * cx + cy * cy);\n' + |
| 526 '\n' + |
| 527 ' // Linear alpha\n' + |
| 528 ' float alpha = clamp(0.5 - sd, 0.0, 1.0);\n'; |
| 529 } else { |
| 530 alphaComputation = '' + |
| 531 ' float t = klm.x * klm.x * klm.x - klm.y * klm.z;\n' + |
| 532 ' float alpha = clamp(sign(t), 0.0, 1.0);\n'; |
| 533 } |
| 534 |
| 535 return result + alphaComputation + |
| 536 '\n' + |
| 537 fillSource + |
| 538 '}\n' + |
| 539 '\n' + |
| 540 '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + |
| 541 '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + |
| 542 '// #o3d MatrixLoadOrder RowMajor\n'; |
| 543 }; |
| 544 |
| 545 /** |
| 546 * Generates the source for the shader used on the interior triangles |
| 547 * of the shape. |
| 548 * @param {string} fillUniforms the uniforms for the fill. |
| 549 * @param {string} fillSource the source code snippet for the fill. |
| 550 * @return {string} |
| 551 * @private |
| 552 */ |
| 553 o3djs.gpu2d.generateSolidShaderSource_ = function(fillUniforms, fillSource) { |
| 554 var result = '' + |
| 555 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + |
| 556 fillUniforms + |
| 557 '\n' + |
| 558 'struct VertexShaderInput {\n' + |
| 559 ' float2 position : POSITION;\n' + |
| 560 '};\n' + |
| 561 '\n' + |
| 562 'struct PixelShaderInput {\n' + |
| 563 ' float4 position : POSITION;\n' + |
| 564 '};\n' + |
| 565 '\n' + |
| 566 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + |
| 567 ' PixelShaderInput output;\n' + |
| 568 '\n' + |
| 569 ' output.position = mul(float4(input.position, 0, 1),\n' + |
| 570 ' worldViewProjection);\n' + |
| 571 ' return output;\n' + |
| 572 '}\n' + |
| 573 '\n' + |
| 574 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' + |
| 575 ' float alpha = 1.0;\n' + |
| 576 fillSource + |
| 577 '}\n' + |
| 578 '\n' + |
| 579 '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + |
| 580 '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + |
| 581 '// #o3d MatrixLoadOrder RowMajor\n'; |
| 582 return result; |
| 583 }; |
| 584 |
| 585 /** |
| 586 * Enum for the types of fills. |
| 587 * @enum |
| 588 * @private |
| 589 */ |
| 590 o3djs.gpu2d.FillTypes_ = { |
| 591 COLOR: 0 |
| 592 }; |
| 593 |
| 594 /** |
| 595 * Shader code for the various fills, indexed by FillTypes_. |
| 596 * @type {!Array.<{uniforms: string, source: string}>} |
| 597 * @private |
| 598 */ |
| 599 o3djs.gpu2d.FILL_CODE_ = [ |
| 600 { uniforms: |
| 601 'uniform float4 color;\n', |
| 602 source: |
| 603 'return float4(color.r, color.g, color.b, color.a * alpha);\n' |
| 604 } |
| 605 ]; |
| 606 |
| 607 /** |
| 608 * Cache of effects indexed by pack's client ID. Each entry is an |
| 609 * array indexed by fill type. |
| 610 * @type {!Array.<!Array.<!o3d.Effect>>} |
| 611 * @private |
| 612 */ |
| 613 o3djs.gpu2d.interiorEffectCache_ = []; |
| 614 |
| 615 /** |
| 616 * Cache of effects indexed by pack's client ID. Each entry is an |
| 617 * array indexed by fill type. |
| 618 * @type {!Array.<!Array.<!o3d.Effect>>} |
| 619 * @private |
| 620 */ |
| 621 o3djs.gpu2d.exteriorEffectCache_ = []; |
| 622 |
| 623 /** |
| 624 * Loads a fill effect for a Path. |
| 625 * @param {!o3d.Pack} pack the Pack in which to create materials. |
| 626 * @param {o3djs.gpu2d.FillTypes_} fillType the fill type to create. |
| 627 * @param {boolean} interior whether this effect is filling the solid |
| 628 * interior portion of the shape or the exterior region containing |
| 629 * the curves. |
| 630 * @return {!o3d.Effect} |
| 631 * @private |
| 632 */ |
| 633 o3djs.gpu2d.loadEffect_ = function(pack, fillType, interior) { |
| 634 var effectCache; |
| 635 if (interior) { |
| 636 effectCache = o3djs.gpu2d.interiorEffectCache_; |
| 637 } else { |
| 638 effectCache = o3djs.gpu2d.exteriorEffectCache_; |
| 639 } |
| 640 var effectList = o3djs.gpu2d.getEffectList_(pack, effectCache); |
| 641 var effect = effectList[fillType]; |
| 642 if (!effect) { |
| 643 effect = pack.createObject('Effect'); |
| 644 var result = false; |
| 645 var sourceSnippets = o3djs.gpu2d.FILL_CODE_[fillType]; |
| 646 if (interior) { |
| 647 result = effect.loadFromFXString( |
| 648 o3djs.gpu2d.generateSolidShaderSource_(sourceSnippets.uniforms, |
| 649 sourceSnippets.source)); |
| 650 } else { |
| 651 result = effect.loadFromFXString( |
| 652 o3djs.gpu2d.generateLoopBlinnShaderSource_(false, |
| 653 sourceSnippets.uniforms, |
| 654 sourceSnippets.source)); |
| 655 } |
| 656 if (!result) { |
| 657 alert('Error loading shader: interior = ' + interior); |
| 658 } |
| 659 effectList[fillType] = effect; |
| 660 } |
| 661 return effect; |
| 662 }; |
| 663 |
| 664 /** |
| 665 * Fetches and/or creates the effect list for a given pack from the |
| 666 * passed effect cache. |
| 667 * @param {!o3d.Pack} pack the Pack in which to create materials. |
| 668 * @param {!Array.<!Array.<!o3d.Effect>>} effectCache the effect cache. |
| 669 * @return {!Array.<o3d.Effect>} |
| 670 * @private |
| 671 */ |
| 672 o3djs.gpu2d.getEffectList_ = function(pack, effectCache) { |
| 673 var list = effectCache[pack.clientId]; |
| 674 if (!list) { |
| 675 list = []; |
| 676 effectCache[pack.clientId] = list; |
| 677 } |
| 678 return list; |
| 679 }; |
| 680 |
OLD | NEW |