summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js')
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js213
1 files changed, 213 insertions, 0 deletions
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js
new file mode 100644
index 0000000..35f0161
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+/**
+ * Represents the surface area that has been hit on a texture map by creating
+ * a same-sized canvas composed of uniquely colored pixels.
+ *
+ * The original intention was to render the target from a single bubble's point
+ * of view using this as the texture map in place of the original texture. With
+ * stenciling, you render only the pixels that the bubble occludes and thus
+ * intersects with when it hits the block. Since each pixel in the rendered
+ * buffer has a unique color, it can be reverse mapped to the corresponding
+ * texel position and marked off as "hit".
+ *
+ * @param {number} width The width of the texture to track hits for.
+ * @param {number} height The height of the texture to track hits for.
+ * @param {o3d.Pack} pack A pack to create objects with.
+ * @constructor
+ */
+var ShadowMap = function(width, height, pack) {
+ this.width = width;
+ this.height = height;
+ this.canvas = document.createElement("canvas");
+ this.canvas.width = width;
+ this.canvas.height = height;
+
+ this.cached_percent_ = 0;
+ this.dirty_cache_ = true;
+
+ this.texture = pack.createTexture2D(width, height,
+ g.o3d.Texture.XRGB8,
+ 1,
+ false);
+
+ ShadowMap.init(this.canvas);
+ this.redrawTexture();
+};
+
+/**
+ * Updates the texture object to reflect the most recent values in the canvas.
+ */
+ShadowMap.prototype.redrawTexture = function() {
+ this.texture.setFromCanvas_(this.canvas, true, false, false);
+};
+
+/**
+ * Calculates the percent of the shadow map covered of the entire map.
+ * @private
+ */
+ShadowMap.prototype.calculatePercent_ = function() {
+ var context = this.canvas.getContext("2d");
+ var imageData = context.getImageData(0, 0, this.width, this.height);
+ var count = 0;
+ for (var i = 0; i < this.width * this.height; i++) {
+ if (imageData.data[i * 4 + 3] != ShadowMap.ALPHA_START) {
+ count++;
+ }
+ }
+ this.cached_percent_ =
+ Math.round(count * 100.0 / (this.width * this.height));
+};
+
+
+/**
+ * Returns the percentage of the texture that is currently covered as a value
+ * from 0 to 100. Caching is used so this value is not recomputed if no changes
+ * have been made to the texture (via the apply method).
+ *
+ * @return {number} The percent, or -1 if the block has not been initialized.
+ */
+ShadowMap.prototype.percentCovered = function() {
+ if (this.dirty_cache_) {
+ this.dirty_cache_ = false;
+ this.calculatePercent_();
+ }
+ return this.cached_percent_;
+};
+
+/**
+ * Given a canvas whose pixels contain the "hit" pixels, marks off the
+ * corresponding pixels in the original texture. Any pixels in the canvas with
+ * an alpha of 0 are ignored.
+ *
+ * @param {Canvas} renderedCanvas
+ */
+ShadowMap.prototype.apply = function(renderedCanvas) {
+ var data = this.canvas.getContext("2d").getImageData(0, 0,
+ this.width, this.height);
+ var rWidth = renderedCanvas.width;
+ var rHeight = renderedCanvas.height;
+ var rContext = renderedCanvas.getContext("2d");
+ var rData = rContext.getImageData(0, 0, rWidth, rHeight);
+
+ for (var i = 0; i < rWidth * rHeight; i++) {
+ if (rData.data[i * 4 + 3] > 0) { // Alpha of the rendered canvas > 0.
+ ShadowMap.updatePixel_(data, rData.data[i * 4],
+ rData.data[i * 4 + 1], rData.data[i * 4 + 2]);
+ }
+ }
+ this.canvas.getContext("2d").putImageData(data, 0, 0);
+};
+
+/**
+ * Number of values per channel.
+ * @type {number}
+ */
+ShadowMap.base = 256;
+
+/**
+ * The alpha value of unhit pixels in this map. In [0, 255] range.
+ * @type {number}
+ */
+ShadowMap.ALPHA_START = 127;
+
+/**
+ * The alpha value of hit pixels in this map. In [0, 255] range.
+ * @type {number}
+ */
+ShadowMap.ALPHA_PUT = 255;
+
+/**
+ * Given the index of a pixel in an image, computes the RGB color of that pixel
+ * based on the pattern we used to initialize the map.
+ * @param {number} index
+ * @return {!Array.<number>}
+ */
+ShadowMap.indexToRGB_ = function(index) {
+ var base = ShadowMap.base;
+ var r = Math.floor(index / (base * base));
+ var g = Math.floor((index % (base * base)) / base);
+ var b = index % base;
+ return [r, g, b];
+}
+
+/**
+ * Given an array [r, g, b] of colors, computes the index of that pixel based on
+ * the pattern we used to initialize the map.
+ * @param {!Array.<number>} colors
+ * @return {number}
+ */
+ShadowMap.rgbToIndex_ = function(colors) {
+ var r = colors[0];
+ var g = colors[1];
+ var b = colors[2];
+ var base = ShadowMap.base;
+ return b + (g * base) + (r * base * base);
+};
+
+/**
+ * Given a color, marks off the corresponding location in this map.
+ * @param {ImageData} the canvas' ImageData object
+ * @param {number} r
+ * @param {number} g
+ * @param {number} b
+ * @return {ImageData} a modified ImageData object
+ */
+ShadowMap.updatePixel_ = function(imageData, r, g, b) {
+ var index = ShadowMap.rgbToIndex_([r, g, b]);
+ imageData.data[index * 4 + 3] = ShadowMap.ALPHA_PUT;
+ return imageData;
+};
+
+/**
+ * Initializes this map so each pixel is a unique color, where the channels are
+ * incremented iteratively in B-G-R order.
+ * @param {Canvas} canvas The canvas object to initialize to unique colors.
+ */
+ShadowMap.init = function(canvas) {
+ var width = canvas.width;
+ var height = canvas.height;
+ var context = canvas.getContext("2d");
+ var imageData = context.getImageData(0, 0, width, height);
+ for (var h = 0; h < height; h++) {
+ for (var w = 0; w < width; w++) {
+ var pixelNum = w + h * width;
+ var index = pixelNum * 4;
+ var color = ShadowMap.indexToRGB_(pixelNum);
+ imageData.data[index] = color[0];
+ imageData.data[index + 1] = color[1];
+ imageData.data[index + 2] = color[2];
+ imageData.data[index + 3] = ShadowMap.ALPHA_START;
+ }
+ }
+ context.putImageData(imageData, 0, 0);
+};