summaryrefslogtreecommitdiffstats
path: root/o3d
diff options
context:
space:
mode:
authorluchen@chromium.org <luchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-13 23:37:58 +0000
committerluchen@chromium.org <luchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-13 23:37:58 +0000
commit5b75999a092d6071fcaa09b0b4e522e3d7feea97 (patch)
tree1d45989a26838022e80ab217aa75a9c3024bfe9d /o3d
parent10836fe9a967047565088b0dc1414185e89dd70a (diff)
downloadchromium_src-5b75999a092d6071fcaa09b0b4e522e3d7feea97.zip
chromium_src-5b75999a092d6071fcaa09b0b4e522e3d7feea97.tar.gz
chromium_src-5b75999a092d6071fcaa09b0b4e522e3d7feea97.tar.bz2
o3d-webgl: Adding preliminary files for 'bubble blaster' demo.
Review URL: http://codereview.chromium.org/3123017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56110 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d')
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/extra/shadow-map.js213
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/index.html165
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/block.js145
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble-manager.js150
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble.js231
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/camera.js190
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/game.js125
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/level.js154
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/js/main.js174
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble1.pngbin0 -> 3139 bytes
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble2.pngbin0 -> 2267 bytes
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble3.pngbin0 -> 1779 bytes
-rw-r--r--o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/style.css188
13 files changed, 1735 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);
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/index.html b/o3d/samples/o3d-webgl-samples/bubbles/blaster/index.html
new file mode 100644
index 0000000..284fcb3
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/index.html
@@ -0,0 +1,165 @@
+<!--
+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.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>
+Bubble Blaster (Experimental)
+</title>
+
+<!-- CSS -->
+<link href='http://fonts.googleapis.com/css?family=Inconsolata'
+ rel='stylesheet' type='text/css'>
+<link href='resources/style.css' rel='stylesheet' type='text/css'>
+
+<!-- jQuery -->
+<script type="text/javascript"
+ src="../third_party/jquery/jquery-1.4.2.min.js"></script>
+
+<!-- O3D -->
+<script type="text/javascript" src="../../../o3d-webgl/base.js"></script>
+<script type="text/javascript" src="../../../o3djs/base.js"></script>
+
+<!-- Demo files -->
+<script type="text/javascript" src="js/camera.js"></script>
+<script type="text/javascript" src="js/bubble.js"></script>
+<script type="text/javascript" src="js/bubble-manager.js"></script>
+<script type="text/javascript" src="js/block.js"></script>
+<script type="text/javascript" src="js/level.js"></script>
+<script type="text/javascript" src="js/game.js"></script>
+<script type="text/javascript" src="js/main.js"></script>
+</head>
+<body onload="init();" onunload="uninit();" id="body">
+
+<!-- Contains the level name and number. -->
+<div id="level-description" class="main-container"></div>
+
+<!-- Bubble listing on the left. -->
+<div id="bubble-manager" class="main-container">
+ <div id="bubble-count">
+ <div id="bubble-count-span"></div> bubbles remaining
+ </div>
+ <div id="bubble-bubbles"></div>
+</div>
+
+<!-- Links in the upper right. -->
+<div id="options" class="main-container">
+ <ul>
+ <li><a id="link-help" href="#help">help</a></li>
+ <li><a id="link-restart" href="#restart">restart</a></li>
+ </ul>
+</div>
+
+<!-- Progress bar at the bottom -->
+<div id="progress">
+ <div id="goal" style="width: 0%"></div>
+</div>
+
+<!-- Help dialog. -->
+<div id="help" style="display:none;">
+ <b>Goal</b>
+ <p>Your mission is to use bubble "splats" to cover as much of the surface
+ area of the center target as possible.
+ </p>
+ <br />
+ <b>Controls</b>
+ <ul>
+ <li> <b>SPACE</b> :: releases the next bubble in your queue.</li>
+ <li> <b>wasd</b> :: rotates your position around the model.</li>
+ </ul>
+ <br />
+ <p>
+ <a href="#close-help" id="close-help">close</a>
+ </p>
+</div>
+
+<!-- Final result -->
+<div id="finalResult" style="display:none;">
+ <b>Game over!</b>
+ <p>You successfully covered <span id="final"></span> of the target.</p>
+</div>
+
+<!-- The o3d container -->
+<div id="o3d-main" style="width: 100%; height: 100%;"></div>
+
+<!-- Shader for the block. -->
+<textarea style="display:none;" id="blockShader">
+ attribute vec4 position;
+ attribute vec2 texCoord0;
+ uniform mat4 worldViewProjection;
+ varying vec4 pos;
+ varying vec2 tex;
+
+ /**
+ * The vertex shader simply transforms the input vertices to screen space.
+ */
+ void main() {
+ // Multiply the vertex positions by the worldViewProjection matrix to
+ // transform them to screen space.
+ gl_Position = worldViewProjection * position;
+ pos = position;
+ tex = texCoord0;
+ }
+
+ // #o3d SplitMarker
+ varying vec4 pos;
+ varying vec2 tex;
+ uniform sampler2D myTexture;
+ uniform vec4 bubblePosition[SIZE];
+
+ bool insideABubble(vec3 pos) {
+ bool count = false;
+ for (int i = 0; i < SIZE; i++) {
+ vec4 bubble = bubblePosition[i];
+ if (length(abs(pos.xyz - bubble.xyz)) < bubble.w) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The fragment shader derives color based on the position.
+ */
+ void main() {
+ vec4 color = texture2D(myTexture, tex);
+ if (insideABubble(pos.xyz)) {
+ // inside the bubble
+ gl_FragColor = vec4((color.xyz / 2.0), 1.0);
+ } else {
+ gl_FragColor = vec4(color.xyz, 1.0);
+ }
+ }
+</textarea>
+</body>
+</html>
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/block.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/block.js
new file mode 100644
index 0000000..a2102df1
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/block.js
@@ -0,0 +1,145 @@
+/*
+ * 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 rotating target that is the focus of each level.
+ * @param {string} src The path to the json scene.
+ * @constructor
+ */
+var Block = function(src) {
+ this.src = src;
+ this.load_();
+};
+
+/**
+ * The URL of the scene.json file the target uses.
+ * @type {string}
+ */
+Block.prototype.src = '';
+
+/**
+ * The top level transform that contains this block. Beneath this transform
+ * sits a custom transform for intermediate scaling, then the geometry.
+ * @type {o3d.Transform}
+ */
+Block.prototype.transform = null;
+
+/**
+ * A pack just for components belonging to this block.
+ * @type {o3d.Pack}
+ */
+Block.prototype.pack = null;
+
+/**
+ * A pick manager that helps computes intersections between the block and
+ * bubbles.
+ *
+ * @type {o3d.PickManager}
+ */
+Block.prototype.pickManager = null;
+
+/**
+ * Advances the block one time step.
+ * @param {number} elapsedTime
+ */
+Block.prototype.step = function(elapsedTime) {
+ // No op. Could potentially advance a clock to rotate the model.
+};
+
+/**
+ * Returns the overridden effect that the model should use.
+ * @return {o3d.Effect}
+ */
+Block.prototype.getOverrideEffect = function() {
+ var shader = document.getElementById('blockShader').value;
+ shader = shader.replace(/SIZE/g,
+ g_game.level.bubbleManager_.bubbleQueue_.length);
+ var effect = this.pack.createObject('Effect');
+ effect.loadFromFXString(shader);
+ return effect;
+};
+
+/**
+ * Loads the scene.json file and adds it to the scene. Also binds the eye and
+ * bubble position parameters.
+ */
+Block.prototype.load_ = function() {
+ var that = this;
+ this.pack = g_client.createPack();
+ this.transform = this.pack.createObject('Transform');
+
+ var parentTransform = this.transform;
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert("Could not load: \n" + exception);
+ } else {
+ o3djs.pack.preparePack(that.pack, g_viewInfo);
+ parent.parent = g_client.root;
+ // Create the pick manager.
+ that.pickManager = o3djs.picking.createPickManager(parent);
+ that.pickManager.update();
+
+ // TODO: Create a generic sampler with IO-loaded image texture that will
+ // be used for all models.
+ var samplers = pack.getObjectsByClassName('o3d.Sampler');
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ var effect = that.getOverrideEffect();
+ effect.createUniformParameters(material);
+ material.effect = effect;
+ if (material.getParam('myTexture')) {
+ material.getParam('myTexture').value = samplers[0];
+ }
+ if (material.getParam('bubblePosition')) {
+ material.getParam('bubblePosition').value = g_paramArray;
+ }
+
+ // TODO: Not all materials will use this parameter, or it may be
+ // called something different.
+ if (material.getParam('lightWorldPos')) {
+ material.getParam('lightWorldPos').bind(g_lightWorldPosParam);
+ }
+ }
+
+ }
+ }
+
+ // Inner transform for scaling and adjustment.
+ var child = this.pack.createObject('Transform');
+ child.parent = this.transform;
+
+ try {
+ o3djs.scene.loadScene(g_client, this.pack, child, this.src, callback);
+ } catch (e) {
+ alert("loading failed : " + e);
+ }
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble-manager.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble-manager.js
new file mode 100644
index 0000000..e37eae7
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble-manager.js
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+/**
+ * Manages a sequence of bubble objects.
+ *
+ * @constructor
+ * @param {!Array<!Bubble.BubbleType>} requestedSequence
+ * @param {o3d.Pack} pack
+ */
+var BubbleManager = function(requestedSequence, pack) {
+ this.bubbleQueue_ = [];
+ this.nextIndex_ = 0;
+ this.pack_ = pack;
+
+ this.container_ = $('#bubble-bubbles');
+ this.container_.html(" "); // reset
+ this.counter_ = $('#bubble-count-span');
+
+ for (var i = 0; i < requestedSequence.length; i++) {
+ var bubble = new Bubble(requestedSequence[i], this.pack_);
+ var el = g_paramArray.createParam(i, 'ParamFloat4');
+ el.bind(bubble.param);
+
+ this.bubbleQueue_.push(bubble);
+ var img = new Image();
+ var imageSrc;
+ var type = requestedSequence[i];
+ switch (type) {
+ case Bubble.SMALL:
+ imageSrc = "resources/bubble3.png";
+ break;
+ case Bubble.MEDIUM:
+ imageSrc = "resources/bubble2.png";
+ break;
+ case Bubble.LARGE:
+ imageSrc = "resources/bubble1.png";
+ break;
+ }
+ img.src = imageSrc;
+ img.alt = "Bubble " + i;
+ this.container_.append(img);
+ this.counter_.text("" + requestedSequence.length);
+ }
+};
+
+/**
+ * The bubbles queued to be released.
+ * @type {!Array<!Bubble>}
+ * @private
+ */
+BubbleManager.prototype.bubbleQueue_ = [];
+
+/**
+ * The DOM container to show the queue of bubbles.
+ * @type {DOM.Element}
+ * @private
+ */
+BubbleManager.prototype.container_ = null;
+
+/**
+ * The DOM container that keeps the count of bubbles remaining.
+ * @type {DOM.Element}
+ * @private
+ */
+BubbleManager.prototype.counter_ = null;
+
+/**
+ * A pointer to the current position in the bubble queue.
+ * @type {number}
+ * @private
+ */
+BubbleManager.prototype.nextIndex_ = 0;
+
+/**
+ * A pack contains all the transforms, geometry and shaders of the bubbles it
+ * manages.
+ * @type {o3d.Pack}
+ * @private
+ */
+BubbleManager.prototype.pack_ = 0;
+
+/**
+ * Moves a bubble from the head of the queue to the tail of the bubblesInPlay
+ * list and returns that bubble.
+ *
+ * If the queue is empty, returns null.
+ *
+ * @return {Bubble}
+ */
+BubbleManager.prototype.pop = function() {
+ if (this.nextIndex_ == this.bubbleQueue_.length) {
+ return null;
+ }
+ var children = this.container_.children();
+ var img = children[this.nextIndex_];
+ img.className = "used";
+ $(img).slideUp();
+ this.counter_.text(this.bubbleQueue_.length - this.nextIndex_ - 1) + "";
+ return this.bubbleQueue_[this.nextIndex_++];
+};
+
+/**
+ * Returns the bubble at the head of the queue, but does not pop it.
+ * If the queue is empty, returns null.
+ *
+ * @return {Bubble}
+ */
+BubbleManager.prototype.peek = function() {
+ return (this.bubbleQueue_.length == this.nextIndex_) ? null :
+ this.bubbleQueue_[this.nextIndex_];
+};
+
+/**
+ * Advances each bubble in the active queue by one timestep.
+ */
+BubbleManager.prototype.step = function() {
+ for (var i = 0; i < this.nextIndex_; i++) {
+ var bubble = this.bubbleQueue_[i];
+ bubble.step();
+ }
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble.js
new file mode 100644
index 0000000..fef0107
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/bubble.js
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+
+/**
+ * A bubble object.
+ * @param {Bubble.BubbleType} type
+ * @param {o3d.Pack} pack
+ * @constructor
+ */
+var Bubble = function(type, pack) {
+ this.kind = type;
+ this.position = [0, 0, 0];
+ this.velocity = [0, 0, 0];
+ this.alive = false;
+ this.position = [2 * globals.playerRadius, 0, 0];
+ this.param = pack.createObject('ParamFloat4');
+ this.param.value = [0, 0, 0, 0];
+
+ switch (this.kind) {
+ case Bubble.SMALL:
+ this.scale = 0.2;
+ this.color = [0, 0.4, 0.7, 0.7];
+ this.speed = 0.05;
+ break;
+ case Bubble.MEDIUM:
+ this.scale = 0.3;
+ this.color = [0, 0.6, 0.2, 0.7];
+ this.speed = 0.08;
+ break;
+ case Bubble.LARGE:
+ this.scale = 0.45; // This is the radius.
+ this.color = [0.8, 0.2, 0.3, 0.7];
+ this.speed = 0.1;
+ break;
+ default:
+ throw 'Invalid type of Bubble';
+ }
+
+ this.transform = pack.createObject('Transform');
+ if (!Bubble.defaultShape) {
+ Bubble.createDefaultShape(pack);
+ }
+ this.transform.addShape(Bubble.defaultShape);
+ this.transform.visible = false;
+ this.transform.createParam('diffuse', 'ParamFloat4').value = this.color;
+};
+
+
+/**
+ * BubbleType,
+ * SMALL
+ * MEDIUM
+ * LARGE
+ */
+Bubble.SMALL = 0;
+Bubble.MEDIUM = 1;
+Bubble.LARGE = 2;
+
+/**
+ * The type of a bubble.
+ * @type {number}
+ */
+Bubble.BubbleType = goog.typedef;
+
+/**
+ * The type of bubble.
+ * @type {Bubble.BubbleType}
+ */
+Bubble.prototype.kind = -1;
+
+/**
+ * The bubble's position.
+ * @type {!Array.<number>}
+ */
+Bubble.prototype.position = [];
+
+/**
+ * The bubble's velocity.
+ * @type {!Array.<number>}
+ */
+Bubble.prototype.velocity = [];
+
+
+/**
+ * The bubble's color.
+ * @type {!Array.<number>}
+ */
+Bubble.prototype.color = [1, 1, 1];
+
+/**
+ * The bubble's speed. Higher = faster.
+ * @type {number}
+ */
+Bubble.prototype.speed = 1.0;
+
+/**
+ * The bubble's relative size.
+ * @type {!Array.<number>}
+ */
+Bubble.prototype.scale = 1.0;
+
+/**
+ * If bubble is in bounds.
+ * @type {boolean}
+ */
+Bubble.prototype.alive = false;
+
+/**
+ * The bubble's parent transform.
+ * @type {o3d.Transform}
+ */
+Bubble.prototype.transform = null;
+
+/**
+ * A param that will pass this bubble's position and radius to the shader.
+ * @type {o3d.ParamFloat4}
+ */
+Bubble.prototype.param = null;
+
+/**
+ * A shared primitive used by all the bubbles.
+ * @type {o3d.Shape}
+ */
+Bubble.defaultShape = null;
+
+/**
+ * An initializer that creates the default shape.
+ * @param {o3d.Pack} pack
+ */
+Bubble.createDefaultShape = function(pack) {
+ var shader = o3djs.material.createBasicMaterial(
+ pack,
+ g_viewInfo,
+ [1, 1, 1, 1],
+ true);
+ Bubble.defaultShape = o3djs.primitives.createSphere(pack, shader, 1, 20, 20);
+};
+
+/**
+ * Initializes a bubble at given position. The velocity is inferred to be
+ * towards the origin and is scaled based on this bubble's speed.
+ *
+ * @param {!Array.<number>} position
+ */
+Bubble.prototype.launch = function(position) {
+ this.transform.visible = true;
+ this.alive = true;
+ this.position = position;
+ this.velocity = g_math.mulScalarVector(this.speed,
+ g_math.negativeVector(g_math.normalize(position)));
+ this.updateTransforms_();
+ this.transform.parent = g_client.root;
+};
+
+
+/**
+ * Updates the transforms so the bubble draws in the correct position.
+ * @private
+ */
+Bubble.prototype.updateTransforms_ = function() {
+ this.transform.identity();
+ this.transform.translate(this.position);
+ this.transform.scale([this.scale, this.scale, this.scale]);
+};
+
+/**
+ * Updates the values of the uniform param associated with this bubble.
+ */
+Bubble.prototype.updateParam = function() {
+ this.param.value = [this.position[0], this.position[1], this.position[2],
+ this.scale];
+};
+
+/**
+ * Advances the bubble by one timestep to its new location.
+ */
+Bubble.prototype.step = function() {
+ if (!this.alive) {
+ return;
+ }
+
+ this.position[0] += this.velocity[0];
+ this.position[1] += this.velocity[1];
+ this.position[2] += this.velocity[2];
+ this.updateParam();
+ this.updateTransforms_();
+
+ // Compute the point furthest from the origin along the velocity ray.
+ var back = g_math.addVector(g_math.mulScalarVector(-this.scale,
+ g_math.normalize(this.velocity)), this.position);
+ var worldRay = {near: back, far: [0, 0, 0]};
+
+ // Use picking to determine if the origin has intersected the block.
+ var result = g_game.level.block.pickManager.pick(worldRay);
+ if (result) {
+ var ray = g_math.subVector(result.worldIntersectionPosition, back);
+ if (g_math.length(ray) <= this.scale) {
+ // Bubble intersected, so stop its movement.
+ this.alive = false;
+ this.transform.visible = false;
+ }
+ }
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/camera.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/camera.js
new file mode 100644
index 0000000..a9005d0
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/camera.js
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+/**
+ * A camera represents the view of the player. This class has a dependency on
+ * g_math.
+ *
+ * @param {number} fov
+ * @param {number} distance
+ * @param {o3d.DrawContext} context
+ */
+var Camera = function(fov, distance, context) {
+ this.eye_ = [0, 0, distance, 1];
+ this.fov_ = fov;
+ this.target_ = [0, 0, 0, 1];
+ this.up_ = [0, 1, 0, 0];
+ this.context_ = context;
+};
+
+/**
+ * The camera's eye (position).
+ * @type {!Array.<number>}
+ * @private
+ */
+Camera.prototype.eye_ = [0, 0, 0, 1];
+
+/**
+ * The camera's field of vision, in degrees.
+ * @type {number}
+ * @private
+ */
+Camera.prototype.fov_ = 45;
+
+/**
+ * The camera's target (lookAt).
+ * @type {!Array.<number>}
+ * @private
+ */
+Camera.prototype.target_ = [0, 0, 0, 1];
+
+/**
+ * The up vector.
+ * @type {!Array.<number>}
+ * @private
+ */
+Camera.prototype.up_ = [0, 0, 0, 0];
+
+/**
+ * The near plane.
+ * @type {number}
+ * @private
+ */
+Camera.prototype.nearPlane_ = 0.1;
+
+/**
+ * The far plane.
+ * @type {number}
+ * @private
+ */
+Camera.prototype.farPlane_ = 5000;
+
+/**
+ * The drawContext which this camera's position and perspective affects.
+ * @type {o3d.DrawContext}
+ * @private
+ */
+Camera.prototype.context_ = null;
+
+/**
+ * Returns the eye position of the camera as a 3-vector.
+ * @type {!Array.<number>}
+ */
+Camera.prototype.__defineGetter__('eye',
+ function() {
+ return [this.eye_[0], this.eye_[1], this.eye_[2]];
+ }
+);
+
+/**
+ * Returns the up vector of the camera as a 3-vector.
+ * @type {!Array.<number>}
+ */
+Camera.prototype.__defineGetter__('up',
+ function() {
+ return [this.up_[0], this.up_[1], this.up_[2]];
+ }
+);
+
+/**
+ * Returns the target/focal point of the camera as a 3-vector.
+ * @type {!Array.<number>}
+ */
+Camera.prototype.__defineGetter__('target',
+ function() {
+ return [this.target_[0], this.target_[1], this.target_[2]];
+ }
+);
+
+/**
+ * Camera.Dir
+ * UP,
+ * DOWN,
+ * LEFT,
+ * RIGHT,
+ */
+Camera.UP = 0;
+Camera.DOWN = 1;
+Camera.LEFT = 2;
+Camera.RIGHT = 3;
+
+/**
+ * Updates the projection matrix to correspond to the new width and height of
+ * the canvas.
+ * @param {number} width The width of the screen/canvas.
+ * @param {number} height The height of the screen/canvas.
+ */
+Camera.prototype.updateProjection = function(width, height) {
+ this.context_.projection = g_math.matrix4.perspective(
+ g_math.degToRad(this.fov_),
+ width / height,
+ this.nearPlane_,
+ this.farPlane_);
+};
+
+
+/**
+ * Updates the view matrix using this Camera's eye, target, and up vectors.
+ */
+Camera.prototype.updateView = function() {
+ this.context_.view = g_math.matrix4.lookAt(this.eye, this.target, this.up);
+};
+
+/**
+ * Rotates the Camera by delta radians in the given direction and updates the
+ * view.
+ *
+ * @param {Camera.Dir} direction
+ * @param {number} delta
+ */
+Camera.prototype.rotate = function(direction, delta) {
+ var axis;
+ switch(direction) {
+ case Camera.LEFT:
+ case Camera.RIGHT:
+ axis = this.up_;
+ break;
+ case Camera.UP:
+ case Camera.DOWN:
+ axis = g_math.cross(this.up_, g_math.negativeVector(this.eye));
+ break;
+ }
+ switch(direction) {
+ case Camera.LEFT:
+ case Camera.DOWN:
+ delta *= -1;
+ break;
+ }
+ var matrix = g_math.matrix4.axisRotation(axis, delta);
+ this.up_ = g_math.rowMajor.mulMatrixVector(matrix, this.up_);
+ this.eye_ = g_math.rowMajor.mulMatrixVector(matrix, this.eye_);
+ this.updateView();
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/game.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/game.js
new file mode 100644
index 0000000..d7f818c
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/game.js
@@ -0,0 +1,125 @@
+/*
+ * 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 a game entity. There should be a single game object.
+ * @constructor
+ */
+var Game = function() {
+};
+
+/**
+ * Initializes the game object and populates its fields.
+ * @param {o3d.Pack} pack The pack that this game should create objects in.
+ */
+Game.prototype.init = function(pack) {
+ this.camera = new Camera(globals.fov, globals.playerRadius,
+ g_viewInfo.drawContext);
+ this.level = new Level(''); // TODO: This will eventually be a .json path.
+ this.level.start();
+ g_lightWorldPosParam.value = this.camera.eye;
+ this.camera.updateView();
+};
+
+/**
+ * The game's camera.
+ * @type {Camera}
+ */
+Game.prototype.camera = null;
+
+/**
+ * The current level.
+ * @type {Level}
+ */
+Game.prototype.level = null;
+
+/**
+ * An action handler to address key presses in the game.
+ * @param {Event} event
+ */
+Game.prototype.onKeyPress = function(event) {
+ event = event || window.event;
+ if (event.metaKey)
+ return;
+
+ var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event));
+ keyChar = keyChar.toLowerCase();
+
+ var dir;
+ switch(keyChar) {
+ case 'a':
+ dir = Camera.LEFT;
+ break;
+ case 'd':
+ dir = Camera.RIGHT;
+ break;
+ case 'w':
+ dir = Camera.UP;
+ break;
+ case 's':
+ dir = Camera.DOWN;
+ break;
+ case ' ':
+ this.level.launchBubble();
+ // fall through
+ default:
+ return;
+ }
+ this.camera.rotate(dir, globals.keyPressDelta);
+ g_lightWorldPosParam.value = this.camera.eye;
+};
+
+/**
+ * Ends the game and displays the score screen.
+ * @param {string} amount Coverage achieved, as a string.
+ */
+Game.prototype.endGame = function(amount) {
+ $("#final").text(amount);
+ $("#finalResult").fadeIn()
+};
+
+/**
+ * Steps the game forward one timestep (elapsedTime).
+ * @param {number} elapsedTime The seconds passed since the last call.
+ */
+Game.prototype.step = function(elapsedTime) {
+ this.level.step(elapsedTime);
+};
+
+
+/**
+ * An action handler to address when the o3d container is resized.
+ * @param {number} newWidth
+ * @param {number} newHeight
+ */
+Game.prototype.onClientResize = function(newWidth, newHeight) {
+ this.camera.updateProjection(newWidth, newHeight);
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/level.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/level.js
new file mode 100644
index 0000000..c8aaebc
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/level.js
@@ -0,0 +1,154 @@
+/*
+ * 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 a currently active level that is being played.
+ * @param config
+ */
+var Level = function(config) {
+ this.load_(config);
+ this.pack_ = g_client.createPack();
+};
+
+/**
+ * This level's bubble manager.
+ * @type {BubbleManager}
+ * @private
+ */
+Level.prototype.bubbleManager_ = null;
+
+/**
+ * This level's block.
+ * @type {Block}
+ */
+Level.prototype.block = null;
+
+/**
+ * A pack for assets belonging to this level.
+ * @type {o3d.Pack}
+ * @private
+ */
+Level.prototype.pack_ = null;
+
+/**
+ * The current percent of surface area covered, as a percent value in the range
+ * 0 to 100.
+ * @type {number}
+ * @private
+ */
+Level.prototype.progress_ = 0;
+
+/**
+ * The name of the level.
+ * @type {string}
+ */
+Level.prototype.levelName = '';
+
+/**
+ * The level number.
+ * @type {number}
+ */
+Level.prototype.levelNumber = 0;
+
+/**
+ * The sequence of bubbles alloted for this level.
+ * @type {!Array.<Bubble.BubbleType>}
+ */
+Level.prototype.bubbles = [];
+
+/**
+ * The target coverage goal for this level, in the range [0, 100].
+ * @type {number}
+ */
+Level.prototype.goal = -1;
+
+/**
+ * The path to the resource used to load the block model.
+ * @type {string}
+ */
+Level.prototype.sceneSrc = '';
+
+/**
+ * Launches a bubble into the game, if possible. Ends the game if no bubbles
+ * are remaining in the queue.
+ */
+Level.prototype.launchBubble = function() {
+ var bubble = this.bubbleManager_.pop();
+ if (bubble) {
+ bubble.launch(g_game.camera.eye);
+ // TODO: We arbitrarily advance the progress counter. This should later
+ // compute the covered surface area and adjust appropriately.
+ this.progress_ += Math.floor(Math.random() * 10);
+ this.progress_ = Math.min(this.progress_, 99);
+ $("#goal").css("width", this.progress_ + "%");
+ } else {
+ g_game.endGame($("#goal").css("width"));
+ }
+};
+
+/**
+ * Advances the level by a time duration of elapsedTime.
+ * @param {number} elapsedTime The seconds passed since the last call.
+ */
+Level.prototype.step = function(elapsedTime) {
+ this.bubbleManager_.step(elapsedTime);
+ this.block.step(elapsedTime);
+};
+
+/**
+ * Starts the level. Should be called after the configurations have loaded.
+ */
+Level.prototype.start = function() {
+ $('#level-description').text(this.levelNumber + ' ' + this.levelName);
+ this.bubbleManager_ = new BubbleManager(this.bubbles, this.pack_);
+ this.block = new Block(this.sceneSrc);
+};
+
+/**
+ * Loads a JSON-encoded configuration file and populates this level with its
+ * values.
+ * @param {string} config The path to the JSON file.
+ * @private
+ */
+Level.prototype.load_ = function(config) {
+ // TODO: This will eventually load config, a json path, and populate the
+ // fields below.
+ this.levelNumber = 12;
+ this.levelName = 'Crate';
+ this.bubbles = [Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM,
+ Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM,
+ Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM,
+ Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM,
+ Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM,
+ Bubble.SMALL, Bubble.LARGE, Bubble.SMALL, Bubble.MEDIUM];
+ this.goal = 90;
+ this.sceneSrc = "../../../simpleviewer/assets/cube/scene.json";
+};
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/main.js b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/main.js
new file mode 100644
index 0000000..7ec99c6
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/js/main.js
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+o3djs.base.o3d = o3d;
+o3djs.require('o3djs.webgl');
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.scene');
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.picking');
+
+/**
+ * Some global settings.
+ */
+var globals = {
+ playerRadius: 5,
+ fov: 45,
+ keyPressDelta: 0.1,
+ bubbleCullDistance: 5,
+};
+
+/**
+ * Some global objects.
+ */
+var g_root;
+var g_o3d;
+var g_client;
+var g_viewInfo;
+var g_game;
+var g_pack;
+var g_o3dHeight, g_o3dWidth;
+var g_paramArray;
+
+/**
+ * Run once the body is loaded.
+ */
+function init() {
+ o3djs.webgl.makeClients(init2);
+}
+
+function init2(clientElements) {
+ var g_o3dElement = clientElements[0]; // A canvas element.
+ g_o3d = g_o3dElement.o3d;
+ g_client = g_o3dElement.client;
+
+ g_client.normalizeClearColorAlpha = false;
+ g_math = o3djs.math;
+
+ g_pack = g_client.createPack();
+ var rootPack = g_client.createPack();
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ rootPack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0, 0, 0, 0]);
+
+ var paramObject = rootPack.createObject('ParamObject');
+ g_clockParam = paramObject.createParam('time', 'ParamFloat');
+ g_clockParam.value = 0;
+ g_lightWorldPosParam =
+ paramObject.createParam('lightWorldPos', 'ParamFloat4');
+ g_lightWorldPosParam.value = [0, 0, 5];
+
+ g_paramArray = rootPack.createObject('ParamArray');
+
+ g_game = new Game();
+ g_game.init(g_pack);
+ checkClientSize();
+
+ // Pass the key press to the game's handler.
+ o3djs.event.addEventListener(g_o3dElement, 'keypress', function(e) {
+ g_game.onKeyPress(e);
+ });
+
+ // Attach click handlers to the level links.
+ $("#link-help").bind('click', function(e) {
+ $("#finalResult").hide();
+ showHelpMenu(true);
+ return false;
+ });
+
+ $("#close-help").bind('click', function(e) {
+ showHelpMenu(false);
+ return false;
+ });
+
+ $("#link-restart").bind('click', function(e) {
+ $("#finalResult").hide();
+ $("#help").hide();
+ g_game = new Game();
+ g_game.init();
+ var children = g_client.root.children();
+ for (var i = 0; i < children.length; ++i) {
+ children[i].parent = null; // remove from tree
+ }
+ return false;
+ });
+
+ g_client.setRenderCallback(onRender);
+}
+
+/**
+ * Shows or hides the help menu.
+ * @param doShow
+ */
+function showHelpMenu(doShow) {
+ doShow ? $("#help").slideDown() : $("#help").slideUp();
+}
+
+
+/**
+ * Called every frame.
+ */
+function onRender(renderEvent) {
+ g_clockParam.value += renderEvent.elapsedTime;
+ g_game.step(renderEvent.elapsedTime);
+ checkClientSize();
+}
+
+/**
+ * Checks if the window size has changed and updates the matrices if so.
+ * @return
+ */
+function checkClientSize() {
+ var newWidth = parseInt(g_client.width);
+ var newHeight = parseInt(g_client.height);
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+ g_game.onClientResize(newWidth, newHeight);
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble1.png b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble1.png
new file mode 100644
index 0000000..9fb84c6
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble1.png
Binary files differ
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble2.png b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble2.png
new file mode 100644
index 0000000..570fc7f
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble2.png
Binary files differ
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble3.png b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble3.png
new file mode 100644
index 0000000..732d192
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/bubble3.png
Binary files differ
diff --git a/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/style.css b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/style.css
new file mode 100644
index 0000000..b5e8a55
--- /dev/null
+++ b/o3d/samples/o3d-webgl-samples/bubbles/blaster/resources/style.css
@@ -0,0 +1,188 @@
+/**
+ 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.
+*/
+
+* {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ position: relative;
+}
+
+body, p, td, a {
+ font-family: 'Inconsolata', Verdana, Arial, Tahoma;
+ color: #FFF;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ color: yellow;
+}
+
+p {
+ margin: 5px 0;
+}
+
+ul {
+ margin-left: 15px;
+}
+
+/** The left hand side bubble viewer. */
+#bubble-manager {
+ min-width: 50px;
+ min-height: 300px;
+ height: 95%;
+ overflow: hidden;
+ padding: 10px;
+ position: absolute;
+ margin: 10px;
+ text-align: center;
+}
+
+/** The bubble images within */
+#bubble-bubbles img {
+ display: block;
+ margin: 3px auto;
+}
+
+/** Text div describing bubbles */
+#bubble-count {
+ font-size: 8pt;
+}
+
+/** Number of bubbles left */
+#bubble-count-span {
+ font-size: 18pt;
+}
+
+/** For bubbles that have been placed. */
+.used {
+ opacity: 0.5;
+ -moz-opacity: 0.5;
+}
+
+body {
+ background: -webkit-gradient(radial, center center, 0, center center,
+ 400, from(#89EAF5), to(black));
+ background: -moz-radial-gradient(#89EAF5, black);
+}
+
+/** Links that float upper right corner */
+#options {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 15px;
+ font-size: 12pt;
+ z-index: 2;
+}
+
+#options ul {
+ list-style-type: none;
+}
+
+#options li {
+ margin: 5px;
+}
+
+/** Links and transitions */
+#options a {
+ padding: 3px 6px;
+ -webkit-transition: all 0.5s ease-in-out;
+ -moz-transition: all 0.5s ease-in-out;
+ -o-transition: all 0.5s ease-in-out;
+ -webkit-transition: all 0.5s ease-in-out;
+ transition: all 0.5s ease-in-out;
+}
+
+#options a:hover {
+ background: #333;
+ color: #FFF;
+ border-radius: 5px;
+}
+
+#level-description {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ text-align: center;
+ padding-bottom: 30px;
+ font-size: 20pt;
+ font-weight: bold;
+}
+
+.main-container {
+ border: 0px solid red;
+}
+
+#progress {
+ position: absolute;
+ bottom: 0;
+ height: 10px;
+ width: 100%;
+ background: rgba(255, 0, 0, 0.2);
+ border-top: 1px solid #333;
+}
+
+#goal {
+ height: 100%;
+ background: rgba(0, 255, 0, 0.5);
+ border-right: 3px solid rgba(0, 200, 0, 0.5);
+}
+
+#help, #finalResult {
+ position: absolute;
+ z-index: 5;
+ border: 5px solid #888;
+ background: #FFF;
+ color: #000;
+ border-radius: 10px;
+ padding: 15px;
+ width: 400px;
+ left: 50%;
+ margin-left: -200px;
+ margin-top: 20%;
+}
+
+#help, #help *, #finalResult, #finalResult * {
+ font-family: Verdana, Arial, Tahoma, sans-serif;
+ font-size: 8pt;
+ color: #000;
+}