| 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;
|
| + }
|
| +}
|
| +
|
|
|