Index: samples/siteswap/siteswap.js |
=================================================================== |
--- samples/siteswap/siteswap.js (revision 0) |
+++ samples/siteswap/siteswap.js (revision 0) |
@@ -0,0 +1,415 @@ |
+// @@REWRITE(insert js-copyright) |
+// @@REWRITE(delete-start) |
+// Copyright 2009 Google Inc. All Rights Reserved |
+// @@REWRITE(delete-end) |
+ |
+/** |
+ * This file contains the top-level logic and o3d-related code for the siteswap |
+ * animator. |
+ */ |
+ |
+o3djs.require('o3djs.rendergraph'); |
+o3djs.require('o3djs.math'); |
+o3djs.require('o3djs.primitives'); |
+o3djs.require('o3djs.dump'); |
+ |
+// Global variables are all referenced via g, so that either interpreter can |
+// find them easily. |
+var g = {}; |
+ |
+/** |
+ * Creates a color based on an input index as a seed. |
+ * @param {!number} index the seed value to select the color. |
+ * @return {!Array.number} an [r g b a] color. |
+ */ |
+function createColor(index) { |
+ var N = 12; // Number of distinct colors. |
+ var root3 = Math.sqrt(3); |
+ var theta = 2 * Math.PI * index / N; |
+ var sin = Math.sin(theta); |
+ var cos = Math.cos(theta); |
+ return [(1 / 3 + 2 / 3 * cos) + (1 / 3 - cos / 3 - sin / root3), |
+ (1 / 3 - cos / 3 + sin / root3) + (1 / 3 + 2 / 3 * cos), |
+ (1 / 3 - cos / 3 - sin / root3) + (1 / 3 - cos / 3 + sin / root3), |
+ 1]; |
+} |
+ |
+/** |
+ * Creates a material, given the index as a seed to make it distinguishable. |
+ * @param {number} index an integer used to create a distinctive color. |
+ * @return {!o3d.Material} the material. |
+ */ |
+function createMaterial(index) { |
+ var material = g.pack.createObject('Material'); |
+ |
+ // Apply our effect to this material. The effect tells the 3D hardware |
+ // which shader to use. |
+ material.effect = g.effect; |
+ |
+ // Set the material's drawList |
+ material.drawList = g.viewInfo.performanceDrawList; |
+ |
+ // This will create our quadColor parameter on the material. |
+ g.effect.createUniformParameters(material); |
+ |
+ // Set up the individual parameters in our effect file. |
+ |
+ // Light position |
+ var light_pos_param = material.getParam('light_pos'); |
+ light_pos_param.value = [10, 10, 20]; |
+ |
+ // Phong components of the light source |
+ var light_ambient_param = material.getParam('light_ambient'); |
+ var light_diffuse_param = material.getParam('light_diffuse'); |
+ var light_specular_param = material.getParam('light_specular'); |
+ |
+ // White ambient light |
+ light_ambient_param.value = [0.04, 0.04, 0.04, 1]; |
+ |
+ light_diffuse_param.value = createColor(index); |
+ // White specular light |
+ light_specular_param.value = [0.5, 0.5, 0.5, 1]; |
+ |
+ // Shininess of the material (for specular lighting) |
+ var shininess_param = material.getParam('shininess'); |
+ shininess_param.value = 30.0; |
+ |
+ // Bind the counter's count to the input of the FunctionEval. |
+ var paramTime = material.getParam('time'); |
+ paramTime.bind(g.counter.getParam('count')); |
+ |
+ material.getParam('camera_pos').value = g.eye; |
+ |
+ return material; |
+} |
+ |
+/** |
+ * Gets a material from our cache, creating it if it's not yet been made. |
+ * Uses index as a seed to make the material distinguishable. |
+ * @param {number} index an integer used to create/fetch a distinctive color. |
+ * @return {!o3d.Material} the material. |
+ */ |
+function getMaterial(index) { |
+ g.materials = g.materials || []; // See initStep2 for a comment. |
+ if (!g.materials[index]) { |
+ g.materials[index] = createMaterial(index); |
+ } |
+ return g.materials[index]; |
+} |
+ |
+/** |
+ * Initializes g.o3d. |
+ * @param {Array} clientElements Array of o3d object elements. |
+ */ |
+function initStep2(clientElements) { |
+ // Initializes global variables and libraries. |
+ window.g = g; |
+ |
+ // Used to tell whether we need to recompute our view on resize. |
+ g.o3dWidth = -1; |
+ g.o3dHeight = -1; |
+ |
+ // We create a different material for each color of object. |
+ //g.materials = []; // TODO(ericu): If this is executed, we fail. Why? |
+ |
+ // We hold on to all the shapes here so that we can clean them up when we want |
+ // to change patterns. |
+ g.ballShapes = []; |
+ g.handShapes = []; |
+ |
+ g.o3dElement = clientElements[0]; |
+ g.o3d = g.o3dElement.o3d; |
+ g.math = o3djs.math; |
+ g.client = g.o3dElement.client; |
+ |
+ // Initialize client sample libraries. |
+ o3djs.base.init(g.o3dElement); |
+ |
+ // Create a g.pack to manage our resources/assets |
+ g.pack = g.client.createPack(); |
+ |
+ // Create the render graph for a view. |
+ g.viewInfo = o3djs.rendergraph.createBasicView( |
+ g.pack, |
+ g.client.root, |
+ g.client.renderGraphRoot); |
+ |
+ // Get the default context to hold view/projection matrices. |
+ g.context = g.viewInfo.drawContext; |
+ |
+ // Load a simple effect from a textarea. |
+ g.effect = g.pack.createObject('Effect'); |
+ g.effect.loadFromFXString(document.getElementById('shader').value); |
+ |
+ // Eye-position: this is where our camera is located. |
+ // Global because each material we create must also know where it is, so that |
+ // the shader works properly. |
+ g.eye = [1, 6, 10]; |
+ |
+ // Target, this is the point at which our camera is pointed. |
+ var target = [0, 2, 0]; |
+ |
+ // Up-vector, this tells the camera which direction is 'up'. |
+ // We define the positive y-direction to be up in this example. |
+ var up = [0, 1, 0]; |
+ |
+ g.context.view = g.math.matrix4.lookAt(g.eye, target, up); |
+ |
+ // Make a SecondCounter to provide the time for our animation. |
+ g.counter = g.pack.createObject('SecondCounter'); |
+ g.counter.multiplier = 3; // Speed up time; this is in throws per second. |
+ |
+ // Generate the projection and viewProjection matrices based |
+ // on the g.o3d plugin size by calling onResize(). |
+ onResize(); |
+ |
+ // If we don't check the size of the client area every frame we don't get a |
+ // chance to adjust the perspective matrix fast enough to keep up with the |
+ // browser resizing us. |
+ // TODO(ericu): Switch to using the resize event once it's checked in. |
+ g.client.setRenderCallback(onResize); |
+} |
+ |
+/** |
+ * Stops or starts the animation based on the state of an html checkbox. |
+ */ |
+function updateAnimating() { |
+ var box = document.the_form.check_box; |
+ g.counter.running = box.checked; |
+} |
+ |
+/** |
+ * Generates the projection matrix based on the size of the g.o3d plugin |
+ * and calculates the view-projection matrix. |
+ */ |
+function onResize() { |
+ var newWidth = g.client.width; |
+ var newHeight = g.client.height; |
+ |
+ if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) { |
+ debug('resizing'); |
+ g.o3dWidth = newWidth; |
+ g.o3dHeight = newHeight; |
+ |
+ // Create our projection matrix, with a vertical field of view of 45 degrees |
+ // a near clipping plane of 0.1 and far clipping plane of 100. |
+ g.context.projection = g.math.matrix4.perspective( |
+ 45 * Math.PI / 180, |
+ g.o3dWidth / g.o3dHeight, |
+ 0.1, |
+ 100); |
+ } |
+} |
+ |
+/** |
+ * Computes and prepares animation of the pattern input via the html form. If |
+ * the box is checked, this will immediately begin animation as well. |
+ */ |
+function onComputePattern() { |
+ try { |
+ g.counter.removeAllCallbacks(); |
+ var group = document.the_form.radio_group_hands; |
+ var numHands = -1; |
+ for (var i = 0; i < group.length; ++i) { |
+ if (group[i].checked) { |
+ numHands = parseInt(group[i].value); |
+ } |
+ } |
+ var style = 'even'; |
+ if (document.the_form.pair_hands.checked) { |
+ style = 'pairs'; |
+ } |
+ var patternString = document.getElementById('input_pattern').value; |
+ var patternData = |
+ computeFullPatternFromString(patternString, numHands, style); |
+ startAnimation( |
+ patternData.numBalls, |
+ patternData.numHands, |
+ patternData.duration, |
+ patternData.ballCurveSets, |
+ patternData.handCurveSets); |
+ } catch (ex) { |
+ popup(stringifyObj(ex)); |
+ throw ex; |
+ } |
+ setUpSelection(); |
+} |
+ |
+/** |
+ * Removes any callbacks so they don't get called after the page has unloaded. |
+ */ |
+function cleanup() { |
+ g.client.cleanup(); |
+} |
+ |
+ |
+/** |
+ * Dump out a newline-terminated string to the debug console, if available. |
+ * @param {!string} s the string to output. |
+ */ |
+function debug(s) { |
+ o3djs.dump.dump(s + '\n'); |
+} |
+ |
+/** |
+ * Dump out a newline-terminated string to the debug console, if available, |
+ * then display it via an alert. |
+ * @param {!string} s the string to output. |
+ */ |
+function popup(s) { |
+ debug(s); |
+ window.alert(s); |
+} |
+ |
+/** |
+ * If t, throw an exception. |
+ * @param {!bool} t the value to test. |
+ */ |
+function assert(t) { |
+ if (!t) { |
+ throw new Error('Assertion failed!'); |
+ } |
+} |
+ |
+/** |
+ * Convert an object to a string containing a full one-level-deep property |
+ * listing, with values. |
+ * @param {!Object} o the object to convert. |
+ * @return {!string} the converted object. |
+ */ |
+function stringifyObj(o) { |
+ var s = ''; |
+ for (var i in o) { |
+ s += i + ':' + o[i] + '\n'; |
+ } |
+ return s; |
+} |
+ |
+/** |
+ * Add the information in a curve to the params on a shape, such that the vertex |
+ * shader will move the shape along the curve at times after timeBase. |
+ * @param {!Curve} curve the curve the shape should follow. |
+ * @param {!o3d.Shape} shape the shape being moved. |
+ * @param {!number} timeBase the base to subtract from the current time when |
+ * giving the curve calculation its time input. |
+ */ |
+function setParamCurveInfo(curve, shape, timeBase) { |
+ assert(curve); |
+ assert(shape); |
+ try { |
+ shape.elements[0].getParam('time_base').value = timeBase; |
+ shape.elements[0].getParam('coeff_a').value = |
+ [curve.xEqn.a, curve.yEqn.a, curve.zEqn.a]; |
+ shape.elements[0].getParam('coeff_b').value = |
+ [curve.xEqn.b, curve.yEqn.b, curve.zEqn.b]; |
+ shape.elements[0].getParam('coeff_c').value = |
+ [curve.xEqn.c, curve.yEqn.c, curve.zEqn.c]; |
+ shape.elements[0].getParam('coeff_d').value = |
+ [curve.xEqn.d, curve.yEqn.d, curve.zEqn.d]; |
+ shape.elements[0].getParam('coeff_e').value = |
+ [curve.xEqn.e, curve.yEqn.e, curve.zEqn.e]; |
+ shape.elements[0].getParam('coeff_f').value = |
+ [curve.xEqn.f, curve.yEqn.f, curve.zEqn.f]; |
+ |
+ assert(curve.xEqn.lerpRate == curve.yEqn.lerpRate); |
+ assert(curve.xEqn.lerpRate == curve.zEqn.lerpRate); |
+ shape.elements[0].getParam('coeff_lerp').value = curve.xEqn.lerpRate; |
+ if (curve.xEqn.lerpRate) { |
+ shape.elements[0].getParam('coeff_l_a').value = |
+ [curve.xEqn.lA, curve.yEqn.lA, curve.zEqn.lA]; |
+ shape.elements[0].getParam('coeff_l_b').value = |
+ [curve.xEqn.lB, curve.yEqn.lB, curve.zEqn.lB]; |
+ shape.elements[0].getParam('coeff_l_c').value = |
+ [curve.xEqn.lC, curve.yEqn.lC, curve.zEqn.lC]; |
+ } |
+ } catch (ex) { |
+ debug(ex); |
+ throw ex; |
+ } |
+} |
+ |
+/** |
+ * Create the params that the shader expects on the supplied shape's first |
+ * element. |
+ * @param {!o3d.Shape} shape the shape on whose first element to create params. |
+ */ |
+function createParams(shape) { |
+ shape.elements[0].createParam('coeff_a', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_b', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_c', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_d', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_e', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_f', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_l_a', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_l_b', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_l_c', 'ParamFloat3').value = [0, 0, 0]; |
+ shape.elements[0].createParam('coeff_lerp', 'ParamFloat').value = 0; |
+ shape.elements[0].createParam('time_base', 'ParamFloat').value = 0; |
+} |
+ |
+/** |
+ * Adjust the number of ball shapes in g.pack. |
+ * @param {!number} numBalls the number of balls desired. |
+ */ |
+function setNumBalls(numBalls) { |
+ for (var i = 0; i < g.ballShapes.length; ++i) { |
+ g.pack.removeObject(g.ballShapes[i]); |
+ g.client.root.removeShape(g.ballShapes[i]); |
+ } |
+ g.ballShapes = []; |
+ |
+ for (var i = 0; i < numBalls; ++i) { |
+ var shape = o3djs.primitives.createSphere(g.pack, |
+ getMaterial(5 * i), |
+ 0.10, |
+ 70, |
+ 70); |
+ shape.name = 'Ball ' + i; |
+ |
+ // generate the draw elements. |
+ shape.createDrawElements(g.pack, null); |
+ |
+ // Now attach the sphere to the root of the scene graph. |
+ g.client.root.addShape(shape); |
+ |
+ // Create the material params for the shader. |
+ createParams(shape); |
+ |
+ g.ballShapes[i] = shape; |
+ } |
+} |
+ |
+/** |
+ * Adjust the number of hand shapes in g.pack. |
+ * @param {!number} numHands the number of hands desired. |
+ */ |
+function setNumHands(numHands) { |
+ g.counter.removeAllCallbacks(); |
+ |
+ for (var i = 0; i < g.handShapes.length; ++i) { |
+ g.pack.removeObject(g.handShapes[i]); |
+ g.client.root.removeShape(g.handShapes[i]); |
+ } |
+ g.handShapes = []; |
+ |
+ for (var i = 0; i < numHands; ++i) { |
+ var shape = o3djs.primitives.createBox(g.pack, |
+ getMaterial(3 * (i + 1)), |
+ 0.25, |
+ 0.05, |
+ 0.25); |
+ shape.name = 'Hand ' + i; |
+ |
+ // generate the draw elements. |
+ shape.createDrawElements(g.pack, null); |
+ |
+ // Now attach the sphere to the root of the scene graph. |
+ g.client.root.addShape(shape); |
+ |
+ // Create the material params for the shader. |
+ createParams(shape); |
+ |
+ g.handShapes[i] = shape; |
+ } |
+} |
+ |