OLD | NEW |
(Empty) | |
| 1 // @@REWRITE(insert js-copyright) |
| 2 // @@REWRITE(delete-start) |
| 3 // Copyright 2009 Google Inc. All Rights Reserved |
| 4 // @@REWRITE(delete-end) |
| 5 |
| 6 /** |
| 7 * This file contains the top-level logic and o3d-related code for the siteswap |
| 8 * animator. |
| 9 */ |
| 10 |
| 11 o3djs.require('o3djs.rendergraph'); |
| 12 o3djs.require('o3djs.math'); |
| 13 o3djs.require('o3djs.primitives'); |
| 14 o3djs.require('o3djs.dump'); |
| 15 |
| 16 // Global variables are all referenced via g, so that either interpreter can |
| 17 // find them easily. |
| 18 var g = {}; |
| 19 |
| 20 /** |
| 21 * Creates a color based on an input index as a seed. |
| 22 * @param {!number} index the seed value to select the color. |
| 23 * @return {!Array.number} an [r g b a] color. |
| 24 */ |
| 25 function createColor(index) { |
| 26 var N = 12; // Number of distinct colors. |
| 27 var root3 = Math.sqrt(3); |
| 28 var theta = 2 * Math.PI * index / N; |
| 29 var sin = Math.sin(theta); |
| 30 var cos = Math.cos(theta); |
| 31 return [(1 / 3 + 2 / 3 * cos) + (1 / 3 - cos / 3 - sin / root3), |
| 32 (1 / 3 - cos / 3 + sin / root3) + (1 / 3 + 2 / 3 * cos), |
| 33 (1 / 3 - cos / 3 - sin / root3) + (1 / 3 - cos / 3 + sin / root3), |
| 34 1]; |
| 35 } |
| 36 |
| 37 /** |
| 38 * Creates a material, given the index as a seed to make it distinguishable. |
| 39 * @param {number} index an integer used to create a distinctive color. |
| 40 * @return {!o3d.Material} the material. |
| 41 */ |
| 42 function createMaterial(index) { |
| 43 var material = g.pack.createObject('Material'); |
| 44 |
| 45 // Apply our effect to this material. The effect tells the 3D hardware |
| 46 // which shader to use. |
| 47 material.effect = g.effect; |
| 48 |
| 49 // Set the material's drawList |
| 50 material.drawList = g.viewInfo.performanceDrawList; |
| 51 |
| 52 // This will create our quadColor parameter on the material. |
| 53 g.effect.createUniformParameters(material); |
| 54 |
| 55 // Set up the individual parameters in our effect file. |
| 56 |
| 57 // Light position |
| 58 var light_pos_param = material.getParam('light_pos'); |
| 59 light_pos_param.value = [10, 10, 20]; |
| 60 |
| 61 // Phong components of the light source |
| 62 var light_ambient_param = material.getParam('light_ambient'); |
| 63 var light_diffuse_param = material.getParam('light_diffuse'); |
| 64 var light_specular_param = material.getParam('light_specular'); |
| 65 |
| 66 // White ambient light |
| 67 light_ambient_param.value = [0.04, 0.04, 0.04, 1]; |
| 68 |
| 69 light_diffuse_param.value = createColor(index); |
| 70 // White specular light |
| 71 light_specular_param.value = [0.5, 0.5, 0.5, 1]; |
| 72 |
| 73 // Shininess of the material (for specular lighting) |
| 74 var shininess_param = material.getParam('shininess'); |
| 75 shininess_param.value = 30.0; |
| 76 |
| 77 // Bind the counter's count to the input of the FunctionEval. |
| 78 var paramTime = material.getParam('time'); |
| 79 paramTime.bind(g.counter.getParam('count')); |
| 80 |
| 81 material.getParam('camera_pos').value = g.eye; |
| 82 |
| 83 return material; |
| 84 } |
| 85 |
| 86 /** |
| 87 * Gets a material from our cache, creating it if it's not yet been made. |
| 88 * Uses index as a seed to make the material distinguishable. |
| 89 * @param {number} index an integer used to create/fetch a distinctive color. |
| 90 * @return {!o3d.Material} the material. |
| 91 */ |
| 92 function getMaterial(index) { |
| 93 g.materials = g.materials || []; // See initStep2 for a comment. |
| 94 if (!g.materials[index]) { |
| 95 g.materials[index] = createMaterial(index); |
| 96 } |
| 97 return g.materials[index]; |
| 98 } |
| 99 |
| 100 /** |
| 101 * Initializes g.o3d. |
| 102 * @param {Array} clientElements Array of o3d object elements. |
| 103 */ |
| 104 function initStep2(clientElements) { |
| 105 // Initializes global variables and libraries. |
| 106 window.g = g; |
| 107 |
| 108 // Used to tell whether we need to recompute our view on resize. |
| 109 g.o3dWidth = -1; |
| 110 g.o3dHeight = -1; |
| 111 |
| 112 // We create a different material for each color of object. |
| 113 //g.materials = []; // TODO(ericu): If this is executed, we fail. Why? |
| 114 |
| 115 // We hold on to all the shapes here so that we can clean them up when we want |
| 116 // to change patterns. |
| 117 g.ballShapes = []; |
| 118 g.handShapes = []; |
| 119 |
| 120 g.o3dElement = clientElements[0]; |
| 121 g.o3d = g.o3dElement.o3d; |
| 122 g.math = o3djs.math; |
| 123 g.client = g.o3dElement.client; |
| 124 |
| 125 // Initialize client sample libraries. |
| 126 o3djs.base.init(g.o3dElement); |
| 127 |
| 128 // Create a g.pack to manage our resources/assets |
| 129 g.pack = g.client.createPack(); |
| 130 |
| 131 // Create the render graph for a view. |
| 132 g.viewInfo = o3djs.rendergraph.createBasicView( |
| 133 g.pack, |
| 134 g.client.root, |
| 135 g.client.renderGraphRoot); |
| 136 |
| 137 // Get the default context to hold view/projection matrices. |
| 138 g.context = g.viewInfo.drawContext; |
| 139 |
| 140 // Load a simple effect from a textarea. |
| 141 g.effect = g.pack.createObject('Effect'); |
| 142 g.effect.loadFromFXString(document.getElementById('shader').value); |
| 143 |
| 144 // Eye-position: this is where our camera is located. |
| 145 // Global because each material we create must also know where it is, so that |
| 146 // the shader works properly. |
| 147 g.eye = [1, 6, 10]; |
| 148 |
| 149 // Target, this is the point at which our camera is pointed. |
| 150 var target = [0, 2, 0]; |
| 151 |
| 152 // Up-vector, this tells the camera which direction is 'up'. |
| 153 // We define the positive y-direction to be up in this example. |
| 154 var up = [0, 1, 0]; |
| 155 |
| 156 g.context.view = g.math.matrix4.lookAt(g.eye, target, up); |
| 157 |
| 158 // Make a SecondCounter to provide the time for our animation. |
| 159 g.counter = g.pack.createObject('SecondCounter'); |
| 160 g.counter.multiplier = 3; // Speed up time; this is in throws per second. |
| 161 |
| 162 // Generate the projection and viewProjection matrices based |
| 163 // on the g.o3d plugin size by calling onResize(). |
| 164 onResize(); |
| 165 |
| 166 // If we don't check the size of the client area every frame we don't get a |
| 167 // chance to adjust the perspective matrix fast enough to keep up with the |
| 168 // browser resizing us. |
| 169 // TODO(ericu): Switch to using the resize event once it's checked in. |
| 170 g.client.setRenderCallback(onResize); |
| 171 } |
| 172 |
| 173 /** |
| 174 * Stops or starts the animation based on the state of an html checkbox. |
| 175 */ |
| 176 function updateAnimating() { |
| 177 var box = document.the_form.check_box; |
| 178 g.counter.running = box.checked; |
| 179 } |
| 180 |
| 181 /** |
| 182 * Generates the projection matrix based on the size of the g.o3d plugin |
| 183 * and calculates the view-projection matrix. |
| 184 */ |
| 185 function onResize() { |
| 186 var newWidth = g.client.width; |
| 187 var newHeight = g.client.height; |
| 188 |
| 189 if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) { |
| 190 debug('resizing'); |
| 191 g.o3dWidth = newWidth; |
| 192 g.o3dHeight = newHeight; |
| 193 |
| 194 // Create our projection matrix, with a vertical field of view of 45 degrees |
| 195 // a near clipping plane of 0.1 and far clipping plane of 100. |
| 196 g.context.projection = g.math.matrix4.perspective( |
| 197 45 * Math.PI / 180, |
| 198 g.o3dWidth / g.o3dHeight, |
| 199 0.1, |
| 200 100); |
| 201 } |
| 202 } |
| 203 |
| 204 /** |
| 205 * Computes and prepares animation of the pattern input via the html form. If |
| 206 * the box is checked, this will immediately begin animation as well. |
| 207 */ |
| 208 function onComputePattern() { |
| 209 try { |
| 210 g.counter.removeAllCallbacks(); |
| 211 var group = document.the_form.radio_group_hands; |
| 212 var numHands = -1; |
| 213 for (var i = 0; i < group.length; ++i) { |
| 214 if (group[i].checked) { |
| 215 numHands = parseInt(group[i].value); |
| 216 } |
| 217 } |
| 218 var style = 'even'; |
| 219 if (document.the_form.pair_hands.checked) { |
| 220 style = 'pairs'; |
| 221 } |
| 222 var patternString = document.getElementById('input_pattern').value; |
| 223 var patternData = |
| 224 computeFullPatternFromString(patternString, numHands, style); |
| 225 startAnimation( |
| 226 patternData.numBalls, |
| 227 patternData.numHands, |
| 228 patternData.duration, |
| 229 patternData.ballCurveSets, |
| 230 patternData.handCurveSets); |
| 231 } catch (ex) { |
| 232 popup(stringifyObj(ex)); |
| 233 throw ex; |
| 234 } |
| 235 setUpSelection(); |
| 236 } |
| 237 |
| 238 /** |
| 239 * Removes any callbacks so they don't get called after the page has unloaded. |
| 240 */ |
| 241 function cleanup() { |
| 242 g.client.cleanup(); |
| 243 } |
| 244 |
| 245 |
| 246 /** |
| 247 * Dump out a newline-terminated string to the debug console, if available. |
| 248 * @param {!string} s the string to output. |
| 249 */ |
| 250 function debug(s) { |
| 251 o3djs.dump.dump(s + '\n'); |
| 252 } |
| 253 |
| 254 /** |
| 255 * Dump out a newline-terminated string to the debug console, if available, |
| 256 * then display it via an alert. |
| 257 * @param {!string} s the string to output. |
| 258 */ |
| 259 function popup(s) { |
| 260 debug(s); |
| 261 window.alert(s); |
| 262 } |
| 263 |
| 264 /** |
| 265 * If t, throw an exception. |
| 266 * @param {!bool} t the value to test. |
| 267 */ |
| 268 function assert(t) { |
| 269 if (!t) { |
| 270 throw new Error('Assertion failed!'); |
| 271 } |
| 272 } |
| 273 |
| 274 /** |
| 275 * Convert an object to a string containing a full one-level-deep property |
| 276 * listing, with values. |
| 277 * @param {!Object} o the object to convert. |
| 278 * @return {!string} the converted object. |
| 279 */ |
| 280 function stringifyObj(o) { |
| 281 var s = ''; |
| 282 for (var i in o) { |
| 283 s += i + ':' + o[i] + '\n'; |
| 284 } |
| 285 return s; |
| 286 } |
| 287 |
| 288 /** |
| 289 * Add the information in a curve to the params on a shape, such that the vertex |
| 290 * shader will move the shape along the curve at times after timeBase. |
| 291 * @param {!Curve} curve the curve the shape should follow. |
| 292 * @param {!o3d.Shape} shape the shape being moved. |
| 293 * @param {!number} timeBase the base to subtract from the current time when |
| 294 * giving the curve calculation its time input. |
| 295 */ |
| 296 function setParamCurveInfo(curve, shape, timeBase) { |
| 297 assert(curve); |
| 298 assert(shape); |
| 299 try { |
| 300 shape.elements[0].getParam('time_base').value = timeBase; |
| 301 shape.elements[0].getParam('coeff_a').value = |
| 302 [curve.xEqn.a, curve.yEqn.a, curve.zEqn.a]; |
| 303 shape.elements[0].getParam('coeff_b').value = |
| 304 [curve.xEqn.b, curve.yEqn.b, curve.zEqn.b]; |
| 305 shape.elements[0].getParam('coeff_c').value = |
| 306 [curve.xEqn.c, curve.yEqn.c, curve.zEqn.c]; |
| 307 shape.elements[0].getParam('coeff_d').value = |
| 308 [curve.xEqn.d, curve.yEqn.d, curve.zEqn.d]; |
| 309 shape.elements[0].getParam('coeff_e').value = |
| 310 [curve.xEqn.e, curve.yEqn.e, curve.zEqn.e]; |
| 311 shape.elements[0].getParam('coeff_f').value = |
| 312 [curve.xEqn.f, curve.yEqn.f, curve.zEqn.f]; |
| 313 |
| 314 assert(curve.xEqn.lerpRate == curve.yEqn.lerpRate); |
| 315 assert(curve.xEqn.lerpRate == curve.zEqn.lerpRate); |
| 316 shape.elements[0].getParam('coeff_lerp').value = curve.xEqn.lerpRate; |
| 317 if (curve.xEqn.lerpRate) { |
| 318 shape.elements[0].getParam('coeff_l_a').value = |
| 319 [curve.xEqn.lA, curve.yEqn.lA, curve.zEqn.lA]; |
| 320 shape.elements[0].getParam('coeff_l_b').value = |
| 321 [curve.xEqn.lB, curve.yEqn.lB, curve.zEqn.lB]; |
| 322 shape.elements[0].getParam('coeff_l_c').value = |
| 323 [curve.xEqn.lC, curve.yEqn.lC, curve.zEqn.lC]; |
| 324 } |
| 325 } catch (ex) { |
| 326 debug(ex); |
| 327 throw ex; |
| 328 } |
| 329 } |
| 330 |
| 331 /** |
| 332 * Create the params that the shader expects on the supplied shape's first |
| 333 * element. |
| 334 * @param {!o3d.Shape} shape the shape on whose first element to create params. |
| 335 */ |
| 336 function createParams(shape) { |
| 337 shape.elements[0].createParam('coeff_a', 'ParamFloat3').value = [0, 0, 0]; |
| 338 shape.elements[0].createParam('coeff_b', 'ParamFloat3').value = [0, 0, 0]; |
| 339 shape.elements[0].createParam('coeff_c', 'ParamFloat3').value = [0, 0, 0]; |
| 340 shape.elements[0].createParam('coeff_d', 'ParamFloat3').value = [0, 0, 0]; |
| 341 shape.elements[0].createParam('coeff_e', 'ParamFloat3').value = [0, 0, 0]; |
| 342 shape.elements[0].createParam('coeff_f', 'ParamFloat3').value = [0, 0, 0]; |
| 343 shape.elements[0].createParam('coeff_l_a', 'ParamFloat3').value = [0, 0, 0]; |
| 344 shape.elements[0].createParam('coeff_l_b', 'ParamFloat3').value = [0, 0, 0]; |
| 345 shape.elements[0].createParam('coeff_l_c', 'ParamFloat3').value = [0, 0, 0]; |
| 346 shape.elements[0].createParam('coeff_lerp', 'ParamFloat').value = 0; |
| 347 shape.elements[0].createParam('time_base', 'ParamFloat').value = 0; |
| 348 } |
| 349 |
| 350 /** |
| 351 * Adjust the number of ball shapes in g.pack. |
| 352 * @param {!number} numBalls the number of balls desired. |
| 353 */ |
| 354 function setNumBalls(numBalls) { |
| 355 for (var i = 0; i < g.ballShapes.length; ++i) { |
| 356 g.pack.removeObject(g.ballShapes[i]); |
| 357 g.client.root.removeShape(g.ballShapes[i]); |
| 358 } |
| 359 g.ballShapes = []; |
| 360 |
| 361 for (var i = 0; i < numBalls; ++i) { |
| 362 var shape = o3djs.primitives.createSphere(g.pack, |
| 363 getMaterial(5 * i), |
| 364 0.10, |
| 365 70, |
| 366 70); |
| 367 shape.name = 'Ball ' + i; |
| 368 |
| 369 // generate the draw elements. |
| 370 shape.createDrawElements(g.pack, null); |
| 371 |
| 372 // Now attach the sphere to the root of the scene graph. |
| 373 g.client.root.addShape(shape); |
| 374 |
| 375 // Create the material params for the shader. |
| 376 createParams(shape); |
| 377 |
| 378 g.ballShapes[i] = shape; |
| 379 } |
| 380 } |
| 381 |
| 382 /** |
| 383 * Adjust the number of hand shapes in g.pack. |
| 384 * @param {!number} numHands the number of hands desired. |
| 385 */ |
| 386 function setNumHands(numHands) { |
| 387 g.counter.removeAllCallbacks(); |
| 388 |
| 389 for (var i = 0; i < g.handShapes.length; ++i) { |
| 390 g.pack.removeObject(g.handShapes[i]); |
| 391 g.client.root.removeShape(g.handShapes[i]); |
| 392 } |
| 393 g.handShapes = []; |
| 394 |
| 395 for (var i = 0; i < numHands; ++i) { |
| 396 var shape = o3djs.primitives.createBox(g.pack, |
| 397 getMaterial(3 * (i + 1)), |
| 398 0.25, |
| 399 0.05, |
| 400 0.25); |
| 401 shape.name = 'Hand ' + i; |
| 402 |
| 403 // generate the draw elements. |
| 404 shape.createDrawElements(g.pack, null); |
| 405 |
| 406 // Now attach the sphere to the root of the scene graph. |
| 407 g.client.root.addShape(shape); |
| 408 |
| 409 // Create the material params for the shader. |
| 410 createParams(shape); |
| 411 |
| 412 g.handShapes[i] = shape; |
| 413 } |
| 414 } |
| 415 |
OLD | NEW |